updated kpi list & detail

This commit is contained in:
Warunee Tamkoo 2024-05-17 11:10:23 +07:00
parent 8c829c03bd
commit bd0835b0f1
32 changed files with 5970 additions and 2034 deletions

View file

@ -1,65 +0,0 @@
<script setup lang="ts">
import { ref } from "vue";
import DialogHeader from "@/components/DialogHeader.vue";
import type { QTableProps } from "quasar";
const modal = defineModel<boolean>("modal", { required: true });
const rows = ref<any[]>([]);
const visibleColumns = ref<string[]>(["fullName"]);
const columns = ref<QTableProps["columns"]>([
{
name: "fullName",
align: "left",
label: "ชื่อ",
sortable: true,
field: "fullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
function close() {
modal.value = false;
}
</script>
<template>
<q-dialog persistent v-model="modal">
<q-card style="min-width: 50vw">
<DialogHeader tittle="ประวัติการแก้ไข" :close="close" />
<q-separator />
<q-card-section>
<d-table
for="table"
ref="table"
:columns="columns"
:rows="rows"
row-key="id"
flat
bordered
dense
class="custom-header-table"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumns"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
<q-th auto-width />
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div class="table_ellipsis">
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card-section>
</q-card>
</q-dialog>
</template>

View file

@ -0,0 +1,587 @@
<script setup lang="ts">
import { ref, onMounted, computed, watch, reactive } from "vue";
import { useQuasar, type QTableProps } from "quasar";
import { useRoute } from "vue-router";
import DialogListCriteria from "@/modules/14_KPI/components/Tab/Dialog/DialogListCriteria.vue";
import config from "@/app.config";
import http from "@/plugins/http";
import Work from "@/modules/14_KPI/components/Tab/Topic/01_Indicator.vue";
import Competency from "@/modules/14_KPI/components/Tab/Topic/02_Competency.vue";
import Develop from "@/modules/14_KPI/components/Tab/Topic/03_Develop.vue";
import { useCounterMixin } from "@/stores/mixin";
import { useKpiDataStore } from "@/modules/14_KPI/store";
import type { ListCriteria } from "@/modules/14_KPI/interface/request/index";
const dataListCriteria = ref<ListCriteria[]>([]);
const modalCriteria = ref<boolean>(false);
const $q = useQuasar();
const route = useRoute();
const {
hideLoader,
messageError,
date2Thai,
success,
showLoader,
dialogRemove,
} = useCounterMixin();
const store = useKpiDataStore();
const evaluationId = ref<string>(route.params.id.toString());
const rows_01 = ref<any[]>();
const rows_02 = ref<any[]>();
const rows_03 = ref<any[]>();
const totalResults1 = ref<number>(0);
const totalResults2 = ref<number>(0);
const totalResults3 = ref<number>(0);
// const resultWork = ref<number>(0);
const weightPlanned = ref<number>(0);
const weightRole = ref<number>(0);
const weightAssigned = ref<number>(0);
const resultPlanned = ref<number>(0);
const resultRole = ref<number>(0);
const resultAssigned = ref<number>(0);
function fetchListPlanned() {
http
.get(config.API.kpiAchievement("planned") + `?id=${evaluationId.value}`)
.then((res) => {
const data = res.data.result;
const newRow = data.map((e: any) => ({
...e,
evaluationResults: (e.point / 5) * e.weight,
}));
rows_01.value = newRow;
if (newRow.length > 0) {
resultPlanned.value = newRow.reduce(
(sum: number, e: any) => sum + e.evaluationResults,
0
);
store.excusiveIndicator1PercentVal = resultPlanned.value;
const weight = newRow.reduce(
(sum: number, e: any) => sum + e.weight,
0
);
weightPlanned.value = weight;
store.indicatorWeight1Total = Number(weight);
totalResults1.value =
(resultPlanned.value * store.excusiveIndicator1Weight) / weight;
store.excusiveIndicator1ScoreVal = totalResults1.value;
}
})
.catch((err) => {
messageError($q, err);
});
}
function fetchListRole() {
http
.get(config.API.kpiAchievement("role") + `?id=${evaluationId.value}`)
.then((res) => {
const data = res.data.result;
const newRow = data.map((e: any) => ({
...e,
evaluationResults: (e.point / 5) * e.weight,
}));
rows_02.value = newRow;
if (newRow.length > 0) {
resultRole.value = newRow.reduce(
(sum: number, e: any) => sum + e.evaluationResults,
0
);
const weight = newRow.reduce(
(sum: number, e: any) => sum + e.weight,
0
);
weightRole.value = weight;
}
})
.catch((err) => {
messageError($q, err);
});
}
function fetchAssigned() {
http
.get(config.API.kpiAchievement("special") + `?id=${evaluationId.value}`)
.then((res) => {
const data = res.data.result;
const newRow = data.map((e: any) => ({
...e,
evaluationResults: (e.point / 5) * e.weight,
}));
rows_03.value = newRow;
if (newRow.length > 0) {
resultAssigned.value = newRow.reduce(
(sum: number, e: any) => sum + e.evaluationResults,
0
);
store.excusiveIndicator2PercentVal = resultAssigned.value;
const weight = newRow.reduce(
(sum: number, e: any) => sum + e.weight,
0
);
weightAssigned.value = weight;
store.indicatorWeight2Total = Number(weight);
totalResults3.value =
(resultAssigned.value * store.excusiveIndicator2Weight) / weight;
store.excusiveIndicator2ScoreVal = totalResults3.value;
}
})
.catch((err) => {
messageError($q, err);
});
}
function onInfo() {
modalCriteria.value = true;
}
function getCriteria() {
http
.get(config.API.KpiEvaluationInfo)
.then((res) => {
const data = res.data.result.data;
dataListCriteria.value = data;
})
.catch((e) => {
messageError($q, e);
});
}
const isShowScore = computed(() => {
return store.tabOpen === 3 && store.tabMain === "3";
});
watch(
[weightPlanned, weightRole, weightAssigned],
([newA, newB, newC], [prevA, prevB, prevC]) => {
if (newA !== prevA || newB !== prevB || newC !== prevC) {
store.indicatorWeightTotal =
Number(weightPlanned.value) +
Number(weightAssigned.value) +
Number(weightRole.value);
}
}
);
watch(
[resultPlanned, resultRole, resultAssigned],
([newA, newB, newC], [prevA, prevB, prevC]) => {
if (newA !== prevA || newB !== prevB || newC !== prevC) {
store.indicatorPercentVal =
Number(resultPlanned.value) +
Number(resultRole.value) +
Number(resultAssigned.value);
store.indicatorScoreVal =
store.indicatorPercentVal * (store.indicatorScore / 100);
}
}
);
onMounted(() => {
getCriteria();
fetchListPlanned();
fetchListRole();
fetchAssigned();
setTimeout(() => {
hideLoader();
}, 1000);
});
</script>
<template>
<q-scroll-area
style="height: 100vh"
class="bg-white row col-12 text-dark q-pa-md"
>
<div class="text-weight-bold text-body2">
<span class="txt-under text-blue-6">องคประกอบท 1 </span>
<span class="q-ml-sm"> ผลสมฤทธของงาน</span>
</div>
<div class="q-gutter-md q-mt-sm">
<!-- องคประกอบท 1 -->
<div v-if="store.dataEvaluation.posExecutiveName != null">
<Work
v-model:data="rows_01"
:title="`มิติที่ 1 ภารกิจตามนโยบายและยุทธศาสตร์ของกรุงเทพมหานคร`"
:page="1"
:fetchList="fetchListPlanned"
:total="totalResults1"
/>
<div v-if="isShowScore">
<q-table
flat
dense
bordered
:rows="[
{
name: 'รวมผลการประเมิน (ร้อยละ)',
value: store.excusiveIndicator1PercentVal.toFixed(2),
},
{
name: 'ผลการประเมินมิติที่ 1 (คะแนน)',
value: store.excusiveIndicator1ScoreVal.toFixed(2),
},
]"
:columns="[
{
name: 'name',
field: 'name',
label: 'name',
style: 'font-size: 14px',
},
{
name: 'value',
field: 'value',
label: 'value',
style: 'font-size: 14px; font-weight: bold',
},
]"
row-key="name"
hide-header
hide-bottom
class="q-mt-xs q-mb-md"
/>
</div>
<div v-else class="q-mt-md"></div>
<Work
v-model:data="rows_03"
:title="`มิติที่ 2 วาระเร่งด่วนที่ได้รับมอบหมายพิเศษ (ถ้ามี)`"
:page="3"
:fetchList="fetchAssigned"
:total="totalResults3"
/>
<div v-if="isShowScore">
<q-table
flat
dense
bordered
:rows="[
// {
// name: ' ()',
// value: store.excusiveIndicator2PercentVal.toFixed(2),
// },
{
name: 'ผลการประเมินมิติที่ 2 (คะแนน)',
value: store.excusiveIndicator2ScoreVal.toFixed(2),
},
]"
:columns="[
{
name: 'name',
field: 'name',
label: 'name',
style: 'font-size: 14px',
},
{
name: 'value',
field: 'value',
label: 'value',
style: 'font-size: 14px; font-weight: bold',
},
]"
row-key="name"
hide-header
hide-bottom
class="q-mt-xs q-mb-md"
/>
<div class="row text-body2 text-weight-bold">
<div class="col-12 text-center row justify-center">
<span
>สรปผลการประเมนผลสมฤทธของงาน ( 1 + 2)
(คะแนนเต
{{ store.excusiveIndicatorScore }}
คะแนน)</span
>
<div class="text-primary q-pl-md">
{{
(
store.excusiveIndicator1ScoreVal +
store.excusiveIndicator2ScoreVal
).toFixed(2)
}}
</div>
</div>
</div>
</div>
</div>
<div v-else>
<Work
v-model:data="rows_01"
:title="`1. งานตามแผนปฏิบัติราชการประจำปี`"
:page="1"
:fetchList="fetchListPlanned"
:total="totalResults1"
/>
<Work
v-model:data="rows_02"
:title="`2. งานตามหน้าที่ความรับผิดชอบหลัก`"
:page="2"
:fetchList="fetchListRole"
:total="totalResults2"
/>
<Work
v-model:data="rows_03"
:title="`3. งานที่ได้รับมอบหมายพิเศษ`"
:page="3"
:fetchList="fetchAssigned"
:total="totalResults3"
/>
<div v-if="isShowScore">
<q-table
flat
dense
bordered
:rows="[
{
name: 'รวมผลการประเมิน (ร้อยละ)',
value: store.indicatorPercentVal.toFixed(2),
},
]"
:columns="[
{
name: 'name',
field: 'name',
label: 'name',
style: 'font-size: 14px',
},
{
name: 'value',
field: 'value',
label: 'value',
style: 'font-size: 14px; font-weight: bold',
},
]"
row-key="name"
hide-header
hide-bottom
class="q-mt-xs q-mb-md"
/>
<div class="row text-body2 text-weight-bold">
<div class="col-12 text-center row justify-center">
<span
>สรปผลการประเมนผลสมฤทธของงาน (คะแนนเต
{{ store.indicatorScore }}
คะแนน)</span
>
<div class="text-primary q-pl-md">
{{ store.indicatorScoreVal.toFixed(2) }}
</div>
</div>
</div>
</div>
</div>
<q-separator size="3px" class="q-my-lg" />
<!-- องคประกอบท 2 -->
<div class="text-weight-bold text-body2 q-mb-sm">
<span class="txt-under text-blue-6">องคประกอบท 2</span>
<span class="q-ml-sm"> พฤตกรรมการปฎราชการ (สมรรถนะ)</span>
<q-btn
flat
icon="info"
color="info"
round
class="q-ml-xs"
@click="onInfo"
>
<q-tooltip>เกณฑการประเม</q-tooltip>
</q-btn>
</div>
<Competency v-model:dataListCriteria="dataListCriteria" />
<q-table
v-if="isShowScore"
flat
dense
bordered
:rows="[
{
name: `สรุปผลการประเมินสมรรถนะ (คะแนนเต็ม ${
!store.dataEvaluation.posExecutiveName
? store.competencyScore
: store.excusiveCompetencyScore
} คะแนน)`,
value: store.competencyScoreVal.toFixed(2),
},
]"
:columns="[
{
name: 'name',
field: 'name',
label: 'name',
style: 'font-size: 14px',
},
{
name: 'value',
field: 'value',
label: 'value',
style: 'font-size: 14px; font-weight: bold',
},
]"
row-key="name"
hide-header
hide-bottom
class="q-mt-xs q-mb-md"
/>
<div v-if="!store.dataEvaluation.posExecutiveName">
<Develop />
<div v-if="isShowScore">
<q-table
flat
dense
bordered
:rows="[
{
name: `ผลการประเมินการพัฒนาตนเอง (คะแนนเต็ม ${store.devScore} คะแนน)`,
value: store.devScoreVal.toFixed(2),
},
]"
:columns="[
{
name: 'name',
field: 'name',
label: 'name',
style: 'font-size: 14px',
},
{
name: 'value',
field: 'value',
label: 'value',
style: 'font-size: 14px; font-weight: bold',
},
]"
row-key="name"
hide-header
hide-bottom
class="q-mt-xs q-mb-md"
/>
</div>
</div>
<div v-if="isShowScore">
<div
v-if="store.dataEvaluation.posExecutiveName == null"
class="row text-body2 text-weight-bold"
>
<div class="col-12 text-center row justify-center">
<span
>สรปผลการประเมนพฤตกรรมการปฏราชการ (สมรรถนะ+การพฒนาตนเอง)
(คะแนนเต {{ store.competencyDevScore }} คะแนน)</span
>
<div class="text-primary q-pl-md">
{{ (store.competencyScoreVal + store.devScoreVal).toFixed(2) }}
</div>
</div>
</div>
<div v-else class="row text-body2 text-weight-bold">
<div class="col-12 text-center row justify-center">
<span
>สรปผลการประเมนพฤตกรรมการปฏราชการ (สมรรถนะ) (คะแนนเต
{{ store.competencyScore }} คะแนน)</span
>
<div class="text-primary q-pl-md">
{{ store.competencyScoreVal.toFixed(2) }}
</div>
</div>
</div>
</div>
</div>
</q-scroll-area>
<DialogListCriteria
v-model:modal="modalCriteria"
v-model:dataListCriteria="dataListCriteria"
/>
</template>
<style scoped>
.txt-under {
text-decoration: underline;
}
.custom-table2 {
max-height: 64vh;
.q-table tr:nth-child(odd) td {
background: white;
}
.q-table tr:nth-child(even) td {
background: #f8f8f8;
}
.q-table thead tr {
background: #ecebeb;
}
.q-table thead tr th {
position: sticky;
}
.q-table td:nth-of-type(2) {
z-index: 3 !important;
}
.q-table th:nth-of-type(2),
.q-table td:nth-of-type(2) {
position: sticky;
left: 0;
z-index: 1;
}
/* this will be the loading indicator */
.q-table thead tr:last-child th {
/* height of all previous header rows */
top: 48px;
}
.q-table thead tr:first-child th {
top: 0;
}
.q-btn-group--outline > .q-btn-item:not(:last-child):before {
border-right: 1px solid #c4c4c4;
}
.q-btn-group--outline > .q-btn-item.active {
color: #2196f3 !important;
background-color: #fff;
}
.q-btn-group--outline > .q-btn-item.active:not(:last-child):before {
border: 1px solid #2196f3;
}
}
</style>

View file

@ -6,8 +6,8 @@ import config from "@/app.config";
import http from "@/plugins/http";
import type { QTableProps } from "quasar";
// import type { FormComment } from "@/modules/08_KPI/interface/request/index";
import type { ResEvaluator } from "@/modules/14_KPI/interface/response/Main";
import type { FormComment } from "@/modules/14_KPI/interface/request/index";
import type { ResEvaluator } from "@/modules/14_KPI/interface/response/index";
import DialogHeader from "@/components/DialogHeader.vue";
@ -54,24 +54,24 @@ const rows = ref<ResEvaluator[]>([]);
const filterKeyword = ref<string>("");
const modal = ref<boolean>(false);
// const formComment = reactive<FormComment>({
// topic: "",
// reason: "",
// });
// const optionTopicMain = ref<string[]>([
// " ",
// "",
// "",
// "",
// "",
// "",
// ]);
// const optionTopic = ref<string[]>(optionTopicMain.value);
const formComment = reactive<FormComment>({
topic: "",
reason: "",
});
const optionTopicMain = ref<string[]>([
"เห็นชอบตัวชี้วัดผลสัมฤทธิ์ของงาน รายการสมรรถนะ และน้ำหนักคะแนนของผู้ใต้บังคับบัญชา",
"ส่งกลับให้แก้ไข",
"บันทึกคำปรึกษาแนะนำ",
"บันทึกความเห็น",
"เห็นด้วยกับผลการประเมิน",
"บันทึกข้อเสนอแนะการพัฒนา",
]);
const optionTopic = ref<string[]>(optionTopicMain.value);
function fetchList() {
showLoader();
http
.get(config.API.kpiUserEvaluation + `/${props.type}/${evaluatorId.value}`)
.get(config.API.kpiEvaluation + `/${props.type}/${evaluatorId.value}`)
.then((res) => {
rows.value = res.data.result;
})
@ -83,54 +83,54 @@ function fetchList() {
});
}
// function openDialog() {
// modal.value = true;
// }
function openDialog() {
modal.value = true;
}
// function closeDialog() {
// modal.value = false;
// formComment.topic = "";
// formComment.reason = "";
// }
function closeDialog() {
modal.value = false;
formComment.topic = "";
formComment.reason = "";
}
// function createValue(val: string, done: Function) {
// if (val.length > 0) {
// if (!optionTopic.value.includes(val)) {
// optionTopic.value.push(val);
// }
// done(val);
// }
// }
function createValue(val: string, done: Function) {
if (val.length > 0) {
if (!optionTopic.value.includes(val)) {
optionTopic.value.push(val);
}
done(val);
}
}
// function filterFn(val: string, update: Function) {
// update(() => {
// optionTopic.value = optionTopicMain.value.filter(
// (v: string) => v.indexOf(val) > -1
// );
// });
// }
function filterFn(val: string, update: Function) {
update(() => {
optionTopic.value = optionTopicMain.value.filter(
(v: string) => v.indexOf(val) > -1
);
});
}
// function onSubmit() {
// dialogConfirm($q, () => {
// showLoader();
// http
// .put(
// config.API.kpiUserEvaluation + `/${props.type}/${evaluatorId.value}`,
// formComment
// )
// .then(() => {
// fetchList();
// closeDialog();
// success($q, "");
// })
// .catch((err) => {
// messageError($q, err);
// })
// .finally(() => {
// hideLoader();
// });
// });
// }
function onSubmit() {
dialogConfirm($q, () => {
showLoader();
http
.put(
config.API.kpiEvaluation + `/${props.type}/${evaluatorId.value}`,
formComment
)
.then(() => {
fetchList();
closeDialog();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
const pagination = ref({
sortBy: "desc",
@ -146,7 +146,15 @@ onMounted(() => {
<template>
<div class="q-pa-md">
<q-toolbar style="padding: 0px">
<q-btn v-if="isReadonly" flat round dense icon="add" color="blue-4">
<q-btn
v-if="isReadonly"
flat
round
dense
icon="add"
color="blue-4"
@click="openDialog"
>
<q-tooltip>เสนอความคดเห</q-tooltip>
</q-btn>
<q-space />
@ -179,7 +187,7 @@ onMounted(() => {
</div>
</q-toolbar>
<div class="col-12">
<d-table
<q-table
ref="table"
:columns="columns"
:rows="rows"
@ -221,11 +229,11 @@ onMounted(() => {
direction-links
></q-pagination>
</template>
</d-table>
</q-table>
</div>
</div>
<!-- <q-dialog v-model="modal" persistent>
<q-dialog v-model="modal" persistent>
<q-card style="width: 700px; max-width: 80vw">
<DialogHeader :tittle="'เสนอความคิดเห็น'" :close="closeDialog" />
<q-separator />
@ -278,7 +286,48 @@ onMounted(() => {
</q-card-actions>
</q-form>
</q-card>
</q-dialog> -->
</q-dialog>
</template>
<style scoped></style>
<style scoped>
.custom-table {
max-height: 64vh;
.q-table tr:nth-child(odd) td {
background: white;
}
.q-table tr:nth-child(even) td {
background: #f8f8f8;
}
.q-table thead tr {
background: #ecebeb;
}
.q-table thead tr th {
position: sticky;
}
.q-table td:nth-of-type(2) {
z-index: 3 !important;
}
.q-table th:nth-of-type(2),
.q-table td:nth-of-type(2) {
position: sticky;
left: 0;
z-index: 1;
}
/* this will be the loading indicator */
.q-table thead tr:last-child th {
/* height of all previous header rows */
top: 48px;
}
.q-table thead tr:first-child th {
top: 0;
}
}
</style>

View file

@ -19,14 +19,13 @@ const {
messageError,
dialogRemove,
} = useCounterMixin();
interface ArrayFileList {
id: string;
pathName: string;
fileName: string;
}
const isReadonly = <boolean>(route.name === "KPIDetail" ? true : false);
const isReadonly = <boolean>(route.name === "KPIEditEvaluator" ? true : false);
const documentFile = ref<any>(null);
const fileList = ref<ArrayFileList[]>([]);
@ -34,7 +33,7 @@ const fileList = ref<ArrayFileList[]>([]);
async function getData() {
showLoader();
await http
.get(config.API.kpiFile + `/KPI/ไฟล์เอกสาร/${id.value}`)
.get(config.API.KpiFile + `/KPI/ไฟล์เอกสาร/${id.value}`)
.then((res) => {
fileList.value = res.data;
})
@ -47,56 +46,57 @@ async function getData() {
}
async function uploadFileDoc(uploadUrl: string, file: any) {
// const Data = new FormData();
// Data.append("file", documentFile.value);
// showLoader();
// await axios
// .put(uploadUrl, file, {
// headers: {
// "Content-Type": file.type,
// },
// })
// .then((res) => {
// success($q, "");
// getData();
// })
// .catch((e) => {
// messageError($q, e);
// })
// .finally(() => {
// hideLoader();
// documentFile.value = null;
// });
const Data = new FormData();
Data.append("file", documentFile.value);
showLoader();
await axios
.put(uploadUrl, file, {
headers: {
"Content-Type": file.type,
},
})
.then((res) => {
success($q, "อัปโหลดไฟล์สำเร็จ");
getData();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
documentFile.value = null;
});
}
async function clickUpload(file: any) {
// const fileName = { fileName: file.name };
// dialogConfirm(
// $q,
// async () => {
// const selectedFile = file;
// const formdata = new FormData();
// formdata.append("file", selectedFile);
// await http
// .post(config.API.file + `/KPI//${id.value}`, {
// replace: false,
// fileList: fileName,
// })
// .then(async (res) => {
// const foundKey: string | undefined = Object.keys(res.data).find(
// (key) =>
// res.data[key]?.fileName !== undefined &&
// res.data[key]?.fileName !== ""
// );
// foundKey &&
// uploadFileDoc(res.data[foundKey]?.uploadUrl, documentFile.value);
// })
// .catch((err) => {
// messageError($q, err);
// });
// },
// "",
// " ?"
// );
const fileName = { fileName: file.name };
dialogConfirm(
$q,
async () => {
const selectedFile = file;
const formdata = new FormData();
formdata.append("file", selectedFile);
await http
.post(config.API.file + `/KPI/ไฟล์เอกสาร/${id.value}`, {
replace: false,
fileList: fileName,
})
.then(async (res) => {
const foundKey: string | undefined = Object.keys(res.data).find(
(key) =>
res.data[key]?.fileName !== undefined &&
res.data[key]?.fileName !== ""
);
foundKey &&
uploadFileDoc(res.data[foundKey]?.uploadUrl, documentFile.value);
})
.catch((err) => {
messageError($q, err);
});
},
"ยืนยันการอัปโหลดไฟล์",
"ต้องการยืนยันการอัปโหลดไฟล์นี้หรือไม่ ?"
);
}
/**
@ -106,7 +106,7 @@ async function clickUpload(file: any) {
function downloadFile(fileName: string) {
showLoader();
http
.get(config.API.kpiFile + `/KPI/ไฟล์เอกสาร/${id.value}/${fileName}`)
.get(config.API.file + `/KPI/ไฟล์เอกสาร/${id.value}/${fileName}`)
.then((res) => {
const data = res.data.downloadUrl;
window.open(data, "_blank");
@ -127,7 +127,7 @@ function deleteFile(fileName: string) {
dialogRemove($q, async () => {
showLoader();
http
.delete(config.API.kpiFile + `/KPI/ไฟล์เอกสาร/${id.value}/${fileName}`)
.delete(config.API.file + `/KPI/ไฟล์เอกสาร/${id.value}/${fileName}`)
.then((res) => {
success($q, `ลบไฟล์สำเร็จ`);

View file

@ -0,0 +1,949 @@
<script setup lang="ts">
import { ref, reactive, watch, computed } from "vue";
import { useQuasar } from "quasar";
import { useRoute } from "vue-router";
import config from "@/app.config";
import http from "@/plugins/http";
import type { DataOptions } from "@/modules/14_KPI/interface/index/Main";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
import { useKpiDataStore } from "@/modules/14_KPI/store";
const $q = useQuasar();
const mixin = useCounterMixin();
const store = useKpiDataStore();
const {
showLoader,
hideLoader,
messageError,
dialogConfirm,
dialogMessageNotify,
success,
date2Thai,
} = mixin;
const modal = defineModel<boolean>("modal", { required: true });
const numpage = defineModel<number>("numpage", { required: true });
const isStatusEdit = defineModel<boolean>("isStatusEdit", { required: true });
const kpiUserPlannedId = defineModel<string>("kpiUserPlannedId", {
required: true,
});
const search = ref<string>("");
const listCheckID = ref<string | null>(null);
const listTarget = ref<any>([]);
const formFilter = reactive<any>({
isAll: false,
keyword: "",
node: 0,
nodeId: "",
period: "",
year: null,
page: 1,
pageSize: 20,
});
const totalList = ref<number>(0); //
const maxPage = ref<number>(1);
const formDetail = reactive<any>({
orgRevisionId: "",
id: "",
year: null,
round: "",
kpiPeriodId: "",
includingName: "",
including: "",
target: "",
unit: "",
weight: null,
achievement1: "",
achievement2: "",
achievement3: "",
achievement4: "",
achievement5: "",
meaning: "",
formula: "",
node: null,
nodeId: "",
nodeName: "",
strategy: null,
strategyId: "",
strategyName: "",
documentInfoEvidence: "",
date: null,
});
/** Option รอบการประเมิน*/
const roundOp = ref<DataOptions[]>([
{ id: "APR", name: "รอบเมษายน" },
{ id: "OCT", name: "รอบตุลาคม" },
]);
function fetchListPlan() {
formFilter.nodeId = store.dataProfile.nodeId;
formFilter.node = store.dataProfile.node;
formFilter.year = formFilter?.year ? formFilter.year.toString() : "";
// const kpiPeriodId = store.dataEvaluation.kpiPeriodId;
showLoader();
http
.post(config.API.kpiPlan + `/search`, formFilter)
.then((res) => {
listTarget.value = res.data.result.data;
maxPage.value = Math.ceil(res.data.result.total / formFilter.pageSize);
totalList.value = res.data.result.total;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function fetchListPlanByid(id: string) {
showLoader();
http
.get(config.API.kpiAchievement("planned") + `/${id}`)
.then((res) => {
const data = res.data.result;
formDetail.target = data.target;
formDetail.unit = data.unit;
formDetail.weight = data.weight;
formDetail.meaning = data.meaning;
formDetail.formula = data.formula;
formDetail.achievement1 = data.achievement1;
formDetail.achievement2 = data.achievement2;
formDetail.achievement3 = data.achievement3;
formDetail.achievement4 = data.achievement4;
formDetail.achievement5 = data.achievement5;
formDetail.documentInfoEvidence = data.documentInfoEvidence;
if (data.startDate && data.endDate) {
formDetail.date = [];
formDetail.date[0] = data.startDate;
formDetail.date[1] = data.endDate;
}
clickList(data.kpiPlanId, true);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function fetchListRole() {
// const kpiPeriodId = store.dataEvaluation.kpiPeriodId;
// const position = store.dataProfile.position;
formFilter.nodeId = store.dataProfile.nodeId;
formFilter.node = store.dataProfile.node;
formFilter.year = formFilter?.year ? formFilter.year.toString() : "";
formFilter.position = store.dataProfile.position;
http
.post(config.API.kpiRole + `/search`, formFilter)
.then((res) => {
listTarget.value = res.data.result.data;
maxPage.value = Math.ceil(res.data.result.total / formFilter.pageSize);
totalList.value = res.data.result.total;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function fetchRoleByid(id: string) {
showLoader();
http
.get(config.API.kpiAchievement("role") + `/${id}`)
.then((res) => {
const data = res.data.result;
formDetail.target = data.target;
formDetail.unit = data.unit;
formDetail.weight = data.weight;
formDetail.meaning = data.meaning;
formDetail.formula = data.formula;
formDetail.achievement1 = data.achievement1;
formDetail.achievement2 = data.achievement2;
formDetail.achievement3 = data.achievement3;
formDetail.achievement4 = data.achievement4;
formDetail.achievement5 = data.achievement5;
formDetail.documentInfoEvidence = data.documentInfoEvidence;
if (data.startDate && data.endDate) {
formDetail.date = [];
formDetail.date[0] = data.startDate;
formDetail.date[1] = data.endDate;
}
clickList(data.kpiRoleId, true);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function fetchListSpecial() {
formFilter.nodeId = store.dataProfile.nodeId;
formFilter.node = store.dataProfile.node;
formFilter.year = formFilter?.year ? formFilter.year.toString() : "";
const body = {
keyword: formFilter.keyword,
period: formFilter.period,
year: formFilter.year,
pageSize: formFilter.pageSize,
page: formFilter.page,
};
showLoader();
http
.post(config.API.kpiSpecial + `/search`, body)
.then((res) => {
listTarget.value = res.data.result.data;
maxPage.value = Math.ceil(res.data.result.total / formFilter.pageSize);
totalList.value = res.data.result.total;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function fetchspecialByid(id: string) {
showLoader();
http
.get(config.API.kpiAchievement("special") + `/${id}`)
.then((res) => {
const data = res.data.result;
formDetail.including = data.including;
formDetail.includingName = data.includingName;
formDetail.target = data.target;
formDetail.unit = data.unit;
formDetail.achievement1 = data.achievement1;
formDetail.achievement2 = data.achievement2;
formDetail.achievement3 = data.achievement3;
formDetail.achievement4 = data.achievement4;
formDetail.achievement5 = data.achievement5;
formDetail.weight = data.weight;
formDetail.formula = data.formula;
formDetail.meaning = data.meaning;
formDetail.documentInfoEvidence = data.documentInfoEvidence;
if (data.startDate && data.endDate) {
formDetail.date = [];
formDetail.date[0] = data.startDate;
formDetail.date[1] = data.endDate;
}
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function clickList(id: string, isData: boolean = false) {
showLoader();
const url =
numpage.value === 1
? config.API.kpiPlan
: numpage.value === 2
? config.API.kpiRole
: config.API.kpiSpecial;
http
.get(`${url}/${id}`)
.then((res) => {
listCheckID.value = id;
const data = res.data.result;
if (!isData) {
formDetail.target = data.target;
formDetail.unit = data.unit;
formDetail.weight = data.weight;
formDetail.meaning = data.meaning;
formDetail.formula = data.formula;
formDetail.achievement1 = data.achievement1;
formDetail.achievement2 = data.achievement2;
formDetail.achievement3 = data.achievement3;
formDetail.achievement4 = data.achievement4;
formDetail.achievement5 = data.achievement5;
}
formDetail.orgRevisionId = data.corgRevisionId;
formDetail.id = data.id;
formDetail.year = data.year;
formDetail.round = data.round;
formDetail.kpiPeriodId = data.kpiPeriodId;
formDetail.includingName = data.includingName;
formDetail.including = data.including;
formDetail.node = data.node;
formDetail.nodeId = data.nodeId;
formDetail.nodeName = data.nodeName;
formDetail.strategy = data.strategy;
formDetail.strategyId = data.strategyId;
formDetail.strategyName = data.strategyName;
formDetail.documentInfoEvidence = data.documentInfoEvidence;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** ปิด dialog */
function closeDialog() {
modal.value = false;
search.value = "";
listCheckID.value = null;
formDetail.orgRevisionId = "";
formDetail.id = "";
formDetail.year = "";
formDetail.round = "";
formDetail.kpiPeriodId = "";
formDetail.includingName = "";
formDetail.including = "";
formDetail.target = "";
formDetail.unit = "";
formDetail.weight = "";
formDetail.achievement1 = "";
formDetail.achievement2 = "";
formDetail.achievement3 = "";
formDetail.achievement4 = "";
formDetail.achievement5 = "";
formDetail.meaning = "";
formDetail.formula = "";
formDetail.node = "";
formDetail.nodeId = "";
formDetail.strategy = "";
formDetail.strategyId = "";
formDetail.documentInfoEvidence = "";
formDetail.date = null;
formFilter.isAll = false;
formFilter.keyword = "";
formFilter.node = 0;
formFilter.nodeId = "";
formFilter.period = "";
formFilter.year = null;
formFilter.page = 1;
formFilter.pageSize = 20;
}
function onSubmit() {
if (!listCheckID.value && numpage.value !== 3) {
dialogMessageNotify($q, "กรุณาเลือกตัวชี้วัด");
} else {
dialogConfirm($q, async () => {
showLoader();
const formBody = {
target: formDetail.target,
unit: formDetail.unit,
weight: Number(formDetail.weight),
meaning: formDetail.meaning,
formula: formDetail.formula,
kpiUserEvaluationId: store.dataEvaluation.id,
kpiPlanId: numpage.value === 1 ? listCheckID.value : undefined,
kpiRoleId: numpage.value === 2 ? listCheckID.value : undefined,
achievement1: formDetail.achievement1,
achievement2: formDetail.achievement2,
achievement3: formDetail.achievement3,
achievement4: formDetail.achievement4,
achievement5: formDetail.achievement5,
documentInfoEvidence: formDetail.documentInfoEvidence,
startDate: formDetail.date ? formDetail.date[0] : undefined,
endDate: formDetail.date ? formDetail.date[1] : undefined,
including: numpage.value === 3 ? formDetail.including : undefined,
includingName:
numpage.value === 3 ? formDetail.includingName : undefined,
period:
numpage.value === 3 ? store.dataEvaluation.durationKPI : undefined,
year:
numpage.value === 3
? store.dataEvaluation.year.toString()
: undefined,
};
try {
const urlPlanned = isStatusEdit.value
? config.API.kpiAchievement("planned") + `/${kpiUserPlannedId.value}`
: config.API.kpiAchievement("planned");
const urlRole = isStatusEdit.value
? config.API.kpiAchievement("role") + `/${kpiUserPlannedId.value}`
: config.API.kpiAchievement("role");
const urlSpecial = isStatusEdit.value
? config.API.kpiAchievement("special") + `/${kpiUserPlannedId.value}`
: config.API.kpiAchievement("special");
const url =
numpage.value === 1
? urlPlanned
: numpage.value === 2
? urlRole
: urlSpecial;
const method = isStatusEdit.value ? "put" : "post";
await http[method](url, formBody);
closeDialog();
success($q, "บันทึกข้อมูลสำเร็จ");
} catch (err) {
messageError($q, err);
} finally {
hideLoader();
}
});
}
}
function fetchNewList() {
formFilter.page = 1;
numpage.value === 1
? fetchListPlan()
: numpage.value === 2
? fetchListRole()
: fetchListSpecial();
}
watch(
() => modal.value,
() => {
if (modal.value) {
if (numpage.value === 1) {
fetchListPlan();
isStatusEdit.value && fetchListPlanByid(kpiUserPlannedId.value);
} else if (numpage.value === 2) {
fetchListRole();
isStatusEdit.value && fetchRoleByid(kpiUserPlannedId.value);
} else if (numpage.value === 3) {
fetchListSpecial();
isStatusEdit.value && fetchspecialByid(kpiUserPlannedId.value);
}
}
}
);
const title = computed(() => {
let name = "";
if (numpage.value === 1) {
name = isStatusEdit.value
? "แก้ไขตัวชี้วัดตามแผนปฏิบัติราชการประจําปี"
: "เพิ่มตัวชี้วัดตามแผนปฏิบัติราชการประจําปี";
} else if (numpage.value === 2) {
name = isStatusEdit.value
? "แก้ไขตัวชี้วัดตามหน้าที่ความรับผิดชอบ"
: "เพิ่มตัวชี้วัดตามหน้าที่ความรับผิดชอบ";
} else if (numpage.value === 3) {
name = isStatusEdit.value
? "แก้ไขตัวชี้วัดที่ได้รับมอบหมาย"
: "เพิ่มตัวชี้วัดที่ได้รับมอบหมาย";
}
return name;
});
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card class="col-12" style="width: 100%">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader :tittle="title" :close="closeDialog" />
<q-separator />
<q-card-section class="q-pa-none scroll" style="max-height: 75vh">
<div class="col-12 row">
<div class="bg-grey-1 q-pa-md col-3 row lineRight">
<div class="col-12 fit">
<div class="row col-12" v-if="numpage !== 3">
<q-checkbox
v-model="formFilter.isAll"
label="แสดงตัวชี้วัดภายใต้หน่วยงาน/ส่วนราชการทุกระดับ"
@update:model-value="fetchNewList()"
/>
</div>
<div class="row q-col-gutter-sm col-12">
<div class="col-5">
<datepicker
menu-class-name="modalfix"
v-model="formFilter.year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="fetchNewList()"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
outlined
:model-value="
formFilter.year === null || formFilter.year === ''
? null
: Number(formFilter.year) + 543
"
:label="`${'ปีงบประมาณ'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
<template v-slot:append>
<q-icon
v-if="formFilter.year"
name="cancel"
class="cursor-pointer"
@click.stop.prevent="
(formFilter.year = null), fetchNewList()
"
/>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-7">
<q-select
dense
outlined
v-model="formFilter.period"
:options="roundOp"
option-label="name"
option-value="id"
emit-value
map-options
input-class="text-red"
label="รอบการประเมิน"
clearable
@update:model-value="fetchNewList()"
/>
</div>
</div>
<div class="col-12 q-mt-sm">
<q-input
v-model="formFilter.keyword"
outlined
dense
label="ค้นหา"
@keydown.enter.prevent="fetchNewList()"
>
<template v-slot:append>
<q-icon v-if="formFilter.keyword == ''" name="search" />
<q-icon
v-if="formFilter.keyword !== ''"
name="clear"
class="cursor-pointer"
@click="(formFilter.keyword = ''), fetchNewList()"
/>
</template>
</q-input>
</div>
<q-card bordered flat class="q-mt-sm no-shadow bg-white col-12">
<div class="row q-px-md q-py-sm items-center bg-grey-1">
<div class="col-4">ลำด/รหสตวช</div>
<div class="col-4">อตวช</div>
</div>
<q-separator />
<q-card-section class="q-pa-none">
<q-list separator>
<q-item
dense
v-for="(item, index) in listTarget"
:key="index"
clickable
v-ripple
:active="listCheckID === item.id"
active-class="my-menu-link"
@click="clickList(item.id)"
>
<q-item-section class="q-pa-none">
<div class="row items-center" style="height: 50px">
<div class="col-4">
{{ item.including }}
</div>
<div class="col-4">
{{ item.includingName }}
</div>
</div>
</q-item-section>
</q-item>
</q-list>
<q-separator />
<div
class="q-pa-lg flex justify-end"
v-if="totalList !== 0"
>
งหมด {{ totalList }} รายการ
<q-pagination
v-model="formFilter.page"
active-color="primary"
color="dark"
:max="Number(maxPage)"
size="sm"
boundary-links
direction-links
:max-pages="5"
@update:model-value="
numpage === 1
? fetchListPlan()
: numpage === 2
? fetchListRole()
: fetchListSpecial()
"
></q-pagination>
</div>
</q-card-section>
</q-card>
</div>
</div>
<div class="col-9">
<div class="row q-pa-md q-col-gutter-sm">
<div class="col-12">
<span class="text-body2 text-weight-medium"
>รายละเอยดตวช</span
>
</div>
<div class="col-6">
<q-card bordered class="full-height q-pa-sm">
<div class="q-pa-sm q-col-gutter-lg">
<div class="col-12 row" v-if="numpage !== 3">
<div class="col-4 text-grey-6">หนวยงาน/วนราชการ</div>
<div class="col-8">{{ formDetail.nodeName }}</div>
</div>
<div class="col-12 row" v-if="numpage === 1">
<div class="col-4 text-grey-6">ทธศาสตร / แผน</div>
<div class="col-8">{{ formDetail.strategyName }}</div>
</div>
<div class="col-12 row">
<div class="col-4 text-grey-6">ลำด/รหสตวช</div>
<div class="col-8">
<q-input
v-if="numpage === 3"
outlined
v-model="formDetail.including"
bg-color="white"
dense
class="inputgreen"
:rules="[
(val) =>
!!val || `${'กรุณากรอกลำดับ/รหัสตัวชี้วัด'}`,
]"
lazy-rules
hide-bottom-space
/>
<div v-else>{{ formDetail.including }}</div>
</div>
</div>
<div class="col-12 row">
<div class="col-4 text-grey-6">อตวช</div>
<div class="col-8">
<q-input
v-if="numpage === 3"
outlined
v-model="formDetail.includingName"
bg-color="white"
dense
class="inputgreen"
:rules="[
(val) => !!val || `${'กรุณากรอกชื่อตัวชี้วัด'}`,
]"
lazy-rules
hide-bottom-space
/>
<div v-else>{{ formDetail.includingName }}</div>
</div>
</div>
<div class="col-12 row">
<div class="col-4 text-grey-6">าเปาหมาย</div>
<div class="col-8">
<q-input
outlined
v-model="formDetail.target"
bg-color="white"
dense
class="inputgreen"
:rules="[
(val) => !!val || `${'กรุณากรอกค่าเป้าหมาย'}`,
]"
hide-bottom-space
lazy-rules
/>
</div>
</div>
<div class="col-12 row">
<div class="col-4 text-grey-6">หนวยน</div>
<div class="col-8">
<q-input
outlined
v-model="formDetail.unit"
bg-color="white"
dense
class="inputgreen"
:rules="[
(val) => !!val || `${'กรุณากรอกหน่วยนับ'}`,
]"
lazy-rules
hide-bottom-space
reverse-fill-mask
/>
</div>
</div>
<div class="col-12 row">
<div class="col-4 text-grey-6">ำหน (อยละ)</div>
<div class="col-8">
<q-input
outlined
v-model="formDetail.weight"
bg-color="white"
dense
class="inputgreen"
:rules="[
(val) =>
!!val || `${'กรุณากรอกน้ำหนัก (ร้อยละ)'}`,
]"
hide-bottom-space
lazy-rules
mask="###"
/>
</div>
</div>
</div>
</q-card>
</div>
<div class="col-6 row">
<div class="row col-12 card-box">
<div
class="col-12 bg-grey-2 row items-center text-weight-medium"
>
<div class="col-6 text-center">ระดบคะแนน</div>
<div class="col-6 text-center">ผลสำเรจของงาน</div>
</div>
<div
class="row col-12 items-center lineTop q-col-gutter-sm"
>
<div class="col-6 text-center text-body2">5</div>
<div class="col-6">
<q-input
v-model="formDetail.achievement5"
outlined
dense
:rules="[(val:string) => !!val || `${'กรุณากรอกผลสำเร็จของงาน'}`,]"
hide-bottom-space
class="inputgreen"
lazy-rules
/>
</div>
</div>
<div
class="row col-12 items-center lineTop q-col-gutter-sm"
>
<div class="col-6 text-center text-body2">4</div>
<div class="col-6 text-center text-primary">
<q-input
v-model="formDetail.achievement4"
outlined
dense
:rules="[(val:string) => !!val || `${'กรุณากรอกผลสำเร็จของงาน'}`,]"
hide-bottom-space
class="inputgreen"
lazy-rules
/>
</div>
</div>
<div
class="row col-12 items-center lineTop q-col-gutter-sm"
>
<div class="col-6 text-center text-body2">3</div>
<div class="col-6 text-center text-primary">
<q-input
v-model="formDetail.achievement3"
outlined
dense
:rules="[(val:string) => !!val || `${'กรุณากรอกผลสำเร็จของงาน'}`,]"
hide-bottom-space
class="inputgreen"
lazy-rules
/>
</div>
</div>
<div
class="row col-12 items-center lineTop q-col-gutter-sm"
>
<div class="col-6 text-center text-body2">2</div>
<div class="col-6 text-center text-primary">
<q-input
v-model="formDetail.achievement2"
outlined
dense
:rules="[(val:string) => !!val || `${'กรุณากรอกผลสำเร็จของงาน'}`,]"
hide-bottom-space
class="inputgreen"
lazy-rules
/>
</div>
</div>
<div
class="row col-12 items-center lineTop q-col-gutter-sm"
>
<div class="col-6 text-center text-body2">1</div>
<div class="col-6 text-center text-primary">
<q-input
v-model="formDetail.achievement1"
outlined
dense
:rules="[(val:string) => !!val || `${'กรุณากรอกผลสำเร็จของงาน'}`,]"
hide-bottom-space
class="inputgreen"
lazy-rules
/>
</div>
</div>
</div>
</div>
<div class="col-12">
<q-input
v-model="formDetail.meaning"
label="นิยามหรือความหมายของตัวชี้วัด"
type="textarea"
outlined
dense
:rules="[(val:string) => !!val || `${'กรุณากรอกตัวชี้วัด'}`,]"
hide-bottom-space
class="inputgreen"
lazy-rules
/>
</div>
<div class="col-12">
<q-input
v-model="formDetail.formula"
label="สูตรคำนวณ"
type="textarea"
outlined
dense
:rules="[(val:string) => !!val || `${'กรุณากรอกตัวชี้วัด'}`,]"
hide-bottom-space
class="inputgreen"
lazy-rules
/>
</div>
<div class="col-12">
<q-input
v-model="formDetail.documentInfoEvidence"
label="ข้อมูลเอกสารหลักฐาน"
type="textarea"
outlined
dense
:rules="[(val:string) => !!val || `${'กรุณากรอกข้อมูลเอกสารหลักฐาน'}`,]"
hide-bottom-space
class="inputgreen"
lazy-rules
/>
</div>
<div class="col-6">
<datepicker
v-model="formDetail.date"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
range
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
outlined
class="inputgreen"
:model-value="
formDetail.date
? `${date2Thai(formDetail.date[0])} - ${date2Thai(
formDetail.date[1]
)}`
: null
"
:label="`${'ช่วงเวลาเริ่มต้น-สิ้นสุด'}`"
hide-bottom-space
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
<template v-slot:append>
<q-icon
v-if="formDetail.date !== null"
name="cancel"
class="cursor-pointer"
@click="formDetail.date = null"
/>
</template>
</q-input>
</template>
</datepicker>
</div>
</div>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn label="บันทึก" color="secondary" type="submit"
><q-tooltip>นทกขอม</q-tooltip></q-btn
>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<style scoped>
.my-menu-link {
background: #ebf9f7 !important;
color: #1bb19ab8 !important;
}
.no-shadow {
box-shadow: none !important;
}
.lineRight {
border-right: 1px solid #ededed !important;
}
.lineTop {
border-top: 1px solid #ededed !important;
}
.card-box {
border: 1px solid #ededed !important;
border-radius: 8px;
}
</style>

View file

@ -4,11 +4,11 @@ import { useQuasar } from "quasar";
import config from "@/app.config";
import http from "@/plugins/http";
import type { FormDataAssigned } from "@/modules/08_KPI/interface/request/index";
import type { FormDataAssigned } from "@/modules/14_KPI/interface/request/index";
import DialogHeader from "@/components/DialogHeader.vue";
import { useKpiDataStore } from "@/modules/08_KPI/store";
import { useKpiDataStore } from "@/modules/14_KPI/store";
import { useCounterMixin } from "@/stores/mixin";
const $q = useQuasar();
@ -33,7 +33,7 @@ const formData = reactive<FormDataAssigned>({
including: "", //
includingName: "", //
target: "", //
unit: null, //
unit: "", //
weight: null, // ()
meaning: "", //
formula: "", //
@ -80,7 +80,7 @@ function closeDialog() {
formData.including = "";
formData.includingName = "";
formData.target = "";
formData.unit = null;
formData.unit = "";
formData.achievement1 = "";
formData.achievement2 = "";
formData.achievement3 = "";
@ -95,7 +95,7 @@ function onSubmit() {
dialogConfirm($q, async () => {
showLoader();
formData.weight = Number(formData.weight);
formData.unit = Number(formData.unit);
formData.unit = formData.unit;
formData.kpiUserEvaluationId = store.dataEvaluation.id;
try {
@ -140,11 +140,11 @@ watch(
<q-input
outlined
v-model="formData.including"
label="รหัสตัวชี้วัด"
label="ลำดับ/รหัสตัวชี้วัด"
bg-color="white"
dense
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกรหัสตัวชี้วัด'}`]"
:rules="[(val) => !!val || `${'กรุณากรอกลำดับ/รหัสตัวชี้วัด'}`]"
hide-bottom-space
lazy-rules
/>
@ -182,7 +182,6 @@ watch(
:rules="[(val:string) => !!val || `${'กรุณากรอกหน่วยนับ'}`,]"
hide-bottom-space
class="inputgreen"
mask="#"
reverse-fill-mask
lazy-rules
/>

View file

@ -1,15 +1,15 @@
<!-- <script setup lang="ts">
<script setup lang="ts">
import { ref, reactive, onMounted, watch } from "vue";
import DialogHeader from "@/components/DialogHeader.vue";
import type { DataOptions } from "@/modules/08_KPI/interface/index/Main";
import type { ListCapacity } from "@/modules/08_KPI/interface/request/index";
import { useKpiDataStore } from "@/modules/08_KPI/store";
import type { DataOptions } from "@/modules/14_KPI/interface/index/Main";
import type { ListCapacity } from "@/modules/14_KPI/interface/request/index";
import { useKpiDataStore } from "@/modules/14_KPI/store";
import { useCounterMixin } from "@/stores/mixin";
import http from "@/plugins/http";
import config from "@/app.config";
import { useQuasar } from "quasar";
import { useQuasar, type QTableProps } from "quasar";
import { useRoute } from "vue-router";
const dataListCapacityDetails = ref<ListCapacity[]>([]);
@ -23,8 +23,14 @@ const props = defineProps({
const $q = useQuasar();
const store = useKpiDataStore();
const expectedLevel = ref<any>();
const pagination = ref({
sortBy: "desc",
descending: false,
page: 1,
rowsPerPage: 20,
});
const weight = ref<number | null>(null);
const weight = ref<number>(100);
const mixin = useCounterMixin();
const {
showLoader,
@ -79,6 +85,28 @@ const fieldLabels = {
const competencyTypeOp = ref<DataOptions[]>(store.competencyType);
const visibleColumns = ref<String[]>(["level", "description"]);
const columns = ref<QTableProps["columns"]>([
{
name: "level",
align: "center",
label: "ระดับสมรรถนะ",
sortable: true,
field: "level",
headerStyle: "font-size: 14px",
style: "font-size: 14px; width:5px;",
},
{
name: "description",
align: "left",
label: "พฤติกรรมที่คาดหวัง/พฤติกรรมย่อย",
sortable: true,
field: "description",
headerStyle: "font-size: 14px",
style: "font-size: 14px; width:15%;",
},
]);
function clickList(index: string, data: any) {
const dataCapacityDetails = data.capacityDetails.sort(
(a: any, b: any) => a.level - b.level
@ -123,7 +151,7 @@ function closeDialog() {
formDetail.definition = "";
formDetail.criteria = "";
idProps.value = null;
weight.value = null;
// weight.value = null;
expectedLevel.value = null;
dataListCapacityDetails.value = [];
@ -147,7 +175,6 @@ function onSubmit() {
kpiCapacityId: formDetail.id,
level: expectedLevel.value.toString(),
weight: weight.value,
summary: 0,
};
showLoader();
http[idProps.value ? `put` : `post`](url, body)
@ -169,7 +196,7 @@ function onSubmit() {
function getData() {
showLoader();
http
.get(config.API.kpiCapacity + `?type=${type.value}`)
.get(config.API.KpiCapacity + `?type=${type.value}`)
.then((res) => {
const data = res.data.result.data;
listTarget.value = data;
@ -375,6 +402,7 @@ watch(
<div class="col-4 text-grey-6">ำหน (อยละ)</div>
<div class="col-8">
<q-input
readonly
v-model="weight"
dense
outlined
@ -386,11 +414,9 @@ watch(
/>
</div>
<div class="col-4 text-grey-6">ระดบทคาดหว</div>
<div
v-if="type == 'HEAD' || type == 'GROUP'"
class="col-8"
>
<div class="col-8">
<q-select
:readonly="type == 'HEAD' || type == 'GROUP'"
v-model="expectedLevel"
:options="expectedLevelOp"
dense
@ -402,7 +428,7 @@ watch(
class="inputgreen"
/>
</div>
<div v-else>
<!-- <div v-else>
<q-input
v-model="expectedLevel"
dense
@ -412,17 +438,69 @@ watch(
hide-bottom-space
class="inputgreen"
/>
</div>
</div> -->
</div>
</q-card>
</div>
<div class="col-7">
<q-card bordered class="col-12 row no-shadow">
<q-table
flat
bordered
dense
:paging="true"
row-key="level"
class="custom-table2"
virtual-scroll
:rows="dataListCapacityDetails"
hide-pagination
:columns="columns"
:visible-columns="visibleColumns"
v-model:pagination="pagination"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
class="bg-grey-2"
>
<span class="text-weight-bold">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
>
<div v-if="(col.name = 'description')">
<span v-html="col.value"></span>
</div>
<div v-else>
{{ col.value }}
<!-- <span v-html="item.description"></span> -->
</div>
</q-td>
</q-tr>
</template>
<template v-slot:no-data="{ icon, message }">
<div
class="q-pa-md text-weight-bold full-width text-center"
>
<span style="font-size: 16px">ไมพบขอมลสมรรถนะ</span>
</div>
</template>
</q-table>
<!-- <q-card bordered class="col-12 no-shadow">
<div class="bg-grey-2 row q-py-sm text-weight-bold col-12">
<div class="col-4 text-center">
<span>ระดบสมรรถนะ</span>
</div>
<div class="col-8 text-center">
<div class="col-8 text-start">
<span>พฤตกรรมทคาดหว/พฤตกรรมยอย</span>
</div>
</div>
@ -437,7 +515,7 @@ watch(
v-for="(item, index) in dataListCapacityDetails"
:key="item.id"
>
<div :class="`row q-pa-sm`">
<div class="row q-pa-sm">
<div class="col-4 text-center self-start text-body1">
<span>{{ item.level }}</span>
</div>
@ -445,9 +523,11 @@ watch(
<span v-html="item.description"></span>
</div>
</div>
<q-separator />
<q-separator
v-if="index !== dataListCapacityDetails.length - 1"
/>
</div>
</q-card>
</q-card> -->
</div>
</div>
</div>
@ -483,4 +563,42 @@ watch(
border: 1px solid #ededed !important;
border-radius: 8px;
}
</style> -->
.custom-table2 {
.q-table tr:nth-child(odd) td {
background: white;
}
.q-table tr:nth-child(even) td {
background: #f8f8f8;
}
.q-table thead tr {
background: #ecebeb;
}
.q-table thead tr th {
position: sticky;
}
.q-table td:nth-of-type(2) {
z-index: 3 !important;
}
.q-table th:nth-of-type(2),
.q-table td:nth-of-type(2) {
position: sticky;
left: 0;
z-index: 1;
}
/* this will be the loading indicator */
.q-table thead tr:last-child th {
/* height of all previous header rows */
top: 48px;
}
.q-table thead tr:first-child th {
top: 0;
}
}
</style>

View file

@ -0,0 +1,607 @@
<script setup lang="ts">
import { ref, watch, onMounted, reactive } from "vue";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
import http from "@/plugins/http";
import config from "@/app.config";
import type {
FormComment,
FormCommentByRole,
} from "@/modules/14_KPI/interface/request/index";
import { useRoute } from "vue-router";
import { useQuasar } from "quasar";
import { useKpiDataStore } from "@/modules/14_KPI/store";
const store = useKpiDataStore();
const $q = useQuasar();
const route = useRoute();
const id = ref<string>(route.params.id as string);
const mixin = useCounterMixin();
const {
dialogConfirm,
showLoader,
hideLoader,
messageError,
dialogMessageNotify,
success,
} = mixin;
const modal = defineModel<boolean>("modal", { required: true });
const type = defineModel<string>("type", { required: true });
const idList = defineModel<string>("idList", { required: true });
const modalAdd = ref<boolean>(false);
const sendId = ref<string>("");
const splitterModel = ref<number>(40);
const listTarget = ref<any>([]);
const listCheck = ref<string>("");
const reasonEvaluator = ref<string>(""); //
const reasonCommander = ref<string>(""); //
const reasonCommanderHigh = ref<string>(""); //
const reasonEvaluatorRef = ref<any>();
const reasonCommanderRef = ref<any>();
const reasonCommanderHighRef = ref<any>();
const sendType = ref<string>("");
const formDataAdd = reactive<FormComment>({
topic: "",
reason: "",
});
const formDataView = reactive<FormCommentByRole>({
id: "",
topic: "",
reason: "",
reasonEvaluator: "",
reasonCommander: "",
reasonCommanderHigh: "",
});
let count = ref<number>(0);
function clickList(index: string, data: any) {
// if (data.status == "DRAFT") {
// }
listCheck.value = index as string;
formDataView.id = data.id;
formDataView.topic = data.topic;
formDataView.reason = data.reason;
formDataView.reasonEvaluator = data.reasonEvaluator;
formDataView.reasonCommander = data.reasonCommander;
formDataView.reasonCommanderHigh = data.reasonCommanderHigh;
reasonEvaluator.value = data.reasonEvaluator ?? "";
reasonCommander.value = data.reasonCommander ?? "";
reasonCommanderHigh.value = data.reasonCommanderHigh ?? "";
if (count.value >= 1) {
reasonEvaluatorRef.value.reset();
reasonCommanderRef.value.reset();
reasonCommanderHighRef.value.reset();
}
count.value++;
showLoader();
setTimeout(() => {
hideLoader();
}, 100);
}
function onEdit(index: string, data: any) {
modalAdd.value = true;
sendId.value = data.id;
formDataAdd.topic = data.topic;
formDataAdd.reason = data.reason;
}
function onSubmitAdd() {
dialogConfirm($q, () => {
showLoader();
http
.put(
config.API.kpiCommentP(
"problem",
type.value + sendType.value,
store.rolePerson.toLowerCase(),
sendId.value ? sendId.value : idList.value
),
{
reason: formDataAdd.reason,
topic: formDataAdd.topic,
}
)
.then(async (res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
if (sendId.value) {
const id = res.data.result;
await closeAdd();
const data = listTarget.value.find((item: any) => item.id == id);
clickList(id, data);
} else {
sendId.value = res.data.result;
getList();
}
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
});
}
function onSubmit() {}
function close() {
count.value = 0;
modal.value = false;
reasonEvaluator.value = "";
reasonCommander.value = "";
reasonCommanderHigh.value = "";
listTarget.value = [];
listCheck.value = "";
}
function closeAdd() {
modalAdd.value = false;
formDataAdd.topic = "";
formDataAdd.reason = "";
sendId.value = "";
getList();
}
function getList() {
showLoader();
http
.get(
config.API.kpiCommentP(
"problem",
type.value,
store.rolePerson.toLowerCase(),
idList.value
)
)
.then((res) => {
const data = res.data.result;
listTarget.value = data;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
function onSubmitComment(role: string) {
dialogConfirm($q, () => {
const body = {
reason:
role == "evaluator"
? reasonEvaluator.value
: role == "commander"
? reasonCommander.value
: role == "commanderhigh"
? reasonCommanderHigh.value
: null,
};
showLoader();
http
.put(
config.API.kpiCommentP(
"problem",
type.value,
store.rolePerson.toLowerCase(),
formDataView.id
),
body
)
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
getList();
setTimeout(() => {
if (listCheck.value) {
const idCheck = listCheck.value;
const data = listTarget.value.find(
(item: any) => item.id == idCheck
);
clickList(idCheck, data);
}
}, 200);
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
});
}
function statusText(val: string) {
switch (val) {
case "DRAFT":
return "แบบร่าง";
case "EVALUATOR":
return "ผู้ประเมิน";
case "COMMANDER":
return "ผู้บังคับบัญชาเหนือขึ้นไป";
case "COMMANDERHIGH":
return "ผู้บังคับบัญชาเหนือขึ้นไปอีกขั้นหนึ่ง";
case "DONE":
return "เสร็จสิ้น";
default:
return "";
}
}
function onNoti() {
listCheck.value = "";
count.value = 0;
}
watch(
() => modal.value,
() => {
if (modal.value == true) {
getList();
}
}
);
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="min-width: 70vw">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader tittle="รายงานปัญหา" :close="close" />
<q-separator />
<q-card-section class="q-pa-none">
<q-splitter
v-model="splitterModel"
disable
separator-class="bg-gray"
separator-style="width: 1px"
>
<template v-slot:before>
<div class="q-pa-sm">
<q-btn
v-if="store.rolePerson === 'USER'"
icon="add"
color="teal"
flat
round
@click="
() => {
modalAdd = true;
}
"
>
<q-tooltip>เพมหวขอรายงานปญหา</q-tooltip>
</q-btn>
<q-card bordered flat class="no-shadow bg-white col-12">
<div class="row q-px-md q-py-sm items-center bg-grey-1">
<div class="col-12">
<span>วขอปญหา</span>
</div>
</div>
<q-separator />
<q-card-section class="q-pa-none">
<div v-if="listTarget.length > 0">
<q-list separator dense>
<q-item
v-for="(item, index) in listTarget"
:key="item.id"
clickable
v-ripple
:active="listCheck === item.id"
active-class="my-menu-link"
@click="
item.status == 'DRAFT'
? onNoti()
: clickList(item.id, item)
"
>
<q-item-section class="q-pa-none">
<div class="row items-center">
<div class="col-12 row justify-between">
<span>{{ item.topic }}</span>
<q-badge
v-if="item.status == 'DRAFT'"
outline
color="grey"
label="แบบร่าง"
@click="onEdit(item.id, item)"
/>
</div>
</div>
</q-item-section>
</q-item>
</q-list>
</div>
<div v-else class="q-pa-md">
<span>ไมพบขอม</span>
</div>
</q-card-section>
</q-card>
</div>
</template>
<template v-slot:after>
<div
v-if="!listCheck"
class="row col-12 items-center"
style="height: 30vh"
>
<q-banner class="q-pa-lg col-12 text-center">
<q-icon
name="mdi-hand-pointing-left"
size="lg"
color="primary"
/>
<p class="text-grey-9 q-pt-sm">กรณาเลอกหวขอรายงานปญหา</p>
</q-banner>
</div>
<div v-else>
<div class="row q-pa-md q-col-gutter-sm">
<div class="row col-12 text-weight-medium">
<div class="col-4 text-grey-6">วขอปญหา</div>
<div class="col-8">{{ formDataView.topic }}</div>
</div>
<div class="row col-12 text-weight-medium">
<div class="col-4 text-grey-6">รายละเอยดปญหา</div>
<div class="col-8">{{ formDataView.reason }}</div>
</div>
<div class="col-12">
<q-separator />
</div>
<!-- ความคดเหนของผประเม -->
<q-form
v-if="store.dataEvaluation.evaluatorId"
ref="reasonEvaluatorRef"
greedy
@submit.prevent
@validation-success="onSubmitComment('evaluator')"
class="full-width q-mt-sm"
>
<div class="row col-12 q-col-gutter-sm">
<div class="col-12">
<span class="text-weight-medium text-grey-6"
>ความคดเหนของผประเม</span
>
</div>
<div class="col-12">
<q-input
outlined
dense
:readonly="
formDataView.reasonEvaluator !== null ||
store.rolePerson !== 'EVALUATOR'
"
label="ความคิดเห็นของผู้ประเมิน"
v-model="reasonEvaluator"
type="textarea"
class="inputgreen"
lazy-rules
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณากรอกความคิดเห็นของผู้ประเมิน'}`,]"
></q-input>
</div>
<div
v-if="
formDataView.reasonEvaluator == null &&
store.rolePerson == 'EVALUATOR'
"
class="col-12"
align="right"
>
<q-btn
label="บันทึกความคิดเห็น"
color="secondary"
type="submit"
><q-tooltip>นทกความคดเห</q-tooltip></q-btn
>
</div>
</div>
</q-form>
<!-- ความคดเหนของผงคบบญชาเหนอขนไป -->
<q-form
v-if="store.dataEvaluation.commanderId"
class="full-width q-mt-sm"
ref="reasonCommanderRef"
greedy
@submit.prevent
@validation-success="onSubmitComment('commander')"
>
<div class="row col-12 q-col-gutter-sm">
<div class="col-12">
<span class="text-weight-medium text-grey-6"
>ความคดเหนของผงคบบญชาเหนอขนไป</span
>
</div>
<div class="col-12">
<q-input
outlined
dense
label="ความคิดเห็นของผู้บังคับบัญชาเหนือขึ้นไป"
v-model="reasonCommander"
type="textarea"
class="inputgreen"
lazy-rules
:readonly="
formDataView.reasonCommander !== null ||
store.rolePerson !== 'COMMANDER'
"
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณากรอกความคิดเห็นของผู้บังคับบัญชาเหนือขึ้นไป'}`,]"
></q-input>
</div>
<div
v-if="
formDataView.reasonCommander == null &&
store.rolePerson == 'COMMANDER'
"
class="col-12"
align="right"
>
<q-btn
label="บันทึกความคิดเห็น"
color="secondary"
type="submit"
><q-tooltip>นทกความคดเห</q-tooltip></q-btn
>
</div>
</div>
</q-form>
<q-form
v-if="store.dataEvaluation.commanderHighId"
class="full-width q-mt-sm"
ref="reasonCommanderHighRef"
greedy
@submit.prevent
@validation-success="onSubmitComment('commanderhigh')"
>
<div class="row col-12 q-col-gutter-sm">
<div class="col-12">
<span class="text-weight-medium text-grey-6"
>ความคดเหนของผงคบบญชาเหนอขนไปอกขนหน</span
>
</div>
<div class="col-12">
<q-input
outlined
dense
label="ความคิดเห็นของผู้บังคับบัญชาเหนือขึ้นไปอีกขั้นหนึ่ง"
v-model="reasonCommanderHigh"
type="textarea"
class="inputgreen"
lazy-rules
:readonly="
formDataView.reasonCommanderHigh !== null ||
store.rolePerson !== 'COMMANDERHIGH'
"
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณากรอกความคิดเห็นของผู้บังคับบัญชาเหนือขึ้นไปอีกขั้นหนึ่ง'}`,]"
></q-input>
</div>
<div
v-if="
formDataView.reasonCommanderHigh == null &&
store.rolePerson == 'COMMANDERHIGH'
"
class="col-12"
align="right"
>
<q-btn
label="บันทึกความคิดเห็น"
color="secondary"
type="submit"
><q-tooltip>นทกความคดเห</q-tooltip></q-btn
>
</div>
</div>
</q-form>
</div>
</div>
</template>
</q-splitter>
</q-card-section>
</q-form>
</q-card>
</q-dialog>
<q-dialog v-model="modalAdd" persistent>
<q-card style="min-width: 30vw">
<q-form greedy @submit.prevent @validation-success="onSubmitAdd">
<DialogHeader
:tittle="`${sendId ? 'แก้ไข' : 'เพิ่ม'}หัวข้อรายงานปัญหา`"
:close="closeAdd"
/>
<q-separator />
<q-card-section>
<div class="row q-col-gutter-sm">
<div class="col-12">
<q-input
v-model="formDataAdd.topic"
outlined
class="inputgreen"
label="หัวข้อรายงานปัญหา"
dense
lazy-rules
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณากรอกหัวข้อรายงานปัญหา'}`,]"
/>
</div>
<div class="col-12">
<q-input
v-model="formDataAdd.reason"
outlined
class="inputgreen"
label="รายละเอียดปัญหา"
dense
lazy-rules
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณากรอกรายละเอียดปัญหา'}`,]"
type="textarea"
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn
label="บันทึกแบบร่าง"
color="info"
type="submit"
@click="
() => {
sendType = '';
}
"
><q-tooltip>นทกแบบราง</q-tooltip></q-btn
>
<q-btn
v-if="sendId !== ''"
label="ส่งไปยังผู้ประเมิน"
color="secondary"
type="submit"
@click="
() => {
sendType = 'send';
}
"
><q-tooltip>งไปยงผประเม</q-tooltip></q-btn
>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<style scoped>
.border-right {
border-right: 1px solid #000;
}
.my-menu-link {
background: #ebf9f7 !important;
color: #1bb19ab8 !important;
}
</style>

View file

@ -0,0 +1,523 @@
<script setup lang="ts">
import { ref, watch, onMounted, reactive } from "vue";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
import http from "@/plugins/http";
import config from "@/app.config";
import type {
FormComment,
FormCommentByRole,
} from "@/modules/14_KPI/interface/request/index";
import { useRoute } from "vue-router";
import { useQuasar } from "quasar";
import { useKpiDataStore } from "@/modules/14_KPI/store";
const store = useKpiDataStore();
const $q = useQuasar();
const mixin = useCounterMixin();
const { dialogConfirm, showLoader, hideLoader, messageError, success } = mixin;
const modal = defineModel<boolean>("modal", { required: true });
const type = defineModel<string>("type", { required: true });
const idList = defineModel<string>("idList", { required: true });
const modalAdd = ref<boolean>(false);
const splitterModel = ref<number>(40);
const listTarget = ref<any>([]);
const listCheck = ref<string>("");
const reasonEvaluator = ref<string>(""); //
const reasonCommander = ref<string>(""); //
const reasonCommanderHigh = ref<string>(""); //
const reasonEvaluatorRef = ref<any>();
const reasonCommanderRef = ref<any>();
const reasonCommanderHighRef = ref<any>();
const formDataAdd = reactive<FormComment>({
topic: "",
reason: "",
});
const formDataView = reactive<FormCommentByRole>({
id: "",
topic: "",
reason: "",
reasonEvaluator: "",
reasonCommander: "",
reasonCommanderHigh: "",
});
let count = ref<number>(0);
function clickList(index: string, data: any) {
listCheck.value = index as string;
formDataView.id = data.id;
formDataView.topic = data.topic;
formDataView.reason = data.reason;
formDataView.reasonEvaluator = data.reasonEvaluator;
formDataView.reasonCommander = data.reasonCommander;
formDataView.reasonCommanderHigh = data.reasonCommanderHigh;
reasonEvaluator.value = data.reasonEvaluator ?? "";
reasonCommander.value = data.reasonCommander ?? "";
reasonCommanderHigh.value = data.reasonCommanderHigh ?? "";
if (count.value >= 1) {
reasonEvaluatorRef.value.reset();
reasonCommanderRef.value.reset();
reasonCommanderHighRef.value.reset();
}
count.value++;
showLoader();
setTimeout(() => {
hideLoader();
}, 100);
}
function onSubmitAdd() {
dialogConfirm($q, () => {
showLoader();
http
.put(
config.API.kpiCommentP(
"progress",
type.value,
store.rolePerson.toLocaleLowerCase(),
idList.value
),
{
reason: formDataAdd.reason,
topic: formDataAdd.topic,
}
)
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
getList();
closeAdd();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
});
}
function onSubmit() {}
function close() {
count.value = 0;
modal.value = false;
reasonEvaluator.value = "";
reasonCommander.value = "";
reasonCommanderHigh.value = "";
listTarget.value = [];
listCheck.value = "";
}
function closeAdd() {
modalAdd.value = false;
formDataAdd.topic = "";
formDataAdd.reason = "";
}
function getList() {
showLoader();
http
.get(
config.API.kpiCommentP(
"progress",
type.value,
store.rolePerson.toLocaleLowerCase(),
idList.value
)
)
.then((res) => {
const data = res.data.result;
listTarget.value = data;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
function onSubmitComment(role: string) {
dialogConfirm($q, () => {
const body = {
reason:
role == "evaluator"
? reasonEvaluator.value
: role == "commander"
? reasonCommander.value
: role == "commanderhigh"
? reasonCommanderHigh.value
: null,
};
showLoader();
http
.put(
config.API.kpiCommentP(
"progress",
type.value,
store.rolePerson.toLocaleLowerCase(),
formDataView.id
),
body
)
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
getList();
setTimeout(() => {
if (listCheck.value) {
const idCheck = listCheck.value;
const data = listTarget.value.find(
(item: any) => item.id == idCheck
);
clickList(idCheck, data);
}
}, 200);
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
});
}
watch(
() => modal.value,
() => {
if (modal.value == true) {
getList();
}
}
);
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="min-width: 70vw">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader tittle="รายงานความก้าวหน้า" :close="close" />
<q-separator />
<q-card-section class="q-pa-none">
<q-splitter
v-model="splitterModel"
disable
separator-class="bg-gray"
separator-style="width: 1px"
>
<template v-slot:before>
<div class="q-pa-sm">
<q-btn
v-if="store.rolePerson === 'USER'"
icon="add"
color="teal"
flat
round
@click="
() => {
modalAdd = true;
}
"
>
<q-tooltip>เพมความกาวหน</q-tooltip>
</q-btn>
<q-card bordered flat class="no-shadow bg-white col-12">
<div class="row q-px-md q-py-sm items-center bg-grey-1">
<div class="col-12">
<span>ความกาวหน</span>
</div>
</div>
<q-separator />
<q-card-section class="q-pa-none">
<div v-if="listTarget.length > 0">
<q-list separator dense>
<q-item
v-for="(item, index) in listTarget"
:key="item.id"
clickable
v-ripple
:active="listCheck === item.id"
active-class="my-menu-link"
@click="clickList(item.id, item)"
>
<q-item-section class="q-pa-none">
<div class="row items-center">
<div class="col-12">
<span>{{ item.topic }}</span>
</div>
</div>
</q-item-section>
</q-item>
</q-list>
</div>
<div v-else class="q-pa-md">
<span>ไมพบขอม</span>
</div>
</q-card-section>
</q-card>
</div>
</template>
<template v-slot:after>
<div
v-if="!listCheck"
class="row col-12 items-center"
style="height: 30vh"
>
<q-banner class="q-pa-lg col-12 text-center">
<q-icon
name="mdi-hand-pointing-left"
size="lg"
color="primary"
/>
<p class="text-grey-9 q-pt-sm">
กรณาเลอกหวขอความกาวหน
</p>
</q-banner>
</div>
<div v-else>
<div class="row q-pa-md q-col-gutter-sm">
<div class="row col-12 text-weight-medium">
<div class="col-4 text-grey-6">วขอความกาวหน</div>
<div class="col-8">{{ formDataView.topic }}</div>
</div>
<div class="row col-12 text-weight-medium">
<div class="col-4 text-grey-6">รายละเอยดความกาวหน</div>
<div class="col-8">{{ formDataView.reason }}</div>
</div>
<div class="col-12">
<q-separator />
</div>
<!-- ความคดเหนของผประเม -->
<q-form
v-if="store.dataEvaluation.evaluatorId"
ref="reasonEvaluatorRef"
greedy
@submit.prevent
@validation-success="onSubmitComment('evaluator')"
class="full-width q-mt-sm"
>
<div class="row col-12 q-col-gutter-sm">
<div class="col-12">
<span class="text-weight-medium text-grey-6"
>ความคดเหนของผประเม</span
>
</div>
<div class="col-12">
<q-input
outlined
dense
:readonly="
formDataView.reasonEvaluator !== null ||
store.rolePerson !== 'EVALUATOR'
"
label="ความคิดเห็นของผู้ประเมิน"
v-model="reasonEvaluator"
type="textarea"
class="inputgreen"
lazy-rules
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณากรอกความคิดเห็นของผู้ประเมิน'}`,]"
></q-input>
</div>
<div
v-if="
formDataView.reasonEvaluator == null &&
store.rolePerson == 'EVALUATOR'
"
class="col-12"
align="right"
>
<q-btn
label="บันทึกความคิดเห็น"
color="secondary"
type="submit"
><q-tooltip>นทกความคดเห</q-tooltip></q-btn
>
</div>
</div>
</q-form>
<!-- ความคดเหนของผงคบบญชาเหนอขนไป -->
<q-form
v-if="store.dataEvaluation.commanderId"
class="full-width q-mt-sm"
ref="reasonCommanderRef"
greedy
@submit.prevent
@validation-success="onSubmitComment('commander')"
>
<div class="row col-12 q-col-gutter-sm">
<div class="col-12">
<span class="text-weight-medium text-grey-6"
>ความคดเหนของผงคบบญชาเหนอขนไป</span
>
</div>
<div class="col-12">
<q-input
outlined
dense
label="ความคิดเห็นของผู้บังคับบัญชาเหนือขึ้นไป"
v-model="reasonCommander"
type="textarea"
class="inputgreen"
lazy-rules
:readonly="
formDataView.reasonCommander !== null ||
store.rolePerson !== 'COMMANDER'
"
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณากรอกความคิดเห็นของผู้บังคับบัญชาเหนือขึ้นไป'}`,]"
></q-input>
</div>
<div
v-if="
formDataView.reasonCommander == null &&
store.rolePerson == 'COMMANDER'
"
class="col-12"
align="right"
>
<q-btn
label="บันทึกความคิดเห็น"
color="secondary"
type="submit"
><q-tooltip>นทกความคดเห</q-tooltip></q-btn
>
</div>
</div>
</q-form>
<q-form
v-if="store.dataEvaluation.commanderHighId"
class="full-width q-mt-sm"
ref="reasonCommanderHighRef"
greedy
@submit.prevent
@validation-success="onSubmitComment('commanderhigh')"
>
<div class="row col-12 q-col-gutter-sm">
<div class="col-12">
<span class="text-weight-medium text-grey-6"
>ความคดเหนของผงคบบญชาเหนอขนไปอกขนหน</span
>
</div>
<div class="col-12">
<q-input
outlined
dense
label="ความคิดเห็นของผู้บังคับบัญชาเหนือขึ้นไปอีกขั้นหนึ่ง"
v-model="reasonCommanderHigh"
type="textarea"
class="inputgreen"
lazy-rules
:readonly="
formDataView.reasonCommanderHigh !== null ||
store.rolePerson !== 'COMMANDERHIGH'
"
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณากรอกความคิดเห็นของผู้บังคับบัญชาเหนือขึ้นไปอีกขั้นหนึ่ง'}`,]"
></q-input>
</div>
<div
v-if="
formDataView.reasonCommanderHigh == null &&
store.rolePerson == 'COMMANDERHIGH'
"
class="col-12"
align="right"
>
<q-btn
label="บันทึกความคิดเห็น"
color="secondary"
type="submit"
><q-tooltip>นทกความคดเห</q-tooltip></q-btn
>
</div>
</div>
</q-form>
</div>
</div>
</template>
</q-splitter>
</q-card-section>
</q-form>
</q-card>
</q-dialog>
<q-dialog v-model="modalAdd" persistent>
<q-card style="min-width: 30vw">
<q-form greedy @submit.prevent @validation-success="onSubmitAdd">
<DialogHeader tittle="เพิ่มหัวข้อความก้าวหน้า" :close="closeAdd" />
<q-separator />
<q-card-section>
<div class="row q-col-gutter-sm">
<div class="col-12">
<q-input
v-model="formDataAdd.topic"
outlined
class="inputgreen"
label="หัวข้อความก้าวหน้า"
dense
lazy-rules
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณากรอกหัวข้อความก้าวหน้า'}`,]"
/>
</div>
<div class="col-12">
<q-input
v-model="formDataAdd.reason"
outlined
class="inputgreen"
label="รายละเอียดความก้าวหน้า"
dense
lazy-rules
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณากรอกรายละเอียดความก้าวหน้า'}`,]"
type="textarea"
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn label="บันทึก" color="secondary" type="submit"
><q-tooltip>นทกขอม</q-tooltip></q-btn
>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<style scoped>
.border-right {
border-right: 1px solid #000;
}
.my-menu-link {
background: #ebf9f7 !important;
color: #1bb19ab8 !important;
}
</style>

View file

@ -0,0 +1,258 @@
<script setup lang="ts">
import DialogHeader from "@/components/DialogHeader.vue";
import { ref, reactive, watch } from "vue";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import { useRoute } from "vue-router";
const props = defineProps({
getAll: Function,
});
const route = useRoute();
const idKpi = ref<string>(route.params.id.toLocaleString());
const $q = useQuasar();
const mixin = useCounterMixin();
const {
showLoader,
hideLoader,
dialogConfirm,
messageError,
success,
dialogMessageNotify,
} = mixin;
const modal = defineModel<boolean>("modal", { required: true });
const id = defineModel<string>("id", { required: true });
const formData = reactive({
name: "",
group: [],
target: "",
isDevelopment70: false,
isDevelopment20: false,
isDevelopment10: false,
achievement10: "มีผลการพัฒนาและมีการดำเนินการตามเป้าหมายการนำไปพัฒนางาน",
achievement5: "มีผลการพัฒนาแต่ยังไม่ได้ดำเนินการตามเป้าหมายการนำไปพัฒนางาน",
achievement0: "ไม่ได้ดำเนินการพัฒนา",
});
function close() {
modal.value = false;
id.value = "";
formData.name = "";
formData.group = [];
formData.target = "";
formData.isDevelopment70 = false;
formData.isDevelopment20 = false;
formData.isDevelopment10 = false;
formData.achievement10 =
"มีผลการพัฒนาและมีการดำเนินการตามเป้าหมายการนำไปพัฒนางาน";
formData.achievement5 =
"มีผลการพัฒนาแต่ยังไม่ได้ดำเนินการตามเป้าหมายการนำไปพัฒนางาน";
formData.achievement0 = "ไม่ได้ดำเนินการพัฒนา";
props.getAll?.();
}
function onSubmit() {
if (
formData.isDevelopment70 == false &&
formData.isDevelopment20 == false &&
formData.isDevelopment10 == false
) {
dialogMessageNotify($q, "กรุณาเลือกวิธีการพัฒนา อย่างน้อย 1 ตัวเลือก");
} else {
dialogConfirm($q, () => {
const url = id.value
? config.API.kpiAchievementDevelop + `/${id.value}`
: config.API.kpiAchievementDevelop;
const body = {
name: formData.name,
target: formData.target,
achievement10: formData.achievement10,
achievement5: formData.achievement5,
achievement0: formData.achievement0,
isDevelopment70: formData.isDevelopment70,
isDevelopment20: formData.isDevelopment20,
isDevelopment10: formData.isDevelopment10,
kpiUserEvaluationId: idKpi.value,
};
showLoader();
http[id.value ? "put" : "post"](url, body)
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
close();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
});
}
}
watch(
() => id.value,
(i) => {
if (i) {
showLoader();
http
.get(config.API.kpiAchievementDevelop + `/${id.value}`)
.then((res) => {
const data = res.data.result;
formData.name = data.name;
formData.group = data.group;
formData.target = data.target;
formData.isDevelopment70 = data.isDevelopment70;
formData.isDevelopment20 = data.isDevelopment20;
formData.isDevelopment10 = data.isDevelopment10;
formData.achievement10 = data.achievement10;
formData.achievement5 = data.achievement5;
formData.achievement0 = data.achievement0;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
}
);
</script>
<template>
<q-dialog persistent v-model="modal">
<q-card style="width: 80%">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader
:tittle="`${id ? 'แก้ไข' : 'เพิ่ม'}การพัฒนาตนเอง`"
:close="close"
/>
<q-separator />
<q-card-section>
<div class="row q-col-gutter-sm">
<div class="col-12">
<q-input
v-model="formData.name"
outlined
dense
:rules="[(val:string) => !!val || `${'กรุณากรอกชื่อเรื่อง / เนื้อเรื่อง / หัวข้อการพัฒนา'}`,]"
lazy-rules
hide-bottom-space
class="inputgreen"
label="ชื่อเรื่อง / เนื้อเรื่อง / หัวข้อการพัฒนา"
>
</q-input>
</div>
<div class="col-12">
<span class="text-weight-medium">การพฒนา</span>
</div>
<div class="column">
<q-checkbox
v-model="formData.isDevelopment70"
label="70 การลงมือปฏิบัติ (โดยผู้บังคับบัญชามอบหมาย)"
/>
<q-checkbox
v-model="formData.isDevelopment20"
label="20 การเรียนรู้จากผู้อื่น (Coach/Mentor/Consulting)"
/>
<q-checkbox
v-model="formData.isDevelopment10"
label="10 การฝึกอบรมอื่นๆ"
/>
</div>
<div class="col-12">
<q-input
label="เป้าหมายการนำไปพัฒนางาน"
v-model="formData.target"
outlined
type="textarea"
dense
class="inputgreen"
:rules="[(val:string) => !!val || `${'กรุณากรอกเป้าหมายการนำไปพัฒนางาน'}`,]"
lazy-rules
hide-bottom-space
></q-input>
</div>
<div class="col-12">
<q-card bordered>
<div class="bg-grey-2 row q-py-sm text-weight-bold col-12">
<div class="col-4 text-center">
<span>ระดบคะแนน</span>
</div>
<div class="col-8 text-start">
<span>เกณฑการประเม</span>
</div>
</div>
<div class="row q-pa-sm">
<div class="col-4 text-center self-start text-body1">
<span>10</span>
</div>
<div class="col-8">
<q-input
class="inputgreen"
v-model="formData.achievement10"
outlined
dense
label="เกณฑ์การประเมิน"
:rules="[(val:string) => !!val || `${'กรุณากรอกเกณฑ์การประเมิน'}`,]"
lazy-rules
hide-bottom-space
></q-input>
</div>
</div>
<q-separator />
<div class="row q-pa-sm bg-grey-2">
<div class="col-4 text-center self-start text-body1">
<span>5</span>
</div>
<div class="col-8">
<q-input
class="inputgreen"
v-model="formData.achievement5"
outlined
dense
label="เกณฑ์การประเมิน"
:rules="[(val:string) => !!val || `${'กรุณากรอกเกณฑ์การประเมิน'}`,]"
lazy-rules
hide-bottom-space
></q-input>
</div>
</div>
<q-separator />
<div class="row q-pa-sm">
<div class="col-4 text-center self-start text-body1">
<span>0</span>
</div>
<div class="col-8">
<q-input
class="inputgreen"
v-model="formData.achievement0"
outlined
dense
label="เกณฑ์การประเมิน"
:rules="[(val:string) => !!val || `${'กรุณากรอกเกณฑ์การประเมิน'}`,]"
lazy-rules
hide-bottom-space
></q-input>
</div>
</div>
</q-card>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn label="บันทึก" color="secondary" type="submit"
><q-tooltip>นทกขอม</q-tooltip></q-btn
>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>

View file

@ -0,0 +1,111 @@
divdiv
<script setup lang="ts">
import { ref, watch } from "vue";
import { useQuasar } from "quasar";
import config from "@/app.config";
import http from "@/plugins/http";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
const $q = useQuasar();
const { showLoader, hideLoader, messageError } = useCounterMixin();
const modal = defineModel<boolean>("modal", { required: true });
const numpage = defineModel<number>("numpage", { required: true });
const kpiUserPlannedId = defineModel<string>("kpiUserPlannedId", {
required: true,
});
const dataAchievement = ref<any[]>([]);
function fetchByid(id: string, type: string) {
showLoader();
http
.get(config.API.kpiAchievement(`${type}`) + `/${id}`)
.then((res) => {
const data = res.data.result;
dataAchievement.value = [
{
level: "5",
val: data.achievement5,
},
{
level: "4",
val: data.achievement4,
},
{
level: "3",
val: data.achievement3,
},
{
level: "2",
val: data.achievement2,
},
{
level: "1",
val: data.achievement1,
},
];
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function closeDialog() {
modal.value = false;
}
watch(
() => modal.value,
() => {
if (modal.value) {
const type =
numpage.value === 1
? "planned"
: numpage.value === 2
? "role"
: "special";
fetchByid(kpiUserPlannedId.value, type);
}
}
);
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card class="col-12" style="width: 45%">
<DialogHeader :tittle="'คำอธิบายผลสำเร็จของงาน'" :close="closeDialog" />
<q-separator />
<q-card-section>
<q-card bordered>
<div class="bg-grey-2 q-pa-sm">
<div
class="row text-dark text-body2 text-weight-medium text-weight-bold"
>
<div class="text-center col-6">ระดบคะแนน</div>
<div class="text-center col-6">ผลสำเรจของงาน</div>
</div>
</div>
<q-separator />
<div v-for="(item, index) in dataAchievement" :key="item.id">
<div :class="`row q-pa-sm ${index % 2 !== 0 && 'bg-grey-2'}`">
<div class="col-6 text-center text-body1">{{ item.level }}</div>
<div class="col-6 text-center self-center">
<span>{{ item.val }}</span>
</div>
</div>
<q-separator />
</div>
</q-card>
</q-card-section>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -8,7 +8,7 @@ import DialogHeader from "@/components/DialogHeader.vue";
import type { QTableProps } from "quasar";
import { useKpiDataStore } from "@/modules/08_KPI/store";
import { useKpiDataStore } from "@/modules/14_KPI/store";
import { useCounterMixin } from "@/stores/mixin";
const $q = useQuasar();
@ -16,6 +16,10 @@ const store = useKpiDataStore();
const { showLoader, hideLoader, messageError, dialogConfirm, success } =
useCounterMixin();
const props = defineProps({
fetchList: { type: Function, required: true },
});
const modal = defineModel<boolean>("modal", { required: true });
const rows = defineModel<any>("data", { required: true });
const numpage = defineModel<number>("numpage", { required: true });
@ -126,9 +130,12 @@ function onSubmit() {
dialogConfirm($q, async () => {
try {
showLoader();
console.log(rows.value);
const formData = rows.value.map((e: any) => ({
id: e.id,
point: e.point,
summary: ((e.point / 5) * e.weight).toFixed(2),
}));
const url =
@ -139,7 +146,9 @@ function onSubmit() {
: numpage.value === 3
? config.API.kpiAchievementPoint("special")
: "";
await http.post(url, formData);
await http.post(url, formData).then((res) => {
props.fetchList();
});
success($q, "บันทึกข้อมูลสำเร็จ");
} catch (err) {
messageError($q, err);
@ -183,7 +192,29 @@ function onSubmit() {
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name === 'point'">
<q-rating
<q-btn-group outline>
<q-btn
v-for="i in 5"
:class="props.row.point == i && 'active'"
outline
color="grey-6"
:label="i"
@click="
() => {
updateAchievement(props.rowIndex, props.row);
props.row.point = i;
}
"
>
<q-tooltip>
<div class="text-body2">
<span v-html="props.row[`achievement${i}`]"></span>
</div>
</q-tooltip>
</q-btn>
</q-btn-group>
<!-- <q-rating
v-model="props.row.point"
max="5"
size="sm"
@ -209,7 +240,7 @@ function onSubmit() {
<template v-slot:tip-5>
<q-tooltip>{{ props.row.achievement5 }}</q-tooltip>
</template>
</q-rating>
</q-rating> -->
</div>
<div v-else-if="col.name === 'achievement'">
{{ props.row.point ? `ระดับ ${props.row.point}` : "" }}
@ -283,4 +314,19 @@ function onSubmit() {
top: 0;
}
}
.q-btn-group--outline > .q-btn-item:not(:last-child):before {
border-right: 1px solid #c4c4c4;
}
.q-btn-group--outline > .q-btn-item.active {
color: #2196f3 !important;
background-color: #cde6fb !important;
}
.q-btn-group--outline > .q-btn-item + .q-btn-item.active:before {
border-left: 1px solid #2196f3 !important;
background-color: #cde6fb;
}
.q-btn-group--outline > .q-btn-item.active:not(:last-child):before {
border: 1px solid #2196f3;
background-color: #cde6fb;
}
</style>

View file

@ -3,18 +3,18 @@ import { ref, watch, computed } from "vue";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
import { useKpiDataStore } from "@/modules/14_KPI/store";
import { useQuasar, type QTableProps } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
// import type { ListCriteria } from "@/modules/08_KPI/interface/request/index";
import type { ListCriteria } from "@/modules/14_KPI/interface/request/index";
const dataListCriteria = defineModel<any[]>("dataListCriteria", {
const dataListCriteria = defineModel<ListCriteria[]>("dataListCriteria", {
required: true,
});
import { useKPIDataStore } from "@/modules/14_KPI/store/KPIStore";
const sortedDataListCriteria = computed(() => {
return dataListCriteria.value.sort((a, b) => a.level - b.level);
});
@ -31,7 +31,7 @@ const {
success,
dialogMessageNotify,
} = useCounterMixin();
const store = useKPIDataStore();
const store = useKpiDataStore();
const $q = useQuasar();
const modal = defineModel<boolean>("modal", { required: true });
const rows = defineModel<any>("data", { required: true });
@ -108,28 +108,30 @@ function closeDialog() {
}
function onSubmit() {
// const main = rows.value;
// const checkPoint = main.some((item: any) => item.point === 0);
// if (checkPoint) {
// dialogMessageNotify($q, "");
// } else {
// dialogConfirm($q, () => {
// const data = rows.value;
// const body = data.map((i: any) => ({
// id: i.id,
// point: i.point,
// }));
// http
// .post(config.API.kpiUserCapacity + `/point`, body)
// .then((res) => {
// closeDialog();
// })
// .catch((e) => {
// messageError($q, e);
// })
// .finally(() => {});
// });
// }
const main = rows.value;
const checkPoint = main.some((item: any) => item.point === 0);
if (checkPoint) {
dialogMessageNotify($q, "กรุณาเลือกระดับคะแนนตามเกณฑ์การประเมิน");
} else {
dialogConfirm($q, () => {
const data = rows.value;
const body = data.map((i: any) => ({
id: i.id,
point: i.point,
summary: i.point * 20,
}));
http
.post(config.API.kpiUserCapacity + `/point`, body)
.then((res) => {
closeDialog();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {});
});
}
}
</script>
@ -170,8 +172,26 @@ function onSubmit() {
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name === 'point'">
<div v-if="type == 'HEAD' || type == 'GROUP'">
<q-rating
<div>
<q-btn-group outline>
<q-btn
v-for="(i, index) in sortedDataListCriteria"
:key="index"
:class="props.row.point == i.level && 'active'"
outline
color="grey-6"
:label="i.level"
@click="props.row.point = i.level"
>
<q-tooltip>
<div class="text-body2">
<span v-html="i.description"></span>
</div>
</q-tooltip>
</q-btn>
</q-btn-group>
<!-- <q-rating
v-model="props.row.point"
max="5"
size="sm"
@ -190,9 +210,8 @@ function onSubmit() {
</div>
</q-tooltip>
</template>
</q-rating>
</q-rating> -->
</div>
<div v-else>รอ ทำ select</div>
</div>
<div v-else-if="col.name === 'summary'">
{{ props.row.point !== 0 ? props.row.point * 20 : "-" }}
@ -259,4 +278,19 @@ function onSubmit() {
top: 0;
}
}
.q-btn-group--outline > .q-btn-item:not(:last-child):before {
border-right: 1px solid #c4c4c4;
}
.q-btn-group--outline > .q-btn-item.active {
color: #2196f3 !important;
background-color: #cde6fb !important;
}
.q-btn-group--outline > .q-btn-item + .q-btn-item.active:before {
border-left: 1px solid #2196f3 !important;
background-color: #cde6fb;
}
.q-btn-group--outline > .q-btn-item.active:not(:last-child):before {
border: 1px solid #2196f3;
background-color: #cde6fb;
}
</style>

View file

@ -0,0 +1,277 @@
<script setup lang="ts">
import DialogHeader from "@/components/DialogHeader.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar, type QTableProps } from "quasar";
import { ref } from "vue";
const props = defineProps({
getAll: Function,
});
const $q = useQuasar();
const mixin = useCounterMixin();
const {
dialogConfirm,
showLoader,
hideLoader,
success,
messageError,
dialogMessageNotify,
} = mixin;
const modal = defineModel<boolean>("modal", { required: true });
const rows = defineModel<any[]>("data", { required: true });
const summary = ref<number | null>(null);
const columns = ref<QTableProps["columns"]>([
{
name: "name",
align: "left",
label: "ชื่อเรื่อง / เนื้อเรื่อง / หัวข้อการพัฒนา",
sortable: true,
field: "name",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "develop",
align: "left",
label: "วิธีการพัฒนา",
sortable: true,
field: "develop",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "target",
align: "left",
label: "เป้าหมายการนำไปพัฒนางาน",
sortable: true,
field: "target",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "achievement",
align: "left",
label: "เกณฑ์การประเมินผลการพัฒนา",
sortable: true,
field: "achievement",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "summary",
align: "left",
label: "ผลการประเมิน",
sortable: true,
field: "summary",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
function onSubmit() {
const data = rows.value.map((i: any) => ({
id: i.id,
point: i.summary,
summary: i.summary,
}));
showLoader();
http
.post(config.API.kpiAchievementDevelop + `/point`, data)
.then((res) => {
success($q, "บันทึกข้อมลสำเร็จ");
close();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
function close() {
modal.value = false;
rows.value = [];
props.getAll?.();
}
</script>
<template>
<q-dialog v-model="modal" persistent style="width: 60vw">
<q-card style="width: 1200px; max-width: 80vw">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader tittle="ประเมิน" :close="close" />
<q-separator />
<q-card-section style="min-height: 70vh">
<div class="col-12 q-pa-sm">
<q-table
ref="table"
:columns="columns"
:rows="rows"
row-key="id"
flat
bordered
dense
hide-pagination
class="custom-table2"
:rows-per-page-options="[20]"
no-data-label="ไม่มีข้อมูล"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
>
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td
v-for="col in props.cols"
:key="col.id"
class="vertical-top"
>
<div v-if="col.name == 'develop'">
<div class="column">
<q-checkbox
size="xs"
:model-value="props.row.isDevelopment70"
label="70 การลงมือปฏิบัติ (โดยผู้บังคับบัญชามอบหมาย)"
/>
<q-checkbox
size="xs"
:model-value="props.row.isDevelopment20"
label="20 การเรียนรู้จากผู้อื่น (Coach/Mentor/Consulting)"
/>
<q-checkbox
size="xs"
:model-value="props.row.isDevelopment10"
label="10 การฝึกอบรมอื่นๆ"
/>
</div>
</div>
<div v-else-if="col.name === 'summary'">
{{ props.row.summary ? props.row.summary : 0 }}
</div>
<div v-else-if="col.name == 'achievement'">
<q-btn-group outline>
<q-btn
outline
color="grey-6"
label="0"
:class="props.row.summary == 0 && 'active'"
@click="props.row.summary = 0"
><q-tooltip>{{
props.row.achievement0
}}</q-tooltip></q-btn
>
<q-btn
outline
color="grey-6"
label="5"
:class="props.row.summary == 5 && 'active'"
@click="props.row.summary = 5"
><q-tooltip>{{
props.row.achievement5
}}</q-tooltip></q-btn
>
<q-btn
outline
color="grey-6"
label="10"
:class="props.row.summary == 10 && 'active'"
@click="props.row.summary = 10"
><q-tooltip>{{
props.row.achievement10
}}</q-tooltip></q-btn
>
</q-btn-group>
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</q-table>
</div>
</q-card-section>
<q-card-actions align="right" class="bg-white text-teal">
<q-btn label="บันทึก" color="secondary" type="submit"
><q-tooltip>นทกขอม</q-tooltip></q-btn
>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<style scoped>
.custom-table2 {
max-height: 64vh;
.q-table tr:nth-child(odd) td {
background: white;
}
.q-table tr:nth-child(even) td {
background: #f8f8f8;
}
.q-table thead tr {
background: #ecebeb;
}
.q-table thead tr th {
position: sticky;
}
.q-table td:nth-of-type(2) {
z-index: 3 !important;
}
.q-table th:nth-of-type(2),
.q-table td:nth-of-type(2) {
position: sticky;
left: 0;
z-index: 1;
}
/* this will be the loading indicator */
.q-table thead tr:last-child th {
/* height of all previous header rows */
top: 48px;
}
.q-table thead tr:first-child th {
top: 0;
}
}
.q-btn-group--outline > .q-btn-item:not(:last-child):before {
border-right: 1px solid #c4c4c4;
}
.q-btn-group--outline > .q-btn-item.active {
color: #2196f3 !important;
background-color: #fff;
}
.q-btn-group--outline > .q-btn-item.active:not(:last-child):before {
border: 1px solid #2196f3;
}
</style>

View file

@ -0,0 +1,107 @@
<script setup lang="ts">
import { ref } from "vue";
import { useKpiDataStore } from "@/modules/14_KPI/store";
import { useRoute } from "vue-router";
import Assessment from "@/modules/14_KPI/components/Tab/01_Assessment.vue";
import Evaluator from "@/modules/14_KPI/components/Tab/02_Evaluator.vue";
import CommanderAbove from "@/modules/14_KPI/components/Tab/03_CommanderAbove.vue";
import CommanderAboveOneStep from "@/modules/14_KPI/components/Tab/04_CommanderAboveOneStep.vue";
import File from "@/modules/14_KPI/components/Tab/05_File.vue";
const store = useKpiDataStore();
const route = useRoute();
const itemsTab = ref<any>([
{
name: "1",
label: "จัดทำแบบฟอร์มการประเมิน",
},
{
name: "2",
label: "รายงานความก้าวหน้า",
},
{
name: "3",
label: "รายงานผลสำเร็จของงาน",
},
{
name: "5",
label: "ไฟล์เอกสาร",
},
]);
const splitterModel = ref<number>(12);
</script>
<template>
<q-splitter v-model="splitterModel" disable>
<template v-slot:before>
<q-tabs
v-model="store.tabMain"
vertical
class="text-grey-7 text-weight-light"
active-class="bg-blue-1 text-blue-8 text-weight-bold"
>
<!-- <q-tab
class="hover-tab"
v-for="(tab, index) in itemsTab"
:key="index"
:name="tab.name"
:icon="tab.icon"
:label="tab.label"
/> -->
<q-tab name="1" label="จัดทำข้อตกลง" />
<q-tab
name="2"
label="รายงานความก้าวหน้า"
:disable="store.tabOpen < 2"
/>
<q-tab
name="3"
label="รายงานผลสำเร็จของงาน"
:disable="store.tabOpen < 3"
/>
<!-- <q-tab name="3" label="ผู้บังคับบัญชา">
<div class="text-caption">เหนอขนไป</div>
</q-tab>
<q-tab name="4" label="ผู้บังคับบัญชา">
<div class="text-caption">เหนอขนไปอกชนหน</div>
</q-tab> -->
<q-tab name="5" label="ไฟล์เอกสาร" />
</q-tabs>
</template>
<template v-slot:after>
<q-tab-panels
v-model="store.tabMain"
animated
swipeable
vertical
transition-prev="jump-up"
transition-next="jump-up"
>
<q-tab-panel
v-for="(tab, index) in itemsTab"
:key="index"
:name="tab.name"
class="q-pa-none"
>
<Assessment v-if="store.tabMain === '1'" />
<Assessment v-if="store.tabMain === '2'" :type="'evaluator'" />
<Assessment v-if="store.tabMain === '3'" :type="'commander'" />
<Assessment v-if="store.tabMain === '4'" :type="'commanderHigh'" />
<File v-if="store.tabMain === '5'" />
</q-tab-panel>
</q-tab-panels>
</template>
</q-splitter>
</template>
<style scoped>
.hover-tab:hover {
background-color: #0793f1;
color: white !important;
opacity: 1 !important;
}
</style>

View file

@ -7,15 +7,18 @@ import http from "@/plugins/http";
import type { QTableProps } from "quasar";
// import Dialog from "@/modules/14_KPI/components/detailList/Dialog/01_FormIndicator.vue";
// import Dialog03 from "@/modules/14_KPI/components/detailList/Dialog/03_FormIndicatorSpecial.vue";
// import DialogEvaluate from "@/modules/14_KPI/components/detailList/DialogEvaluate/01_Indicator.vue";
import Dialog from "@/modules/14_KPI/components/Tab/Dialog/01_FormIndicator.vue";
// import Dialog03 from "@/modules/14_KPI/components/Tab/Dialog/03_FormIndicatorSpecial.vue";
import DialogEvaluate from "@/modules/14_KPI/components/Tab/DialogEvaluate/01_Indicator.vue";
import DialogViewInfo from "@/modules/14_KPI/components/Tab/Dialog/DialogViewInfo.vue";
import DialogProgress from "@/modules/14_KPI/components/Tab/Dialog/DialogCommentProgress.vue";
import DialogProblem from "@/modules/14_KPI/components/Tab/Dialog/DialogCommentProblem.vue";
import { useCounterMixin } from "@/stores/mixin";
import { useKPIDataStore } from "@/modules/14_KPI/store/KPIStore";
import { useKpiDataStore } from "@/modules/14_KPI/store";
const $q = useQuasar();
const store = useKPIDataStore();
const store = useKpiDataStore();
const route = useRoute();
const {
date2Thai,
@ -26,24 +29,26 @@ const {
success,
} = useCounterMixin();
const isReadonly = <boolean>(route.name === "KPIDetail" ? true : false);
const title = defineModel<string>("title", { required: true });
const rows = defineModel<any>("data", { required: true });
const numpage = defineModel<number>("page", { required: true });
const evaluationTotal = defineModel<number>("total", { required: true });
const props = defineProps({
fetchList: { type: Function, required: true },
});
const visibleColumns = ref<string[]>([
"includingName",
"target",
"point",
"weight",
"achievement",
"evaluationResults",
]);
const visibleColumns = ref<string[]>(
store.tabOpen === 3 && store.tabMain === "3"
? [
"includingName",
"target",
"point",
"weight",
"achievement",
"evaluationResults",
]
: ["includingName", "target", "weight"]
);
const columns = ref<QTableProps["columns"]>([
{
name: "includingName",
@ -119,42 +124,67 @@ const modal = ref<boolean>(false);
const modalAssigned = ref<boolean>(false);
const isStatusEdit = ref<boolean>(false);
const modalEvaluate = ref<boolean>(false);
const modalViewInfo = ref<boolean>(false);
function onAdd(edit: boolean = false, id: string = "") {
isStatusEdit.value = edit;
kpiUserPlannedId.value = id;
if (numpage.value !== 3) {
modal.value = true;
} else if (numpage.value == 3) {
modalAssigned.value = true;
}
// if (numpage.value !== 3) {
modal.value = true;
// } else if (numpage.value == 3) {
// modalAssigned.value = true;
// }
}
function onEvaluate() {
function onClickView(id: string) {
kpiUserPlannedId.value = id;
modalViewInfo.value = true;
}
async function onEvaluate() {
modalEvaluate.value = true;
}
function onDelete(id: string) {
// dialogRemove($q, async () => {
// try {
// showLoader();
// const url =
// numpage.value === 1
// ? config.API.kpiAchievement("planned") + `/${id}`
// : numpage.value === 2
// ? config.API.kpiAchievement("role") + `/${id}`
// : numpage.value === 3
// ? config.API.kpiAchievement("special") + `/${id}`
// : "";
// await http.delete(url);
// success($q, "");
// props.fetchList?.();
// } catch (err) {
// messageError($q, err);
// } finally {
// hideLoader();
// }
// });
dialogRemove($q, async () => {
try {
showLoader();
const url =
numpage.value === 1
? config.API.kpiAchievement("planned") + `/${id}`
: numpage.value === 2
? config.API.kpiAchievement("role") + `/${id}`
: numpage.value === 3
? config.API.kpiAchievement("special") + `/${id}`
: "";
await http.delete(url);
success($q, "ลบข้อมูลสำเร็จ");
props.fetchList?.();
} catch (err) {
messageError($q, err);
} finally {
hideLoader();
}
});
}
const modalProgress = ref<boolean>(false);
const modalProblem = ref<boolean>(false);
const type = ref<string>("");
const idList = ref<string>("");
function openPopupProgress(id: string) {
modalProgress.value = true;
type.value =
numpage.value === 1 ? "plan" : numpage.value === 2 ? "role" : "special";
idList.value = id;
}
function openPopupProblem(id: string) {
modalProblem.value = true;
type.value =
numpage.value === 1 ? "plan" : numpage.value === 2 ? "role" : "special";
idList.value = id;
}
watch(
@ -166,24 +196,47 @@ watch(
}
);
watch(
() => modalAssigned.value,
() => {
if (!modalAssigned.value) {
props.fetchList?.();
}
}
);
const isEditStep1 = computed(() => {
return (
(store.dataEvaluation.evaluationStatus === "NEW" &&
store.rolePerson === "USER" &&
store.tabMain === "1") ||
(store.dataEvaluation.evaluationStatus === "NEW_EVALUATOR" &&
store.rolePerson === "EVALUATOR" &&
store.tabMain === "1")
);
});
watch(
() => modalEvaluate.value,
() => {
if (!modalEvaluate.value) {
props.fetchList?.();
}
}
);
const isEditStep3 = computed(() => {
return (
(store.dataEvaluation.evaluationStatus === "EVALUATING" &&
store.rolePerson === "USER" &&
store.tabMain === "3") ||
(store.dataEvaluation.evaluationStatus === "EVALUATING_EVALUATOR" &&
store.rolePerson === "EVALUATOR" &&
store.tabMain === "3")
);
});
// watch(
// () => modalAssigned.value,
// () => {
// if (!modalAssigned.value) {
// props.fetchList?.();
// }
// }
// );
// watch(
// () => modalEvaluate.value,
// () => {
// if (!modalEvaluate.value) {
// props.fetchList?.();
// }
// }
// );
</script>
<template>
<q-card bordered style="border-radius: 5px" class="no-shadow">
<q-card-section class="bg-grey-2 q-py-sm">
@ -191,7 +244,7 @@ watch(
<div class="col">
<span class="text-weight-medium">{{ title }}</span>
<q-btn
v-if="!isReadonly"
v-if="isEditStep1"
class="q-ml-xs"
flat
round
@ -206,7 +259,7 @@ watch(
</div>
<div class="col-auto">
<q-btn
v-if="!isReadonly"
v-if="isEditStep3"
flat
round
icon="mdi-clipboard-check-outline"
@ -239,17 +292,46 @@ watch(
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width />
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
<q-th auto-width v-if="!isReadonly" />
<q-th auto-width />
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td>
<q-btn
flat
round
icon="info"
color="info"
size="12px"
dense
@click="onClickView(props.row.id)"
>
<q-tooltip>คำอธบายผลสำเรจของงาน</q-tooltip>
</q-btn></q-td
>
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name === 'point'">
<q-rating
<q-btn-group outline>
<q-btn
v-for="i in 5"
:class="props.row.point == i && 'active'"
outline
color="grey-6"
:label="i"
>
<q-tooltip>
<div class="text-body2">
<span v-html="props.row[`achievement${i}`]"></span>
</div>
</q-tooltip>
</q-btn>
</q-btn-group>
<!-- <q-rating
v-model="props.row.point"
max="5"
size="sm"
@ -273,7 +355,7 @@ watch(
<template v-slot:tip-5>
<q-tooltip>{{ props.row.achievement5 }}</q-tooltip>
</template>
</q-rating>
</q-rating> -->
</div>
<div v-else-if="col.name === 'achievement'">
{{ props.row.point ? `ระดับ ${props.row.point}` : "" }}
@ -289,46 +371,67 @@ watch(
{{ col.value ? col.value : "-" }}
</div>
</q-td>
<td v-if="!isReadonly">
<q-btn
flat
round
icon="edit"
color="edit"
size="12px"
dense
@click="onAdd(true, props.row.id)"
<td>
<div
v-if="
store.dataEvaluation.evaluationStatus == 'APPROVE' &&
store.tabMain === '2'
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
flat
round
icon="mdi-developer-board"
color="blue-6"
size="12px"
dense
@click="openPopupProgress(props.row.id)"
>
<q-tooltip>รายงานความกาวหน</q-tooltip>
</q-btn>
<q-btn
flat
round
icon="warning"
color="red-5"
size="12px"
dense
main="problem"
@click="openPopupProblem(props.row.id)"
>
<q-tooltip>รายงานปญหา</q-tooltip>
</q-btn>
</div>
<q-btn
flat
round
icon="delete"
color="red"
size="12px"
dense
@click="onDelete(props.row.id)"
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
<div v-if="isEditStep1">
<q-btn
flat
round
icon="edit"
color="edit"
size="12px"
dense
@click="onAdd(true, props.row.id)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
flat
round
icon="delete"
color="red"
size="12px"
dense
@click="onDelete(props.row.id)"
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</div>
</td>
</q-tr>
</template>
<template v-slot:bottom>
<span class="text-body2 text-weight-bold"
>รวมผลการประเม (อยละ)</span
>
<div class="text-primary q-pl-md">{{ evaluationTotal }}</div>
</template>
</q-table>
<!-- <div class="row text-body2 text-weight-bold q-pa-md">
<div class="col-12 text-center row justify-center">
<span>รวมผลการประเม (อยละ)</span>
<div class="text-primary q-pl-md">{{ evaluationTotal }}</div>
</div>
</div> -->
</q-card-section>
</q-card>
@ -338,19 +441,40 @@ watch(
:isStatusEdit="isStatusEdit"
:kpiUserPlannedId="kpiUserPlannedId"
/>
<Dialog03
<!-- <Dialog03
v-model:modal="modalAssigned"
:numpage="numpage"
:isStatusEdit="isStatusEdit"
:kpiUserPlannedId="kpiUserPlannedId"
/>
/> -->
<DialogEvaluate
v-model:modal="modalEvaluate"
:data="rows"
:numpage="numpage"
:fetchList="fetchList"
/>
<DialogViewInfo
v-model:modal="modalViewInfo"
:numpage="numpage"
:isStatusEdit="isStatusEdit"
:kpiUserPlannedId="kpiUserPlannedId"
/>
<DialogProgress
v-model:modal="modalProgress"
v-model:type="type"
:idList="idList"
/>
<DialogProblem
v-model:modal="modalProblem"
v-model:type="type"
:idList="idList"
/>
</template>
<style scoped>
.custom-table2 {
max-height: 64vh;
@ -392,4 +516,19 @@ watch(
top: 0;
}
}
.q-btn-group--outline > .q-btn-item:not(:last-child):before {
border-right: 1px solid #c4c4c4;
}
.q-btn-group--outline > .q-btn-item.active {
color: #2196f3 !important;
background-color: #cde6fb !important;
}
.q-btn-group--outline > .q-btn-item + .q-btn-item.active:before {
border-left: 1px solid #2196f3 !important;
background-color: #cde6fb;
}
.q-btn-group--outline > .q-btn-item.active:not(:last-child):before {
border: 1px solid #2196f3;
background-color: #cde6fb;
}
</style>

View file

@ -1,39 +1,36 @@
<script setup lang="ts">
import { onMounted, ref, computed } from "vue";
// import Dialog from "@/modules/14_KPI/components/detailList/Dialog/04_FormCompetency.vue";
// import DialogEvaluate from "@/modules/14_KPI/components/detailList/DialogEvaluate/02_Competenct.vue";
import { onMounted, ref, computed, watch } from "vue";
import Dialog from "@/modules/14_KPI/components/Tab/Dialog/04_FormCompetency.vue";
import DialogEvaluate from "@/modules/14_KPI/components/Tab/DialogEvaluate/02_Competenct.vue";
import DialogProgress from "@/modules/14_KPI/components/Tab/Dialog/DialogCommentProgress.vue";
import DialogProblem from "@/modules/14_KPI/components/Tab/Dialog/DialogCommentProblem.vue";
import { useQuasar, type QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useKPIDataStore } from "@/modules/14_KPI/store/KPIStore";
import { useKpiDataStore } from "@/modules/14_KPI/store";
import http from "@/plugins/http";
import config from "@/app.config";
import { useRoute } from "vue-router";
// import type {
// FormCapacityList,
// ListCriteria,
// } from "@/modules/08_KPI/interface/request/index";
import type {
FormCapacityList,
ListCriteria,
} from "@/modules/14_KPI/interface/request/index";
const dataListCriteria = defineModel<any[]>("dataListCriteria", {
const dataListCriteria = defineModel<ListCriteria[]>("dataListCriteria", {
required: true,
});
const competencyScore = defineModel<number>("competencyScore", {
default: 0,
});
const sortedDataListCriteria = computed(() => {
return dataListCriteria.value.sort((a, b) => a.level - b.level);
});
const modalEvaluate = ref<boolean>(false);
const store = useKPIDataStore();
const store = useKpiDataStore();
const route = useRoute();
const id = ref<string>(route.params.id as string);
const isReadonly = <boolean>(route.name === "KPIDetail" ? true : false);
const isReadonly = <boolean>(route.name === "KPIEditEvaluator" ? true : false);
const idCapacity = ref<string | null>(null);
@ -108,6 +105,12 @@ const columns = ref<QTableProps["columns"]>([
},
]);
const visibleColumns = ref<string[]>(
store.tabOpen === 3 && store.tabMain === "3"
? ["name", "level", "point", "weight", "summary"]
: ["name", "level", "weight"]
);
const typeCompetency = ref<string>("");
function onAdd(type: string) {
typeCompetency.value = type;
@ -116,7 +119,8 @@ function onAdd(type: string) {
const rows = ref<any>([]);
const lists = ref<any>([]);
const resultEvaluation = ref<string | 0>(0);
// const resultEvaluation = ref<string | 0>(0);
function getData(type: string) {
http
.get(config.API.kpiUserCapacity + `?id=${id.value}&type=${type}`)
@ -136,9 +140,11 @@ function getData(type: string) {
let total = 0;
for (let index = 0; index < store.competencyType.length; index++) {
const element = await store.competencyType[index];
const dataArr = await lists.value.find(
(x: any) => x.type == element.id
);
if (dataArr) {
result += dataArr.data.reduce(
(sum: number, e: any) => sum + (e.point / 5) * e.weight,
@ -151,41 +157,48 @@ function getData(type: string) {
total++;
}
}
if (total > 0) {
let weightAvg = weight / total;
let resultAvg = result / total;
let sum =
weightAvg != 0
? (resultAvg / weightAvg) * store.dataEvaluation.capacityPoint
: 0;
resultEvaluation.value = sum.toFixed(2);
competencyScore.value = sum;
if (store.dataEvaluation.posExecutiveName != null) {
store.competencyScoreVal =
weightAvg != 0
? (resultAvg / weightAvg) * store.excusiveCompetencyScore
: 0;
} else {
store.competencyScoreVal =
weightAvg != 0
? (resultAvg / weightAvg) * store.competencyScore
: 0;
}
}
});
}
function onEdit(data: any, type: string) {
function onEdit(data: FormCapacityList, type: string) {
idCapacity.value = data.id;
typeCompetency.value = type;
modal.value = true;
}
function onDelete(id: string, type: string) {
// dialogRemove($q, () => {
// showLoader();
// http
// .delete(config.API.kpiUserCapacity + `/${id}`)
// .then((res) => {
// success($q, "");
// getData(type);
// })
// .catch((e) => {
// messageError($q, e);
// })
// .finally(() => {
// hideLoader();
// });
// });
dialogRemove($q, () => {
showLoader();
http
.delete(config.API.kpiUserCapacity + `/${id}`)
.then((res) => {
success($q, "ลบข้อมูลสำเร็จ");
getData(type);
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
});
}
function onEvaluate(type: string) {
@ -193,13 +206,61 @@ function onEvaluate(type: string) {
modalEvaluate.value = true;
}
onMounted(() => {
setTimeout(() => {
for (let index = 0; index < store.competencyType.length; index++) {
const element = store.competencyType[index];
getData(element.id);
const modalProgress = ref<boolean>(false);
const modalProblem = ref<boolean>(false);
const type = ref<string>("");
const idList = ref<string>("");
function openPopupProgress(id: string) {
modalProgress.value = true;
type.value = "capacity";
idList.value = id;
}
function openPopupProblem(id: string) {
modalProblem.value = true;
type.value = "capacity";
idList.value = id;
}
const isEditStep1 = computed(() => {
return (
(store.dataEvaluation.evaluationStatus === "NEW" &&
store.rolePerson === "USER" &&
store.tabMain === "1") ||
(store.dataEvaluation.evaluationStatus === "NEW_EVALUATOR" &&
store.rolePerson === "EVALUATOR" &&
store.tabMain === "1")
);
});
const isEditStep3 = computed(() => {
return (
(store.dataEvaluation.evaluationStatus === "EVALUATING" &&
store.rolePerson === "USER" &&
store.tabMain === "3") ||
(store.dataEvaluation.evaluationStatus === "EVALUATING_EVALUATOR" &&
store.rolePerson === "EVALUATOR" &&
store.tabMain === "3")
);
});
watch(
() => store.dataEvaluation.capacityPoint,
(newValue, oldValue) => {
if (newValue !== oldValue) {
for (let index = 0; index < store.competencyType.length; index++) {
const element = store.competencyType[index];
getData(element.id);
}
}
}, 1000);
}
);
onMounted(() => {
for (let index = 0; index < store.competencyType.length; index++) {
const element = store.competencyType[index];
getData(element.id);
}
});
</script>
@ -211,7 +272,7 @@ onMounted(() => {
<div class="col">
<span class="text-weight-medium">{{ item.name }}</span>
<q-btn
v-if="!isReadonly"
v-if="isEditStep1"
class="q-ml-xs"
flat
round
@ -227,7 +288,7 @@ onMounted(() => {
<q-space />
<q-btn
v-if="!isReadonly"
v-if="isEditStep3"
flat
round
icon="mdi-clipboard-check-outline"
@ -253,13 +314,14 @@ onMounted(() => {
hide-pagination
class="custom-table2"
no-data-label="ไม่มีข้อมูล"
:visible-columns="visibleColumns"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
<q-th auto-width v-if="!isReadonly" />
<q-th auto-width />
</q-tr>
</template>
<template v-slot:body="props">
@ -270,7 +332,23 @@ onMounted(() => {
</div>
<div v-else-if="col.name == 'point'">
<div>
<q-rating
<q-btn-group outline>
<q-btn
v-for="(i, index) in sortedDataListCriteria"
:key="index"
:class="props.row.point == i.level && 'active'"
outline
color="grey-6"
:label="i.level"
>
<q-tooltip>
<div class="text-body2">
<span v-html="i.description"></span>
</div>
</q-tooltip>
</q-btn>
</q-btn-group>
<!-- <q-rating
v-model="props.row.point"
max="5"
size="sm"
@ -290,7 +368,7 @@ onMounted(() => {
</div>
</q-tooltip>
</template>
</q-rating>
</q-rating> -->
</div>
<!-- <div v-else>รอ ทำ select</div> -->
</div>
@ -305,25 +383,58 @@ onMounted(() => {
{{ col.value }}
</div>
</q-td>
<q-td v-if="!isReadonly">
<q-btn
flat
round
icon="edit"
color="edit"
@click.stop.pervent="onEdit(props.row, item.id)"
<q-td>
<div
v-if="
store.dataEvaluation.evaluationStatus == 'APPROVE' &&
store.tabMain === '2'
"
>
<q-tooltip>แกไข </q-tooltip>
</q-btn>
<q-btn
flat
round
icon="delete"
color="red"
@click.stop.pervent="onDelete(props.row.id, item.id)"
>
<q-tooltip>ลบขอม </q-tooltip>
</q-btn>
<q-btn
flat
round
icon="mdi-developer-board"
color="blue-6"
size="12px"
dense
@click="openPopupProgress(props.row.id)"
>
<q-tooltip>รายงานความกาวหน</q-tooltip>
</q-btn>
<q-btn
flat
round
icon="warning"
color="red-5"
size="12px"
dense
main="problem"
@click="openPopupProblem(props.row.id)"
>
<q-tooltip>รายงานปญหา</q-tooltip>
</q-btn>
</div>
<div v-if="isEditStep1">
<q-btn
flat
round
icon="edit"
color="edit"
@click.stop.pervent="onEdit(props.row, item.id)"
>
<q-tooltip>แกไข </q-tooltip>
</q-btn>
<q-btn
flat
round
icon="delete"
color="red"
@click.stop.pervent="onDelete(props.row.id, item.id)"
>
<q-tooltip>ลบขอม </q-tooltip>
</q-btn>
</div>
</q-td>
</q-tr>
</template>
@ -347,13 +458,16 @@ onMounted(() => {
:get-data="getData"
/>
<div class="row text-body2 text-weight-bold justify-center">
<span
>สรปผลการประเมนสมรรถนะ (คะแนนเต
{{ store.dataEvaluation.capacityPoint }} คะแนน)</span
>
<div class="text-primary q-pl-md">{{ resultEvaluation }}</div>
</div>
<DialogProgress
v-model:modal="modalProgress"
v-model:type="type"
:idList="idList"
/>
<DialogProblem
v-model:modal="modalProblem"
v-model:type="type"
:idList="idList"
/>
</template>
<style scoped>
@ -397,4 +511,19 @@ onMounted(() => {
top: 0;
}
}
.q-btn-group--outline > .q-btn-item:not(:last-child):before {
border-right: 1px solid #c4c4c4;
}
.q-btn-group--outline > .q-btn-item.active {
color: #2196f3 !important;
background-color: #cde6fb !important;
}
.q-btn-group--outline > .q-btn-item + .q-btn-item.active:before {
border-left: 1px solid #2196f3 !important;
background-color: #cde6fb;
}
.q-btn-group--outline > .q-btn-item.active:not(:last-child):before {
border: 1px solid #2196f3;
background-color: #cde6fb;
}
</style>

View file

@ -0,0 +1,449 @@
<script setup lang="ts">
import { onMounted, ref, computed, watch, reactive } from "vue";
import DialogDevelop from "@/modules/14_KPI/components/Tab/Dialog/DialogDevelop.vue";
import DialogEvalutionDevelop from "@/modules/14_KPI/components/Tab/DialogEvaluate/03_DialogEvalutionDevelop.vue";
import DialogProgress from "@/modules/14_KPI/components/Tab/Dialog/DialogCommentProgress.vue";
import DialogProblem from "@/modules/14_KPI/components/Tab/Dialog/DialogCommentProblem.vue";
import { useQuasar, type QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useKpiDataStore } from "@/modules/14_KPI/store";
import http from "@/plugins/http";
import config from "@/app.config";
import { useRoute } from "vue-router";
const store = useKpiDataStore();
const route = useRoute();
const evaluationId = ref<string>(route.params.id.toString());
const modalEvaluate = ref<boolean>(false);
const rows = ref<any[]>([]);
const modalDevelop = ref<boolean>(false);
const idEditDevelop = ref<string>("");
const $q = useQuasar();
const mixin = useCounterMixin();
const {
date2Thai,
messageError,
showLoader,
hideLoader,
dialogRemove,
success,
} = mixin;
const formData = reactive({
isDevelopment70: false,
isDevelopment20: false,
isDevelopment10: false,
});
const columns = ref<QTableProps["columns"]>([
{
name: "name",
align: "left",
label: "ชื่อเรื่อง / เนื้อเรื่อง / หัวข้อการพัฒนา",
sortable: true,
field: "name",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "develop",
align: "left",
label: "วิธีการพัฒนา",
sortable: true,
field: "develop",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "target",
align: "left",
label: "เป้าหมายการนำไปพัฒนางาน",
sortable: true,
field: "target",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "achievement",
align: "left",
label: "เกณฑ์การประเมินผลการพัฒนา",
sortable: true,
field: "achievement",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "summary",
align: "left",
label: "ผลการประเมิน",
sortable: true,
field: "summary",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumns = ref<string[]>(
store.tabOpen === 3 && store.tabMain === "3"
? ["name", "develop", "target", "achievement", "summary"]
: ["name", "develop", "target"]
);
function onAdd() {
modalDevelop.value = true;
}
function onEdit(id: string) {
modalDevelop.value = true;
idEditDevelop.value = id;
}
function getDevelop() {
http
.get(config.API.kpiAchievementDevelop + `?id=${evaluationId.value}`)
.then((res) => {
const data = res.data.result;
rows.value = data;
store.devScoreVal = rows.value.reduce(
(sum: number, e: any) => sum + e.summary,
0
);
});
}
function onEvaluate() {
modalEvaluate.value = true;
}
function onDelete(id: string) {
dialogRemove($q, () => {
showLoader();
http
.delete(config.API.kpiAchievementDevelop + `/${id}`)
.then((res) => {
success($q, "ลบข้อมูลสำเร็จ");
getDevelop();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
});
}
const modalProgress = ref<boolean>(false);
const modalProblem = ref<boolean>(false);
const type = ref<string>("");
const idList = ref<string>("");
function openPopupProgress(id: string) {
modalProgress.value = true;
type.value = "development";
idList.value = id;
}
function openPopupProblem(id: string) {
modalProblem.value = true;
type.value = "development";
idList.value = id;
}
const isEditStep1 = computed(() => {
return (
(store.dataEvaluation.evaluationStatus === "NEW" &&
store.rolePerson === "USER" &&
store.tabMain === "1") ||
(store.dataEvaluation.evaluationStatus === "NEW_EVALUATOR" &&
store.rolePerson === "EVALUATOR" &&
store.tabMain === "1")
);
});
const isEditStep3 = computed(() => {
return (
(store.dataEvaluation.evaluationStatus === "EVALUATING" &&
store.rolePerson === "USER" &&
store.tabMain === "3") ||
(store.dataEvaluation.evaluationStatus === "EVALUATING_EVALUATOR" &&
store.rolePerson === "EVALUATOR" &&
store.tabMain === "3")
);
});
onMounted(() => {
getDevelop();
});
</script>
<template>
<q-card bordered style="border-radius: 5px" class="no-shadow">
<q-card-section class="bg-grey-2 q-py-sm">
<div class="row items-center">
<div class="col">
<span class="text-weight-medium">การพฒนาตนเอง</span>
<q-btn
v-if="isEditStep1"
class="q-ml-xs"
flat
round
icon="mdi-plus"
color="primary"
size="12px"
dense
@click="onAdd()"
>
<q-tooltip>เพมขอม</q-tooltip>
</q-btn>
</div>
<q-space />
<q-btn
v-if="isEditStep3"
flat
round
icon="mdi-clipboard-check-outline"
color="blue-5"
size="12px"
dense
@click="onEvaluate()"
>
<q-tooltip>ประเม</q-tooltip>
</q-btn>
</div>
</q-card-section>
<q-card-section class="q-pa-sm">
<q-table
ref="table"
:columns="columns"
:rows="rows"
row-key="id"
flat
bordered
:paging="true"
dense
hide-pagination
class="custom-table2"
no-data-label="ไม่มีข้อมูล"
:visible-columns="visibleColumns"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
<q-th auto-width />
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id" class="vertical-top">
<div v-if="col.name == 'createDate'">
{{ col.value ? date2Thai(col.value) : "-" }}
</div>
<div v-else-if="col.name == 'develop'">
<div class="column">
<q-checkbox
size="xs"
:model-value="props.row.isDevelopment70"
label="70 การลงมือปฏิบัติ (โดยผู้บังคับบัญชามอบหมาย)"
/>
<q-checkbox
size="xs"
:model-value="props.row.isDevelopment20"
label="20 การเรียนรู้จากผู้อื่น (Coach/Mentor/Consulting)"
/>
<q-checkbox
size="xs"
:model-value="props.row.isDevelopment10"
label="10 การฝึกอบรมอื่นๆ"
/>
</div>
</div>
<div v-else-if="col.name == 'achievement'">
<q-btn-group outline>
<q-btn
outline
color="grey-6"
label="0"
:class="props.row.summary == 0 && 'active'"
><q-tooltip>{{ props.row.achievement0 }}</q-tooltip></q-btn
>
<q-btn
outline
color="grey-6"
label="5"
:class="props.row.summary == 5 && 'active'"
><q-tooltip>{{ props.row.achievement5 }}</q-tooltip></q-btn
>
<q-btn
outline
color="grey-6"
label="10"
:class="props.row.summary == 10 && 'active'"
><q-tooltip>{{ props.row.achievement10 }}</q-tooltip></q-btn
>
</q-btn-group>
</div>
<div v-else-if="col.name === 'summary'">
{{ props.row.summary ? props.row.summary : 0 }}
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
<q-td>
<div
v-if="
store.dataEvaluation.evaluationStatus == 'APPROVE' &&
store.tabMain === '2'
"
>
<q-btn
flat
round
icon="mdi-developer-board"
color="blue-6"
size="12px"
dense
@click="openPopupProgress(props.row.id)"
>
<q-tooltip>รายงานความกาวหน</q-tooltip>
</q-btn>
<q-btn
flat
round
icon="warning"
color="red-5"
size="12px"
dense
main="problem"
@click="openPopupProblem(props.row.id)"
>
<q-tooltip>รายงานปญหา</q-tooltip>
</q-btn>
</div>
<div v-if="isEditStep1">
<q-btn
flat
round
icon="edit"
color="edit"
@click.stop.pervent="onEdit(props.row.id)"
>
<q-tooltip>แกไข </q-tooltip>
</q-btn>
<q-btn
flat
round
icon="delete"
color="red"
@click.stop.pervent="onDelete(props.row.id)"
>
<q-tooltip>ลบขอม </q-tooltip>
</q-btn>
</div>
</q-td>
</q-tr>
</template>
</q-table>
</q-card-section>
</q-card>
<DialogDevelop
v-model:modal="modalDevelop"
v-model:id="idEditDevelop"
:get-all="getDevelop"
/>
<DialogEvalutionDevelop
v-model:modal="modalEvaluate"
v-model:data="rows"
:get-all="getDevelop"
/>
<DialogProgress
v-model:modal="modalProgress"
v-model:type="type"
:idList="idList"
/>
<DialogProblem
v-model:modal="modalProblem"
v-model:type="type"
:idList="idList"
/>
</template>
<style scoped>
.custom-table2 {
max-height: 64vh;
.q-table tr:nth-child(odd) td {
background: white;
}
.q-table tr:nth-child(even) td {
background: #f8f8f8;
}
.q-table thead tr {
background: #ecebeb;
}
.q-table thead tr th {
position: sticky;
}
.q-table td:nth-of-type(2) {
z-index: 3 !important;
}
.q-table th:nth-of-type(2),
.q-table td:nth-of-type(2) {
position: sticky;
left: 0;
z-index: 1;
}
/* this will be the loading indicator */
.q-table thead tr:last-child th {
/* height of all previous header rows */
top: 48px;
}
.q-table thead tr:first-child th {
top: 0;
}
}
.q-btn-group--outline > .q-btn-item:not(:last-child):before {
border-right: 1px solid #c4c4c4;
}
.q-btn-group--outline > .q-btn-item.active {
color: #2196f3 !important;
background-color: #cde6fb !important;
}
.q-btn-group--outline > .q-btn-item + .q-btn-item.active:before {
border-left: 1px solid #2196f3 !important;
background-color: #cde6fb;
}
.q-btn-group--outline > .q-btn-item.active:not(:last-child):before {
border: 1px solid #2196f3;
background-color: #cde6fb;
}
</style>

View file

@ -1,5 +0,0 @@
<script setup lang="ts"></script>
<template></template>
<style lang="scss" scoped></style>

View file

@ -1,244 +0,0 @@
<script setup lang="ts">
import { ref, onMounted, computed, watch } from "vue";
import { useQuasar } from "quasar";
import { useRoute } from "vue-router";
import config from "@/app.config";
import http from "@/plugins/http";
import Work from "@/modules/14_KPI/components/detailList/Topic/01_Indicator.vue";
import Competency from "@/modules/14_KPI/components/detailList/Topic/02_Competency.vue";
import DialogListCriteria from "@/modules/14_KPI/components/detailList/Dialog/DialogListCriteria.vue";
import { useCounterMixin } from "@/stores/mixin";
import { useKPIDataStore } from "@/modules/14_KPI/store/KPIStore";
// import type { ListCriteria } from "@/modules/08_KPI/interface/request/index";
const dataListCriteria = ref<any[]>([]);
const indicatorScore = defineModel("indicatorScore", { default: 0 });
const competencyScore = defineModel("competencyScore", { default: 0 });
const modalCriteria = ref<boolean>(false);
const $q = useQuasar();
const route = useRoute();
const { showLoader, hideLoader, messageError } = useCounterMixin();
const store = useKPIDataStore();
const evaluationId = ref<string>(route.params.id.toString());
const rows_01 = ref<any[]>();
const rows_02 = ref<any[]>();
const rows_03 = ref<any[]>();
const totalResults1 = ref<number>(0);
const totalResults2 = ref<number>(0);
const totalResults3 = ref<number>(0);
// const resultWork = ref<number>(0);
function fetchListPlanned() {
http
.get(config.API.kpiAchievement("planned") + `?id=${evaluationId.value}`)
.then((res) => {
const data = res.data.result;
const newRow = data.map((e: any) => ({
...e,
evaluationResults: (e.point / 5) * e.weight,
}));
rows_01.value = newRow;
if (newRow.length > 0) {
const result = newRow.reduce(
(sum: number, e: any) => sum + e.evaluationResults,
0
);
const weight = newRow.reduce(
(sum: number, e: any) => sum + e.weight,
0
);
totalResults1.value =
(result * store.dataEvaluation.plannedPoint) / weight;
}
})
.catch((err) => {
messageError($q, err);
});
}
function fetchListRole() {
http
.get(config.API.kpiAchievement("role") + `?id=${evaluationId.value}`)
.then((res) => {
const data = res.data.result;
const newRow = data.map((e: any) => ({
...e,
evaluationResults: (e.point / 5) * e.weight,
}));
rows_02.value = newRow;
if (newRow.length > 0) {
const result = newRow.reduce(
(sum: number, e: any) => sum + e.evaluationResults,
0
);
const weight = newRow.reduce(
(sum: number, e: any) => sum + e.weight,
0
);
totalResults2.value =
(result * store.dataEvaluation.rolePoint) / weight;
}
})
.catch((err) => {
messageError($q, err);
});
}
function fetchAssigned() {
http
.get(config.API.kpiAchievement("special") + `?id=${evaluationId.value}`)
.then((res) => {
const data = res.data.result;
const newRow = data.map((e: any) => ({
...e,
evaluationResults: (e.point / 5) * e.weight,
}));
rows_03.value = newRow;
if (newRow.length > 0) {
const result = newRow.reduce(
(sum: number, e: any) => sum + e.evaluationResults,
0
);
const weight = newRow.reduce(
(sum: number, e: any) => sum + e.weight,
0
);
totalResults3.value =
(result * store.dataEvaluation.specialPoint) / weight;
}
})
.catch((err) => {
messageError($q, err);
});
}
function onInfo() {
modalCriteria.value = true;
}
const resultWork = computed(() => {
const total = totalResults1.value + totalResults2.value + totalResults3.value;
indicatorScore.value = total;
return total.toFixed(2);
});
function getCriteria() {
http
.get(config.API.kpiEvaluation)
.then((res) => {
const data = res.data.result.data;
dataListCriteria.value = data;
})
.catch((e) => {
messageError($q, e);
});
}
onMounted(async() => {
setTimeout(async() => {
await getCriteria();
await fetchListPlanned();
await fetchListRole();
await fetchAssigned();
hideLoader();
}, 1000);
});
</script>
<template>
<q-scroll-area
style="height: 100vh"
class="bg-white row col-12 text-dark q-pa-md"
>
<div class="text-weight-bold text-body2">
<span class="txt-under text-blue-6">องคประกอบท 1</span>
<span class="q-ml-sm"> ผลสมฤทธของงาน</span>
</div>
<div class="q-gutter-md q-mt-sm">
<!-- องคประกอบท 1 -->
<Work
v-model:data="rows_01"
:title="`1. งานตามแผนปฏิบัติราชการประจำปี`"
:page="1"
:fetchList="fetchListPlanned"
:total="totalResults1"
/>
<Work
v-model:data="rows_02"
:title="`2. งานตามหน้าที่ความรับผิดชอบหลัก`"
:page="2"
:fetchList="fetchListRole"
:total="totalResults2"
/>
<Work
v-model:data="rows_03"
:title="`3. งานที่ได้รับมอบหมายพิเศษ`"
:page="3"
:fetchList="fetchAssigned"
:total="totalResults3"
/>
<div class="row text-body2 text-weight-bold">
<!-- <div class="col-6 text-center row justify-center">
<span>รวมผลการประเม (อยละ) 100</span>
<div class="text-primary q-pl-md">{{ total }}</div>
</div> -->
<div class="col-12 text-center row justify-center">
<span
>สรปผลการประเมนผลสมฤทธของงาน (คะแนนเต
{{
store.dataEvaluation.plannedPoint +
store.dataEvaluation.rolePoint +
store.dataEvaluation.specialPoint
}}
คะแนน)</span
>
<div class="text-primary q-pl-md">{{ resultWork }}</div>
</div>
</div>
<q-separator size="3px" class="q-my-lg" />
<!-- องคประกอบท 2 -->
<div class="text-weight-bold text-body2 q-mb-sm">
<span class="txt-under text-blue-6">องคประกอบท 2</span>
<span class="q-ml-sm"> พฤตกรรมการปฎราชการ (สมรรถนะ)</span>
<q-btn
flat
icon="info"
color="info"
round
class="q-ml-xs"
@click="onInfo"
>
<q-tooltip>เกณฑการประเม</q-tooltip>
</q-btn>
</div>
<Competency
v-model:dataListCriteria="dataListCriteria"
v-model:competency-score="competencyScore"
/>
</div>
</q-scroll-area>
<DialogListCriteria
v-model:modal="modalCriteria"
v-model:dataListCriteria="dataListCriteria"
/>
</template>
<style scoped>
.txt-under {
text-decoration: underline;
}
</style>

View file

@ -1,527 +0,0 @@
<script setup lang="ts">
import { ref, reactive, watch } from "vue";
import { useQuasar } from "quasar";
import { useRoute } from "vue-router";
import config from "@/app.config";
import http from "@/plugins/http";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
import { useKpiDataStore } from "@/modules/08_KPI/store";
const $q = useQuasar();
const route = useRoute();
const mixin = useCounterMixin();
const store = useKpiDataStore();
const {
showLoader,
hideLoader,
messageError,
dialogConfirm,
dialogMessageNotify,
success,
} = mixin;
const modal = defineModel<boolean>("modal", { required: true });
const numpage = defineModel<number>("numpage", { required: true });
const isStatusEdit = defineModel<boolean>("isStatusEdit", { required: true });
const kpiUserPlannedId = defineModel<string>("kpiUserPlannedId", {
required: true,
});
const search = ref<string>("");
const listCheckID = ref<string | null>(null);
const listTarget = ref<any>([]);
const formDetail = reactive<any>({
orgRevisionId: "",
id: "",
year: null,
round: "",
kpiPeriodId: "",
includingName: "",
including: "",
target: "",
unit: null,
weight: null,
achievement1: "",
achievement2: "",
achievement3: "",
achievement4: "",
achievement5: "",
meaning: "",
formula: "",
node: null,
nodeId: "",
nodeName: "",
strategy: null,
strategyId: "",
strategyName: "",
});
function fetchIndicators() {
const nodeId = store.dataProfile.nodeId;
const node = store.dataProfile.node;
const kpiPeriodId = store.dataEvaluation.kpiPeriodId;
showLoader();
http
.get(
config.API.kpiPlan +
`?page=${1}&pageSize=${50}&kpiPeriodId=${kpiPeriodId}&nodeId=${nodeId}&node=${node}&keyword=${""}`
)
.then((res) => {
listTarget.value = res.data.result.data;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function fetchIndicatorsByid(id: string) {
showLoader();
http
.get(config.API.kpiAchievement("planned") + `/${id}`)
.then((res) => {
const data = res.data.result;
formDetail.target = data.target;
formDetail.unit = data.unit;
formDetail.weight = data.weight;
formDetail.meaning = data.meaning;
formDetail.formula = data.formula;
clickList(data.kpiPlanId, true);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function fetchListRole() {
const kpiPeriodId = store.dataEvaluation.kpiPeriodId;
const position = store.dataProfile.position;
http
.get(
config.API.kpiRole +
`?page=${1}&pageSize=${50}&kpiPeriodId=${kpiPeriodId}&position=${position}`
)
.then((res) => {
listTarget.value = res.data.result.data;
})
.finally(() => {
hideLoader();
});
}
function fetchRoleByid(id: string) {
showLoader();
http
.get(config.API.kpiAchievement("role") + `/${id}`)
.then((res) => {
const data = res.data.result;
formDetail.target = data.target;
formDetail.unit = data.unit;
formDetail.weight = data.weight;
formDetail.meaning = data.meaning;
formDetail.formula = data.formula;
clickList(data.kpiRoleId, true);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function clickList(id: string, isData: boolean = false) {
showLoader();
const url = numpage.value === 1 ? config.API.kpiPlan : config.API.kpiRole;
http
.get(`${url}/${id}`)
.then((res) => {
listCheckID.value = id;
const data = res.data.result;
if (!isData) {
formDetail.target = data.target;
formDetail.unit = data.unit;
formDetail.weight = data.weight;
formDetail.meaning = data.meaning;
formDetail.formula = data.formula;
}
formDetail.orgRevisionId = data.corgRevisionId;
formDetail.id = data.id;
formDetail.year = data.year;
formDetail.round = data.round;
formDetail.kpiPeriodId = data.kpiPeriodId;
formDetail.includingName = data.includingName;
formDetail.including = data.including;
formDetail.achievement1 = data.achievement1;
formDetail.achievement2 = data.achievement2;
formDetail.achievement3 = data.achievement3;
formDetail.achievement4 = data.achievement4;
formDetail.achievement5 = data.achievement5;
formDetail.node = data.node;
formDetail.nodeId = data.nodeId;
formDetail.nodeName = data.nodeName;
formDetail.strategy = data.strategy;
formDetail.strategyId = data.strategyId;
formDetail.strategyName = data.strategyName;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** ปิด dialog */
function closeDialog() {
modal.value = false;
search.value = "";
listCheckID.value = null;
formDetail.orgRevisionId = "";
formDetail.id = "";
formDetail.year = "";
formDetail.round = "";
formDetail.kpiPeriodId = "";
formDetail.includingName = "";
formDetail.including = "";
formDetail.target = "";
formDetail.unit = "";
formDetail.weight = "";
formDetail.achievement1 = "";
formDetail.achievement2 = "";
formDetail.achievement3 = "";
formDetail.achievement4 = "";
formDetail.achievement5 = "";
formDetail.meaning = "";
formDetail.formula = "";
formDetail.node = "";
formDetail.nodeId = "";
formDetail.strategy = "";
formDetail.strategyId = "";
}
function onSubmit() {
if (!listCheckID.value) {
dialogMessageNotify($q, "กรุณาเลือกตัวชี้วัด");
} else {
dialogConfirm($q, async () => {
// showLoader();
const formBody = {
target: formDetail.target,
unit: Number(formDetail.unit),
weight: Number(formDetail.weight),
meaning: formDetail.meaning,
formula: formDetail.formula,
kpiUserEvaluationId: store.dataEvaluation.id,
kpiPlanId: numpage.value === 1 ? listCheckID.value : undefined,
kpiRoleId: numpage.value === 2 ? listCheckID.value : undefined,
};
try {
const urlPlanned = isStatusEdit.value
? config.API.kpiAchievement("planned") + `/${kpiUserPlannedId.value}`
: config.API.kpiAchievement("planned");
const urlRole = isStatusEdit.value
? config.API.kpiAchievement("role") + `/${kpiUserPlannedId.value}`
: config.API.kpiAchievement("role");
const url = numpage.value === 1 ? urlPlanned : urlRole;
const method = isStatusEdit.value ? "put" : "post";
await http[method](url, formBody);
success($q, "บันทึกข้อมูลสำเร็จ");
} catch (err) {
messageError($q, err);
} finally {
hideLoader();
closeDialog();
}
});
}
}
watch(
() => modal.value,
() => {
if (modal.value) {
if (numpage.value === 1) {
fetchIndicators();
isStatusEdit.value && fetchIndicatorsByid(kpiUserPlannedId.value);
} else if (numpage.value === 2) {
fetchListRole();
isStatusEdit.value && fetchRoleByid(kpiUserPlannedId.value);
}
}
}
);
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card class="col-12" style="width: 100%">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader
:tittle="
numpage == 1
? 'เพิ่มตัวชี้วัดตามแผนปฏิบัติราชการประจําปี'
: 'เพิ่มตัวชี้วัดตามหน้าที่ความรับผิดชอบ'
"
:close="closeDialog"
/>
<q-separator />
<q-card-section class="q-pa-none scroll" style="max-height: 75vh">
<div class="col-12 row">
<div class="bg-grey-1 q-pa-md col-3 row lineRight">
<div class="col-12 fit">
<div class="col-12">
<q-input
v-model="search"
outlined
dense
label="ค้นหา"
class="inputgreen"
>
<template v-slot:append>
<q-icon v-if="search == ''" name="search" />
<q-icon
v-if="search !== ''"
name="clear"
class="cursor-pointer"
@click="search = ''"
/>
</template>
</q-input>
</div>
<q-card bordered flat class="q-mt-sm no-shadow bg-white col-12">
<div class="row q-px-md q-py-sm items-center bg-grey-1">
<div class="col-4">
<span>รหสตวช</span>
</div>
<div class="col-4">
<span>อตวช</span>
</div>
</div>
<q-separator />
<q-card-section class="q-pa-none">
<q-list separator>
<q-item
dense
v-for="(item, index) in listTarget"
:key="index"
clickable
v-ripple
:active="listCheckID === item.id"
active-class="my-menu-link"
@click="clickList(item.id)"
>
<q-item-section class="q-pa-none">
<div class="row items-center" style="height: 50px">
<div class="col-4">
{{ item.including }}
</div>
<div class="col-4">
{{ item.includingName }}
</div>
</div>
</q-item-section>
</q-item>
</q-list>
</q-card-section>
</q-card>
</div>
</div>
<div class="col-9">
<div class="row q-pa-md q-col-gutter-sm">
<div class="col-12">
<span class="text-body2 text-weight-medium"
>รายละเอยดตวช</span
>
</div>
<div class="col-6">
<q-card bordered class="full-height q-pa-sm">
<div class="q-pa-sm q-col-gutter-lg">
<div class="col-12 row">
<div class="col-4 text-grey-6">หนวยงาน/วนราชการ</div>
<div class="col-8">{{ formDetail.nodeName }}</div>
</div>
<div class="col-12 row" v-if="numpage === 1">
<div class="col-4 text-grey-6">ทธศาสตร / แผน</div>
<div class="col-8">{{ formDetail.strategyName }}</div>
</div>
<div class="col-12 row">
<div class="col-4 text-grey-6">รหสตวช</div>
<div class="col-8">{{ formDetail.including }}</div>
</div>
<div class="col-12 row">
<div class="col-4 text-grey-6">อตวช</div>
<div class="col-8">{{ formDetail.includingName }}</div>
</div>
<div class="col-12 row">
<div class="col-4 text-grey-6">าเปาหมาย</div>
<div class="col-8">
<q-input
outlined
v-model="formDetail.target"
bg-color="white"
dense
class="inputgreen"
:rules="[
(val) => !!val || `${'กรุณากรอกค่าเป้าหมาย'}`,
]"
hide-bottom-space
/>
</div>
</div>
<div class="col-12 row">
<div class="col-4 text-grey-6">หนวยน</div>
<div class="col-8">
<q-input
outlined
v-model="formDetail.unit"
bg-color="white"
dense
class="inputgreen"
:rules="[
(val) => !!val || `${'กรุณากรอกหน่วยนับ'}`,
]"
hide-bottom-space
mask="#"
reverse-fill-mask
/>
</div>
</div>
<div class="col-12 row">
<div class="col-4 text-grey-6">ำหน (อยละ)</div>
<div class="col-8">
<q-input
outlined
v-model="formDetail.weight"
bg-color="white"
dense
class="inputgreen"
:rules="[
(val) =>
!!val || `${'กรุณากรอกน้ำหนัก (ร้อยละ)'}`,
]"
hide-bottom-space
mask="###"
/>
</div>
</div>
</div>
</q-card>
</div>
<div class="col-6 row">
<div class="row col-12 card-box">
<div
class="col-12 bg-grey-2 row items-center text-weight-medium"
>
<div class="col-6 text-center">ระดบคะแนน</div>
<div class="col-6 text-center">ผลสำเรจของงาน</div>
</div>
<div class="row col-12 items-center lineTop">
<div class="col-6 text-center text-body2">5</div>
<div class="col-6 text-center text-primary">
{{ formDetail.achievement5 }}
</div>
</div>
<div class="row col-12 items-center lineTop">
<div class="col-6 text-center text-body2">4</div>
<div class="col-6 text-center text-primary">
{{ formDetail.achievement4 }}
</div>
</div>
<div class="row col-12 items-center lineTop">
<div class="col-6 text-center text-body2">3</div>
<div class="col-6 text-center text-primary">
{{ formDetail.achievement3 }}
</div>
</div>
<div class="row col-12 items-center lineTop">
<div class="col-6 text-center text-body2">2</div>
<div class="col-6 text-center text-primary">
{{ formDetail.achievement2 }}
</div>
</div>
<div class="row col-12 items-center lineTop">
<div class="col-6 text-center text-body2">1</div>
<div class="col-6 text-center text-primary">
{{ formDetail.achievement1 }}
</div>
</div>
</div>
</div>
<div class="col-12">
<q-input
v-model="formDetail.meaning"
label="นิยามหรือความหมายของตัวชี้วัด"
type="textarea"
outlined
dense
:rules="[(val:string) => !!val || `${'กรุณากรอกตัวชี้วัด'}`,]"
hide-bottom-space
class="inputgreen"
/>
</div>
<div class="col-12">
<q-input
v-model="formDetail.formula"
label="สูตรคำนวณ"
type="textarea"
outlined
dense
:rules="[(val:string) => !!val || `${'กรุณากรอกตัวชี้วัด'}`,]"
hide-bottom-space
class="inputgreen"
/>
</div>
</div>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn label="บันทึก" color="secondary" type="submit"
><q-tooltip>นทกขอม</q-tooltip></q-btn
>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<style scoped>
.my-menu-link {
background: #ebf9f7 !important;
color: #1bb19ab8 !important;
}
.no-shadow {
box-shadow: none !important;
}
.lineRight {
border-right: 1px solid #ededed !important;
}
.lineTop {
border-top: 1px solid #ededed !important;
}
.card-box {
border: 1px solid #ededed !important;
border-radius: 8px;
}
</style>

View file

@ -1,102 +0,0 @@
<script setup lang="ts">
import { ref } from "vue";
import { useRoute } from "vue-router";
import Assessment from "@/modules/14_KPI/components/detailList/01_Assessment.vue";
import Evaluator from "@/modules/14_KPI/components/detailList/02_Evaluator.vue";
import CommanderAbove from "@/modules/14_KPI/components/detailList/03_CommanderAbove.vue";
import CommanderAboveOneStep from "@/modules/14_KPI/components/detailList/04_CommanderAboveOneStep.vue";
import File from "@/modules/14_KPI/components/detailList/05_File.vue";
import { useKPIDataStore } from "@/modules/14_KPI/store/KPIStore";
const store = useKPIDataStore();
const route = useRoute();
const isReadonly = <boolean>(route.name === "KPIDetail" ? true : false);
const indicatorScore = defineModel("indicatorScore", { default: 0 });
const competencyScore = defineModel("competencyScore", { default: 0 });
const itemsTab = ref<any>([
{
name: "1",
label: "ผู้ขอรับการประเมิน",
},
{
name: "2",
label: "ผู้ประเมิน",
},
{
name: "3",
label: "ผู้บังคับบัญชาเหนือขึ้นไป",
},
{
name: "4",
label: "ผู้บังคับบัญชาเหนือขึ้นไปอีกชั้นหนึ่ง",
},
{
name: "5",
label: "ไฟล์เอกสาร",
},
]);
const splitterModel = ref<number>(12);
</script>
<template>
<q-splitter v-model="splitterModel" disable>
<template v-slot:before>
<q-tabs
v-model="store.tabMain"
vertical
class="text-grey-7 text-weight-light"
active-class="bg-blue-1 text-blue-8 text-weight-bold"
>
<q-tab name="1" label="ผู้ขอรับการประเมิน" />
<q-tab name="2" label="ผู้ประเมิน"> </q-tab>
<q-tab name="3" label="ผู้บังคับบัญชา">
<div class="text-caption">เหนอขนไป</div>
</q-tab>
<q-tab name="4" label="ผู้บังคับบัญชา">
<div class="text-caption">เหนอขนไปอกชนหน</div>
</q-tab>
<q-tab name="5" label="ไฟล์เอกสาร" />
</q-tabs>
</template>
<template v-slot:after>
<q-tab-panels
v-model="store.tabMain"
animated
swipeable
vertical
transition-prev="jump-up"
transition-next="jump-up"
>
<q-tab-panel
v-for="(tab, index) in itemsTab"
:key="index"
:name="tab.name"
class="q-pa-none"
>
<Assessment
v-if="store.tabMain === '1'"
v-model:indicatorScore="indicatorScore"
v-model:competencyScore="competencyScore"
/>
<Evaluator v-if="store.tabMain === '2'" :type="'evaluator'" />
<Evaluator v-if="store.tabMain === '3'" :type="'commander'" />
<Evaluator v-if="store.tabMain === '4'" :type="'commanderHigh'" />
<File v-if="store.tabMain === '5'" />
</q-tab-panel>
</q-tab-panels>
</template>
</q-splitter>
</template>
<style scoped>
.hover-tab:hover {
background-color: #0793f1;
color: white !important;
opacity: 1 !important;
}
</style>

View file

@ -1,345 +0,0 @@
<!-- <script setup lang="ts">
import { onMounted, ref } from "vue";
import Dialog from "@/modules/08_KPI/components/Tab/Dialog/04_FormCompetency.vue";
import DialogEvaluate from "@/modules/08_KPI/components/Tab/DialogEvaluate/02_Competenct.vue";
import { useQuasar, type QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useKpiDataStore } from "@/modules/08_KPI/store";
import http from "@/plugins/http";
import config from "@/app.config";
import { useRoute } from "vue-router";
import type { FormCapacityList } from "@/modules/08_KPI/interface/request/index";
const modalEvaluate = ref<boolean>(false);
const store = useKpiDataStore();
const route = useRoute();
const id = ref<string>(route.params.id as string);
const idCapacity = ref<string | null>(null);
const $q = useQuasar();
const mixin = useCounterMixin();
const {
date2Thai,
messageError,
showLoader,
hideLoader,
dialogRemove,
success,
} = mixin;
const type = defineModel<string>("type", { required: true });
const name = defineModel<any>("name", { required: true });
const lists = defineModel<any>("lists", { required: true });
const filterKeyword = ref<string>("");
const modal = ref<boolean>(false);
const visibleColumns = ref<string[]>([
"name",
"level",
"point",
"weight",
"summary",
]);
const columns = ref<QTableProps["columns"]>([
{
name: "name",
align: "left",
label: "รายการสมรรถนะ",
sortable: true,
field: "name",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "level",
align: "left",
label: "ระดับที่คาดหวัง",
sortable: true,
field: "level",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "point",
align: "left",
label: "ระดับคะแนนตามเกณฑ์การประเมิน",
sortable: true,
field: "point",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "weight",
align: "left",
label: "น้ำหนัก (ร้อยละ)",
sortable: true,
field: "weight",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "summary",
align: "left",
label: "ผลการประเมิน",
sortable: true,
field: "summary",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
function onAdd() {
modal.value = true;
}
const rows = ref<FormCapacityList[]>([]);
function getData() {
showLoader();
http
.get(config.API.kpiUserCapacity + `?id=${id.value}&type=${type.value}`)
.then((res) => {
const data = res.data.result.data;
rows.value = data;
lists.value.push(res.data.result.data);
console.log("lists card===>", lists.value);
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
function onEdit(data: FormCapacityList) {
modal.value = true;
idCapacity.value = data.id;
}
function onDelete(id: string) {
dialogRemove($q, () => {
showLoader();
http
.delete(config.API.kpiUserCapacity + `/${id}`)
.then((res) => {
success($q, "ลบข้อมูลสำเร็จ");
getData();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
});
}
function onEvaluate() {
modalEvaluate.value = true;
}
onMounted(() => {
getData();
});
</script>
<template>
<q-card bordered style="border-radius: 5px" class="no-shadow">
<q-card-section class="bg-grey-2 q-py-sm">
<div class="row items-center">
<div class="col">
<span class="text-weight-medium">{{ name }}</span>
<q-btn
class="q-ml-xs"
flat
round
icon="mdi-plus"
color="primary"
size="12px"
dense
@click="onAdd"
>
<q-tooltip>เพมขอม</q-tooltip>
</q-btn>
</div>
<q-space />
<q-btn
flat
round
icon="mdi-clipboard-check-outline"
color="blue-5"
size="12px"
dense
@click="onEvaluate"
>
<q-tooltip>ประเม</q-tooltip>
</q-btn>
</div>
</q-card-section>
<q-card-section class="q-pa-sm">
<q-table
ref="table"
:columns="columns"
:rows="rows"
:filter="filterKeyword"
row-key="id"
flat
bordered
:paging="true"
dense
hide-pagination
class="custom-table2"
:visible-columns="visibleColumns"
no-data-label="ไม่มีข้อมูล"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
<q-th auto-width />
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name == 'createDate'">
{{ col.value ? date2Thai(col.value) : "-" }}
</div>
<div v-else-if="col.name == 'point'">
<div v-if="type == 'HEAD' || type == 'GROUP'">
<q-rating
v-model="props.row.point"
max="5"
size="sm"
color="grey"
:color-selected="store.ratingColors"
label="ระดับการประเมินพฤติกรรม"
disable
>
<template v-slot:tip-1>
<q-tooltip>ำกวาระดบทคาดหวงมาก (1)</q-tooltip>
</template>
<template v-slot:tip-2>
<q-tooltip>ำกวาระดบทคาดหว (2)</q-tooltip>
</template>
<template v-slot:tip-3>
<q-tooltip>อยในระดบทคาดหว (3)</q-tooltip>
</template>
<template v-slot:tip-4>
<q-tooltip>อยในระดบสงกวาทคาดหว (4)</q-tooltip>
</template>
<template v-slot:tip-5>
<q-tooltip>เปนแบบอยางทใหบผ (5)</q-tooltip>
</template>
</q-rating>
</div>
<div v-else>รอ ทำ select</div>
</div>
<div v-else-if="col.name == 'summary'">
{{ props.row.point !== 0 ? props.row.point * 20 : "-" }}
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
<q-td auto-width>
<q-btn
flat
round
icon="edit"
color="edit"
@click.stop.pervent="onEdit(props.row)"
>
<q-tooltip>แกไข </q-tooltip>
</q-btn>
<q-btn
flat
round
icon="delete"
color="red"
@click.stop.pervent="onDelete(props.row.id)"
>
<q-tooltip>ลบขอม </q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</q-table>
</q-card-section>
</q-card>
<Dialog
v-model:modal="modal"
v-model:competency-type="type"
v-model:id="idCapacity"
:get-data-list="getData"
/>
<DialogEvaluate
v-model:modal="modalEvaluate"
v-model:data="rows"
v-model:type="type"
:get-data="getData"
/>
</template>
<style scoped>
.custom-table2 {
max-height: 64vh;
.q-table tr:nth-child(odd) td {
background: white;
}
.q-table tr:nth-child(even) td {
background: #f8f8f8;
}
.q-table thead tr {
background: #ecebeb;
}
.q-table thead tr th {
position: sticky;
}
.q-table td:nth-of-type(2) {
z-index: 3 !important;
}
.q-table th:nth-of-type(2),
.q-table td:nth-of-type(2) {
position: sticky;
left: 0;
z-index: 1;
}
/* this will be the loading indicator */
.q-table thead tr:last-child th {
/* height of all previous header rows */
top: 48px;
}
.q-table thead tr:first-child th {
top: 0;
}
}
</style> -->

View file

@ -0,0 +1,82 @@
interface FormProfile {
fullName: string;
position: string;
type: string;
level: string;
status: string | undefined;
result: string | undefined;
score: string;
avartar: string;
}
interface FormDataAssigned {
including: string;
includingName: string;
target: string;
unit: string;
weight: number | null;
achievement1: string;
achievement2: string;
achievement3: string;
achievement4: string;
achievement5: string;
meaning: string;
formula: string;
kpiUserEvaluationId: string;
}
interface FormCapacityList {
id: string | null;
name: string;
level: string;
point: number;
weight: number;
summary: number;
}
interface ListCriteria {
id: string;
level: number;
description: string;
}
interface ListCapacity {
id: string;
level: number;
description: string;
capacityId: string;
}
interface FormComment {
topic: string;
reason: string;
}
interface FormCommentByRole {
id: string;
topic: string;
reason: string;
reasonEvaluator: string;
reasonCommander: string;
reasonCommanderHigh: string;
}
interface FormQuery {
page: number;
pageSize: number;
round: string;
keyword: string;
}
export type {
FormProfile,
FormDataAssigned,
FormCapacityList,
ListCriteria,
ListCapacity,
FormComment,
FormCommentByRole,
FormQuery,
};

402
src/modules/14_KPI/store.ts Normal file
View file

@ -0,0 +1,402 @@
import { defineStore } from "pinia";
import { ref, reactive } from "vue";
import type { DataOption } from "@/modules/14_KPI/interface/index/Main";
import type { FormQuery } from "@/modules/14_KPI/interface/request/index";
export const useKpiDataStore = defineStore("KPIDataAdmin", () => {
const tabMainevaluator = ref<string>("1");
const yearRound = ref<number>(new Date().getFullYear());
const formQuery = reactive<FormQuery>({
page: 1,
pageSize: 10,
round: "",
keyword: "",
});
const selected = ref([]);
const tabMain = ref<string>("1");
const dataProfile = ref<any>({
profileId: null,
prefix: "",
rank: "",
firstName: "",
lastName: "",
citizenId: "",
position: "",
posMaster: null,
posLevelName: null,
posLevelRank: null,
posLevelId: null,
posTypeName: null,
posTypeRank: null,
posTypeId: null,
posExecutiveName: "",
posExecutivePriority: null,
posExecutiveId: null,
rootId: null,
root: "",
child1Id: null,
child1: null,
child2Id: null,
child2: null,
child3Id: null,
child3: null,
child4Id: null,
child4: null,
node: null,
nodeId: null,
});
const dataEvaluation = ref<any>({
evaluationReqEdit: null,
evaluationStatus: null,
profileId: null,
evaluatorId: null,
commanderId: null,
commanderHighId: null,
plannedPoint: 0,
rolePoint: 0,
specialPoint: 0,
});
const competencyType = ref<DataOption[]>([
{
id: "HEAD",
name: "สมรรถนะหลัก",
},
{
id: "GROUP",
name: "สมรรถนะประจำกลุ่มงาน",
},
{
id: "EXECUTIVE",
name: "สมรรถนะประจำผู้บริหารกรุงเทพมหานคร",
},
{
id: "DIRECTOR",
name: "สมรรถนะเฉพาะสำหรับตำแหน่ง ผอ.เขต ผช.ผอ.เขต และหัวหน้าฝ่ายในสังกัด สนง.เขต",
},
{
id: "INSPECTOR",
name: "สมรรถนะเฉพาะสำหรับตำแหน่งผู้ตรวจราชการ กทม. และผู้ตรวจราชการ",
},
]);
function convertCompetencyType(val: string) {
const competency = competencyType.value.find(
(x: DataOption) => x.id == val
);
return competency?.name;
}
const statusOptions = ref<DataOption[]>([
{
id: "NEW",
name: "จัดทำข้อตกลง",
},
{
id: "NEW_EVALUATOR",
name: "รอผู้ประเมินตรวจสอบข้อตกลง",
},
{
id: "NEW_COMMANDER",
name: "รอผู้บังคับบัญชาเหนือขึ้นไปตรวจสอบข้อตกลง",
},
{
id: "NEW_COMMANDER_HIGH",
name: "รอผู้บังคับบัญชาเหนือขึ้นไปอีกชั้นหนึ่งตรวจสอบข้อตกลง",
},
{
id: "APPROVE",
name: "รายงานความก้าวหน้า",
},
{
id: "EVALUATING",
name: "รายงานผลสำเร็จของงาน",
},
{
id: "EVALUATING_EVALUATOR",
name: "รอผู้ประเมินตรวจสอบผล",
},
{
id: "EVALUATING_COMMANDER",
name: "รอผู้บังคับบัญชาเหนือขึ้นไปตรวจสอบผล",
},
{
id: "EVALUATING_COMMANDER_HIGH",
name: "รอผู้บังคับบัญชาเหนือขึ้นไปอีกชั้นหนึ่งตรวจสอบผล",
},
{
id: "COMPLETE",
name: "เสร็จสิ้น",
},
]);
function convertStatus(val: string) {
return statusOptions.value.find((x: DataOption) => x.id == val)?.name;
}
function convertResults(val: string) {
switch (val) {
case "PENDING":
return "รอดำเนินการ";
case "PASSED":
return "ผ่านการประเมิน";
case "NOTPASSED":
return "ไม่ผ่านการประเมิน";
default:
break;
}
}
function checkCompetency() {
const position = dataEvaluation.value.position;
const posTypeName = dataEvaluation.value.posTypeName;
const posLevelName = dataEvaluation.value.posLevelName;
const executiveName = dataEvaluation.value.posExecutiveName;
if (
position == "ผู้ตรวจราชการกรุงเทพมหานคร" ||
position == "ผู้ตรวจราชการ"
) {
competencyType.value = competencyType.value.filter(
(x: DataOption) => x.id == "HEAD" || x.id == "INSPECTOR"
);
} else if (position == "ผู้อำนวยการเขต") {
competencyType.value = competencyType.value.filter(
(x: DataOption) => x.id == "HEAD" || x.id == "DIRECTOR"
);
} else {
if (executiveName == null) {
competencyType.value = competencyType.value.filter(
(x: DataOption) => x.id == "HEAD" || x.id == "GROUP"
);
} else {
competencyType.value = competencyType.value.filter(
(x: DataOption) => x.id == "HEAD" || x.id == "EXECUTIVE"
);
}
// switch (posTypeName + " " + posLevelName) {
// // case "ทั่วไป ปฏิบัติงาน":
// // case "ทั่วไป ชำนาญงาน":
// // case "ทั่วไป อาวุโส":
// // case "วิชาการ ปฏิบัติการ":
// // case "วิชาการ ชำนาญการ":
// // case "วิชาการ ชำนาญการพิเศษ":
// // case "วิชาการ เชี่ยวชาญ":
// // case "วิชาการ ทรงคุณวุฒิ":
// // competencyType.value = competencyType.value.filter(
// // (x: DataOption) => x.id == "HEAD" || x.id == "GROUP"
// // );
// // break;
// case "อำนวยการ ต้น":
// case "อำนวยการ สูง":
// case "บริหาร ต้น":
// case "บริหาร สูง":
// competencyType.value = competencyType.value.filter(
// (x: DataOption) => x.id == "HEAD" || x.id == "EXECUTIVE"
// );
// break;
// default:
// competencyType.value = competencyType.value.filter(
// (x: DataOption) => x.id == "HEAD" || x.id == "GROUP"
// );
// break;
// }
}
}
const defaultCompetencyCoreLevel = ref<number>();
const defaultCompetencyGroupLevel = ref<number | null>(null);
function checkCompetencyDefaultCompetencyLevel() {
const posTypeName = dataEvaluation.value.posTypeName;
const posLevelName = dataEvaluation.value.posLevelName;
switch (posTypeName + " " + posLevelName) {
case "บริหาร สูง":
defaultCompetencyCoreLevel.value = 5;
break;
case "บริหาร ต้น":
defaultCompetencyCoreLevel.value = 4;
break;
case "อำนวยการ สูง":
defaultCompetencyCoreLevel.value = 4;
break;
case "อำนวยการ ต้น":
defaultCompetencyCoreLevel.value = 3;
break;
case "วิชาการ ทรงคุณวุฒิ":
defaultCompetencyCoreLevel.value = 5;
defaultCompetencyGroupLevel.value = 5;
break;
case "วิชาการ เชี่ยวชาญ":
defaultCompetencyCoreLevel.value = 4;
defaultCompetencyGroupLevel.value = 4;
break;
case "วิชาการ ชำนาญการพิเศษ":
defaultCompetencyCoreLevel.value = 3;
defaultCompetencyGroupLevel.value = 4;
break;
case "วิชาการ ชำนาญการ":
defaultCompetencyCoreLevel.value = 2;
defaultCompetencyGroupLevel.value = 3;
break;
case "วิชาการ ปฏิบัติการ":
defaultCompetencyCoreLevel.value = 1;
defaultCompetencyGroupLevel.value = 2;
break;
case "ทั่วไป ทักษะพิเศษ":
defaultCompetencyCoreLevel.value = 4;
defaultCompetencyGroupLevel.value = 4;
break;
case "ทั่วไป อาวุโส":
defaultCompetencyCoreLevel.value = 3;
defaultCompetencyGroupLevel.value = 3;
break;
case "ทั่วไป ชำนาญงาน":
defaultCompetencyCoreLevel.value = 2;
defaultCompetencyGroupLevel.value = 2;
break;
case "ทั่วไป ปฏิบัติงาน":
defaultCompetencyCoreLevel.value = 1;
defaultCompetencyGroupLevel.value = 1;
break;
default:
defaultCompetencyCoreLevel.value = 1;
defaultCompetencyGroupLevel.value = 1;
break;
}
}
const ratingColors = ref<string[]>([
"light-blue-3",
"light-blue-6",
"blue",
"blue-9",
"blue-10",
]);
// ROLE & TAB
const rolePerson = ref<string>("USER"); //"USER" | "EVALUATOR" | "COMMANDER", "COMMANDERHIGH"
const tabOpen = ref<number>(1);
function checkStep() {
const role =
dataEvaluation.value.profileId == dataProfile.value.profileId
? "USER"
: dataEvaluation.value.evaluatorId == dataProfile.value.profileId
? "EVALUATOR"
: dataEvaluation.value.commanderId == dataProfile.value.profileId
? "COMMANDER"
: dataEvaluation.value.commanderHighId == dataProfile.value.profileId
? "COMMANDERHIGH"
: "";
rolePerson.value = role;
switch (dataEvaluation.value.evaluationStatus) {
case "NEW":
tabOpen.value = 1;
break;
case "NEW_EVALUATOR":
tabOpen.value = 1;
break;
case "NEW_COMMANDER":
tabOpen.value = 1;
break;
case "NEW_COMMANDER_HIGH":
tabOpen.value = 1;
break;
case "APPROVE":
tabOpen.value = 2;
break;
case "EVALUATING":
tabOpen.value = 3;
break;
case "EVALUATING_EVALUATOR":
tabOpen.value = 3;
break;
case "EVALUATING_COMMANDER":
tabOpen.value = 3;
break;
case "EVALUATING_COMMANDER_HIGH":
tabOpen.value = 3;
break;
case "COMPLETE":
tabOpen.value = 3;
break;
default:
tabOpen.value = 1;
break;
}
}
// SUMMARY GENERAL CASE
const indicatorWeightTotal = ref<number>(0); // น้ำหนักรวมกรณีทั่วไป
const indicatorWeight1Total = ref<number>(0); // น้ำหนักรวมมิติที่ 1 ต้องไม่เกิน 100%
const indicatorWeight2Total = ref<number>(0); // น้ำหนักรวมมิติที่ 2 ต้องไม่เกิน 20
const indicatorPercentVal = ref<number>(0); // รวมผลการประเมิน (ร้อยละ) ที่ได้จริง
const indicatorScore = ref<number>(70); // สรุปผลการประเมินผลสัมฤทธิ์ของงาน ( คะแนนเต็ม indicatorScore คะแนน)
const competencyScore = ref<number>(20); // ผลการประเมินสมรรถนะ (competencyScore คะแนน)
const devScoreVal = ref<number>(0); // ผลการประเมินการพัฒนาตนเองที่ได้กี่คะแนน
const competencyDevScore = ref<number>(30); // สรุปผลการประเมินพฤติกรรมการปฏิบัติราชการ (สมรรถนะ+การพัฒนาตนเอง) (คะแนนเต็ม competencyDevScore คะแนน)
const devScore = ref<number>(10); // ผลการประเมินการพัฒนาตนเอง (devScore คะแนน)
// SUMMARY EXCLUSIVE CASE
const excusiveIndicator1PercentVal = ref<number>(0); // รวมผลการประเมิน (ร้อยละ) มิติที่ 1 ที่ได้จริง
const excusiveIndicator1Weight = ref<number>(60); // น้ำหนักของมิติที่ 1
const excusiveIndicator1ScoreVal = ref<number>(0); // คะแนนมิติที่ 1 ที่ได้จริง
const excusiveIndicator2Weight = ref<number>(20); // น้ำหนักของมิติที่ 2
const excusiveIndicator2PercentVal = ref<number>(0); // รวมผลการประเมิน (ร้อยละ) มิติที่ 2 ที่ได้จริง
const excusiveIndicator2ScoreVal = ref<number>(0); // คะแนนมิติที่ 2 ที่ได้จริง
const excusiveIndicatorScore = ref<number>(80); // สรุปผลการประเมินผลสัมฤทธิ์ของงาน (มิติที่ 1 + มิติที่ 2) ( คะแนนเต็ม excusiveIndicatorScore คะแนน)
const excusiveCompetencyScore = ref<number>(20); // ผลการประเมินสมรรถนะ (competencyScore คะแนน)
const indicatorScoreVal = ref<number>(0); // สรุปผลการประเมินผลสัมฤทธิ์ของงานที่ได้
const competencyScoreVal = ref<number>(0); // ผลการประเมินสมรรถนะที่ได้กี่คะแนน
return {
tabMain,
dataProfile,
dataEvaluation,
competencyType,
convertCompetencyType,
convertStatus,
convertResults,
checkCompetency,
checkCompetencyDefaultCompetencyLevel,
defaultCompetencyCoreLevel,
defaultCompetencyGroupLevel,
ratingColors,
checkStep,
tabOpen,
rolePerson,
// score
indicatorWeightTotal,
indicatorWeight1Total,
indicatorWeight2Total,
indicatorPercentVal,
indicatorScore,
indicatorScoreVal,
competencyScore,
competencyScoreVal,
devScore,
devScoreVal,
competencyDevScore,
excusiveCompetencyScore,
excusiveIndicator1PercentVal,
excusiveIndicator1Weight,
excusiveIndicator1ScoreVal,
excusiveIndicator2Weight,
excusiveIndicator2PercentVal,
excusiveIndicator2ScoreVal,
excusiveIndicatorScore,
//รายการการประเมินผลการปฏิบัติราชการระดับบุคคล
tabMainevaluator,
formQuery,
yearRound,
selected,
statusOptions,
};
});

View file

@ -1,194 +0,0 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import type { DataOption } from "@/modules/14_KPI/interface/index/Main";
// store
export const useKPIDataStore = defineStore("KPIDataStore", () => {
const competencyTypeVal = ref<string>("HEAD");
const tabMain = ref<string>("1");
const dataProfile = ref<any>();
const dataEvaluation = ref<any>({
plannedPoint: 0,
rolePoint: 0,
specialPoint: 0,
capacityPoint: 0,
});
const competencyType = ref<DataOption[]>([
{
id: "HEAD",
name: "สมรรถนะหลัก",
},
{
id: "GROUP",
name: "สมรรถนะประจำกลุ่มงาน",
},
{
id: "EXECUTIVE",
name: "สมรรถนะประจำผู้บริหารกรุงเทพมหานคร",
},
{
id: "DIRECTOR",
name: "สมรรถนะเฉพาะสำหรับตำแหน่ง ผอ.เขต ผช.ผอ.เขต และหัวหน้าฝ่ายในสังกัด สนง.เขต",
},
{
id: "INSPECTOR",
name: "สมรรถนะเฉพาะสำหรับตำแหน่งผู้ตรวจราชการ กทม. และผู้ตรวจราชการ",
},
]);
function convertStatus(val: string) {
switch (val) {
case "PENDING":
return "รอดำเนินการ";
case "INPROGRESS":
return "กําลังดำเนินการ";
case "DONE":
return "ประเมินเสร็จสิ้น";
default:
break;
}
}
function convertResults(val: string) {
switch (val) {
case "PENDING":
return "รอดำเนินการ";
case "PASSED":
return "ผ่านการประเมิน";
case "NOTPASSED":
return "ไม่ผ่านการประเมิน";
default:
break;
}
}
const ratingColors = ref<string[]>([
"light-blue-3",
"light-blue-6",
"blue",
"blue-9",
"blue-10",
]);
function checkCompetency() {
const position = dataProfile.value.position;
const posTypeName = dataProfile.value.posTypeName;
const posLevelName = dataProfile.value.posLevelName;
if (
position == "ผู้ตรวจราชการกรุงเทพมหานคร" ||
position == "ผู้ตรวจราชการ"
) {
competencyType.value = competencyType.value.filter(
(x: DataOption) => x.id == "HEAD" || x.id == "INSPECTOR"
);
} else if (position == "ผู้อำนวยการเขต") {
competencyType.value = competencyType.value.filter(
(x: DataOption) => x.id == "HEAD" || x.id == "DIRECTOR"
);
} else {
switch (posTypeName + " " + posLevelName) {
// case "ทั่วไป ปฏิบัติงาน":
// case "ทั่วไป ชำนาญงาน":
// case "ทั่วไป อาวุโส":
// case "วิชาการ ปฏิบัติการ":
// case "วิชาการ ชำนาญการ":
// case "วิชาการ ชำนาญการพิเศษ":
// case "วิชาการ เชี่ยวชาญ":
// case "วิชาการ ทรงคุณวุฒิ":
// competencyType.value = competencyType.value.filter(
// (x: DataOptions) => x.id == "HEAD" || x.id == "GROUP"
// );
// break;
case "อำนวยการ ต้น":
case "อำนวยการ สูง":
case "บริหาร ต้น":
case "บริหาร สูง":
competencyType.value = competencyType.value.filter(
(x: DataOption) => x.id == "HEAD" || x.id == "EXECUTIVE"
);
break;
default:
competencyType.value = competencyType.value.filter(
(x: DataOption) => x.id == "HEAD" || x.id == "GROUP"
);
break;
}
}
}
const defaultCompetencyCoreLevel = ref<number>();
const defaultCompetencyGroupLevel = ref<number | null>(null);
function checkCompetencyDefaultCompetencyLevel() {
const posTypeName = dataProfile.value.posTypeName;
const posLevelName = dataProfile.value.posLevelName;
switch (posTypeName + " " + posLevelName) {
case "บริหาร สูง":
defaultCompetencyCoreLevel.value = 5;
break;
case "บริหาร ต้น":
defaultCompetencyCoreLevel.value = 4;
break;
case "อำนวยการ สูง":
defaultCompetencyCoreLevel.value = 4;
break;
case "อำนวยการ ต้น":
defaultCompetencyCoreLevel.value = 3;
break;
case "วิชาการ ทรงคุณวุฒิ":
defaultCompetencyCoreLevel.value = 5;
defaultCompetencyGroupLevel.value = 5;
break;
case "วิชาการ เชี่ยวชาญ":
defaultCompetencyCoreLevel.value = 4;
defaultCompetencyGroupLevel.value = 4;
break;
case "วิชาการ ชำนาญการพิเศษ":
defaultCompetencyCoreLevel.value = 3;
defaultCompetencyGroupLevel.value = 4;
break;
case "วิชาการ ชำนาญการ":
defaultCompetencyCoreLevel.value = 2;
defaultCompetencyGroupLevel.value = 3;
break;
case "วิชาการ ปฏิบัติการ":
defaultCompetencyCoreLevel.value = 1;
defaultCompetencyGroupLevel.value = 2;
break;
case "ทั่วไป ทักษะพิเศษ":
defaultCompetencyCoreLevel.value = 4;
defaultCompetencyGroupLevel.value = 4;
break;
case "ทั่วไป อาวุโส":
defaultCompetencyCoreLevel.value = 3;
defaultCompetencyGroupLevel.value = 3;
break;
case "ทั่วไป ชำนาญงาน":
defaultCompetencyCoreLevel.value = 2;
defaultCompetencyGroupLevel.value = 2;
break;
case "ทั่วไป ปฏิบัติงาน":
defaultCompetencyCoreLevel.value = 1;
defaultCompetencyGroupLevel.value = 1;
break;
default:
break;
}
}
return {
competencyTypeVal,
competencyType,
convertStatus,
convertResults,
tabMain,
dataProfile,
dataEvaluation,
ratingColors,
checkCompetency,
checkCompetencyDefaultCompetencyLevel,
};
});

View file

@ -1,29 +1,47 @@
<script setup lang="ts">
import { ref, onMounted, reactive } from "vue";
import { ref, onMounted, reactive, computed, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import TabMain from "@/modules/14_KPI/components/detailList/TabMain.vue";
import TabMain from "@/modules/14_KPI/components/Tab/TabMain.vue";
import { useQuasar } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useKPIDataStore } from "@/modules/14_KPI/store/KPIStore";
import { time } from "console";
import { useKpiDataStore } from "@/modules/14_KPI/store";
// import type { FormProfile } from "@/modules/08_KPI/interface/request/index";
import DialogHeader from "@/components/DialogHeader.vue";
import type { FormProfile } from "@/modules/14_KPI/interface/request/index";
import type { DataOption } from "@/modules/14_KPI/interface/index/Main";
// const modalScore = ref<boolean>(false);
const modalEdit = ref<boolean>(false);
const route = useRoute();
const id = ref<string>(route.params.id as string);
const isReadonly = <boolean>(route.name === "KPIDetail" ? true : false);
const isReadonly = <boolean>(route.name === "KPIEditEvaluator" ? true : false);
const store = useKPIDataStore();
const plannedPoint = ref<string>("");
const rolePoint = ref<string>("");
const specialPoint = ref<string>("");
const capacityPoint = ref<string>("");
const store = useKpiDataStore();
const $q = useQuasar();
const mixin = useCounterMixin();
const { showLoader, hideLoader, messageError } = mixin;
const indicatorScore = ref<number>(0);
const competencyScore = ref<number>(0);
const formProfile = reactive<any>({
const { showLoader, hideLoader, messageError, dialogConfirm, success } = mixin;
const evaluatorIdOp = ref<DataOption[]>([]);
const commanderIdOp = ref<DataOption[]>([]);
const commanderHighOp = ref<DataOption[]>([]);
const evaluatorIdMainOp = ref<DataOption[]>([]);
const commanderIdMainOp = ref<DataOption[]>([]);
const commanderHighMainOp = ref<DataOption[]>([]);
const evaluatorId = ref<any>(null);
const commanderId = ref<any>(null);
const commanderHighId = ref<any>(null);
const formProfile = reactive<FormProfile>({
fullName: "",
position: "",
type: "",
@ -36,37 +54,32 @@ const formProfile = reactive<any>({
const router = useRouter();
function fetchEvaluation() {
http
.get(config.API.kpiUserEvaluation + `/${id.value}`)
.then((res) => {
const data = res.data.result;
store.dataEvaluation = data;
async function fetchEvaluation() {
await http
.get(config.API.kpiEvaluation + `/${id.value}`)
.then(async (res) => {
const data = await res.data.result;
store.dataEvaluation = await data;
formProfile.status = store.convertStatus(data.evaluationStatus);
formProfile.result = store.convertResults(data.evaluationResults);
fetchProfile(data.profileId);
})
.catch((e) => {
messageError($q, e);
});
}
await store.checkCompetency();
await store.checkCompetencyDefaultCompetencyLevel();
function getProfile() {
http
.get(config.API.profilePosition())
.then((res) => {
const data = res.data.result;
store.dataProfile = data;
store.checkCompetency();
store.checkCompetencyDefaultCompetencyLevel();
formProfile.fullName = `${data.prefix}${data.firstName} ${data.lastName}`;
formProfile.position = data.position;
formProfile.type = data.posTypeName;
formProfile.level = data.posLevelName;
fetchProfile(data.profileId);
plannedPoint.value = data.plannedPoint == null ? "" : data.plannedPoint;
rolePoint.value = data.rolePoint == null ? "" : data.rolePoint;
specialPoint.value = data.specialPoint == null ? "" : data.specialPoint;
capacityPoint.value =
data.capacityPoint == null ? "" : data.capacityPoint;
})
.catch((e) => {
messageError($q, e);
});
// .finally(() => {
// hideLoader();
// });
}
async function fetchProfile(id: string) {
@ -75,20 +88,214 @@ async function fetchProfile(id: string) {
config.API.fileByFile("ทะเบียนประวัติ", "โปรไฟล์", id, `profile-${id}`)
)
.then(async (res) => {
formProfile.avartar = res.data.downloadUrl;
store.dataEvaluation.avartar = res.data.downloadUrl;
})
.catch(() => {
// profilePicture.value = avatar;
});
}
/** save */
function onSave() {}
function close() {
modalEdit.value = false;
evaluatorId.value = null;
commanderId.value = null;
commanderHighId.value = null;
getAll();
}
function onSubmit() {
dialogConfirm($q, () => {
if (id.value) {
showLoader();
http
.put(config.API.kpiEvaluationCheck + `/${id.value}`, {
evaluatorId: evaluatorId.value ? evaluatorId.value.id : null,
commanderId: commanderId.value ? commanderId.value.id : null,
commanderHighId: commanderHighId.value
? commanderHighId.value.id
: null,
})
.then((res) => {
success($q, "บันทึกสำเร็จ");
close();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
});
}
function getOrgOp() {
http
.get(config.API.Kpiorg)
.then((res) => {
const data = res.data.result;
evaluatorIdMainOp.value = data.caregiver.map((i: any) => ({
id: i.id,
name: `${i.prefix}${i.firstName} ${i.lastName}`,
}));
commanderIdMainOp.value = data.commander.map((i: any) => ({
id: i.id,
name: `${i.prefix}${i.firstName} ${i.lastName}`,
}));
commanderHighMainOp.value = data.chairman.map((i: any) => ({
id: i.id,
name: `${i.prefix}${i.firstName} ${i.lastName}`,
}));
evaluatorId.value = data.caregiver
.map((i: any) => ({
id: i.id,
name: `${i.prefix}${i.firstName} ${i.lastName}`,
}))
.find((i: any) => i.id == store.dataEvaluation.evaluatorId);
commanderId.value = data.caregiver
.map((i: any) => ({
id: i.id,
name: `${i.prefix}${i.firstName} ${i.lastName}`,
}))
.find((i: any) => i.id == store.dataEvaluation.commanderId);
commanderHighId.value = data.caregiver
.map((i: any) => ({
id: i.id,
name: `${i.prefix}${i.firstName} ${i.lastName}`,
}))
.find((i: any) => i.id == store.dataEvaluation.commanderHighId);
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {});
}
function filterOption(val: any, update: Function, refData: string) {
switch (refData) {
case "evaluatorIdOp":
update(() => {
evaluatorIdOp.value = evaluatorIdMainOp.value.filter(
(v: any) => v.name.indexOf(val) > -1
);
});
break;
case "commanderIdOp":
update(() => {
commanderIdOp.value = commanderIdMainOp.value.filter(
(v: any) => v.name.indexOf(val) > -1
);
});
break;
case "commanderHighOp":
update(() => {
commanderHighOp.value = commanderHighMainOp.value.filter(
(v: any) => v.name.indexOf(val) > -1
);
});
break;
default:
break;
}
}
// function onSubmitScore() {
// showLoader();
// http
// .put(config.API.kpiScoreTotal() + `/${id.value}`, {
// plannedPoint: plannedPoint.value,
// rolePoint: rolePoint.value,
// specialPoint: specialPoint.value,
// capacityPoint: capacityPoint.value,
// })
// .then(async (res) => {
// await fetchEvaluation();
// success($q, "");
// modalScore.value = false;
// })
// .catch((e) => {
// messageError($q, e);
// })
// .finally(() => {
// hideLoader();
// });
// }
// async function clearScore() {
// modalScore.value = false;
// plannedPoint.value = "";
// rolePoint.value = "";
// specialPoint.value = "";
// capacityPoint.value = "";
// getAll();
// }
async function getAll() {
await fetchEvaluation();
await store.checkStep();
await getOrgOp();
}
function sendToEvaluatore(status: string) {
dialogConfirm(
$q,
() => {
if (id.value) {
showLoader();
http
.put(config.API.kpiSendToStatus(id.value), {
status: status,
})
.then((res) => {
success($q, "ส่งข้อตกลงให้ผู้ประเมินอนุมัติสำเร็จ");
close();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
},
"ยืนยันการส่งข้อตกลงให้ผู้ประเมินอนุมัติ",
"ต้องการยืนยันส่งข้อตกลงนี้ให้ผู้ประเมินอนุมัติใช่หรือไม่?"
);
}
function requireEdit() {
dialogConfirm(
$q,
() => {
if (id.value) {
showLoader();
http
.put(config.API.kpiReqEdit(id.value), {
status: "EVALUATOR",
})
.then((res) => {
success($q, "ขอแก้ไขสำเร็จ");
close();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
},
"ยืนยันการขอแก้ไขข้อตกลง",
"ต้องการยืนยันการขอแก้ไขข้อตกลงนี้ใช่หรือไม่?"
);
}
onMounted(async () => {
showLoader();
fetchEvaluation();
getProfile();
await getAll();
});
</script>
@ -103,132 +310,436 @@ onMounted(async () => {
flat
color="primary"
class="q-mr-sm"
@click="
() => {
store.dataEvaluation = {};
isReadonly ? router.push(`/KPI-list`) : router.push(`/KPI`);
}
"
@click="router.push(`/KPI-list`)"
/>
{{ id ? `รายละเอียดแบบประเมิน` : `เพิ่มแบบประเมิน` }}
<q-space />
รายละเอยดการประเมนผลการปฏราชการระดบบคคล
</div>
<div class="col-12">
<q-card bordered flat class="relative-position">
<div
class="absolute-center-left"
style="left: 2%; top: 50%; transform: translateY(-50%)"
>
<q-avatar size="95px">
<q-img
v-if="formProfile.avartar == null"
src="@/assets/avatar_user.jpg"
class="bg-grey-3"
style="object-fit: cover"
/>
<div class="col-xs-12 col-sm-12 col-md-11">
<div class="col-12">
<q-card bordered flat class="relative-position">
<div
class="absolute-center-left"
style="left: 2%; top: 50%; transform: translateY(-50%)"
>
<q-avatar size="95px">
<q-img :src="store.dataEvaluation.avartar" />
</q-avatar>
</div>
<div class="row col-12">
<div class="row items-center col-12 q-pa-sm">
<div class="col-12" style="padding-left: 12%">
<div class="row col-12 items-center">
<span class="text-h6 text-weight-medium text-primary">{{
`${store.dataEvaluation.prefix}${store.dataEvaluation.firstName} ${store.dataEvaluation.lastName}`
}}</span>
<q-space />
<div class="q-gutter-x-sm">
<span
v-if="
(store.dataEvaluation.posExecutiveName == null &&
store.indicatorWeightTotal != 100) ||
(store.dataEvaluation.posExecutiveName != null &&
(store.indicatorWeight1Total != 100 ||
store.indicatorWeight2Total != 20))
"
class="text-red"
>*ำหน(อยละ) ผลสมฤทธของงานไมกตอง</span
>
<q-btn
v-if="
store.rolePerson == 'USER' &&
store.dataEvaluation.evaluationStatus == 'NEW'
"
:disabled="
(store.dataEvaluation.posExecutiveName == null &&
store.indicatorWeightTotal != 100) ||
(store.dataEvaluation.posExecutiveName != null &&
(store.indicatorWeight1Total != 100 ||
store.indicatorWeight2Total != 20))
"
unelevated
round
icon="mdi-send"
color="grey-2"
text-color="blue-6"
size="md"
@click="sendToEvaluatore('NEW_EVALUATOR')"
>
<q-tooltip>งใหประเมนอน</q-tooltip>
</q-btn>
<q-img v-else :src="formProfile.avartar" />
</q-avatar>
</div>
<div class="row col-12">
<div class="row items-center col-12 q-pa-sm">
<div class="col-12" style="padding-left: 12%">
<div class="row col-12 items-center">
<span class="text-h6 text-weight-medium text-primary">{{
formProfile.fullName ? formProfile.fullName : "-"
}}</span>
<q-space />
<div class="q-gutter-x-sm">
<q-btn
unelevated
round
icon="mdi-file-eye-outline"
color="grey-2"
text-color="primary"
size="md"
>
<q-tooltip>อมลการชวยราชการ</q-tooltip>
</q-btn>
<q-btn
unelevated
round
color="grey-2"
text-color="blue-5"
icon="mdi-file-eye-outline"
size="md"
>
<q-tooltip>อมลการทดลองงาน</q-tooltip>
</q-btn>
<q-btn
v-if="
store.rolePerson == 'USER' &&
store.dataEvaluation.evaluationStatus == 'EVALUATING'
"
unelevated
round
icon="mdi-send"
color="grey-2"
text-color="blue-6"
size="md"
@click="sendToEvaluatore('EVALUATING_EVALUATOR')"
>
<q-tooltip>งใหประเมนอนผลการประเม</q-tooltip>
</q-btn>
<q-btn
v-if="
store.rolePerson == 'USER' &&
store.tabOpen < 3 &&
store.dataEvaluation.evaluationStatus != 'NEW' &&
(store.dataEvaluation.evaluationReqEdit == null ||
store.dataEvaluation.evaluationReqEdit == 'DONE')
"
:disabled="store.dataEvaluation.evaluatorId == null"
unelevated
round
icon="mdi-file-edit"
color="grey-2"
text-color="red-6"
size="md"
@click="requireEdit()"
>
<q-tooltip>ขอแกไขขอตกลง</q-tooltip>
</q-btn>
<!-- <q-btn
v-if="!isReadonly"
unelevated
round
icon="mdi-format-list-bulleted-square"
color="grey-2"
text-color="amber-10"
size="md"
@click="modalScore = true"
>
<q-tooltip>แกไขคะแนนเต</q-tooltip>
</q-btn> -->
<q-btn
unelevated
round
icon="mdi-account"
color="grey-2"
text-color="edit"
size="md"
@click="modalEdit = true"
>
<q-tooltip>{{
store.dataEvaluation.evaluationStatus === "NEW" &&
store.rolePerson === "USER"
? "แก้ไขผู้ประเมิน"
: "ข้อมูลผู้ประเมิน"
}}</q-tooltip>
</q-btn>
<q-btn
unelevated
round
icon="mdi-file-eye-outline"
color="grey-2"
text-color="primary"
size="md"
>
<q-tooltip>อมลการชวยราชการ</q-tooltip>
</q-btn>
<q-btn
unelevated
round
color="grey-2"
text-color="blue-5"
icon="mdi-file-eye-outline"
size="md"
>
<q-tooltip>อมลการทดลองงาน</q-tooltip>
</q-btn>
</div>
</div>
</div>
</div>
</div>
<div class="row items-center bg-toolbar col-12 q-pa-sm">
<div class="col-12 q-py-xs" style="padding-left: 12%">
<div class="row no-wrap">
<div class="col-2">
<div class="column">
<span class="text-grey-6">ตำแหนงในสายงาน</span>
<span class="text-weight-medium text-dark">{{
formProfile.position
}}</span>
<div class="row items-center bg-toolbar col-12 q-pa-sm">
<div class="col-12 q-py-xs" style="padding-left: 12%">
<div class="row no-wrap">
<div class="col-2">
<div class="column">
<span class="text-grey-6">ตำแหนงในสายงาน</span>
<span class="text-weight-medium text-dark">{{
store.dataEvaluation.position
}}</span>
</div>
</div>
</div>
<div class="col-2">
<div class="column">
<span class="text-grey-6">ประเภทตำแหน</span>
<span class="text-weight-medium text-dark">{{
formProfile.type
}}</span>
<div class="col-2">
<div class="column">
<span class="text-grey-6">ประเภทตำแหน</span>
<span class="text-weight-medium text-dark">{{
store.dataEvaluation.posTypeName
}}</span>
</div>
</div>
</div>
<div class="col-2">
<div class="column">
<span class="text-grey-6">ระดบตำแหน</span>
<span class="text-weight-medium text-dark">{{
formProfile.level
}}</span>
<div class="col-2">
<div class="column">
<span class="text-grey-6">ระดบตำแหน</span>
<span class="text-weight-medium text-dark">{{
store.dataEvaluation.posLevelName
}}</span>
</div>
</div>
</div>
<div class="col-2">
<div class="column">
<span class="text-grey-6">สถานะการประเม</span>
<span class="text-weight-medium text-dark">{{
formProfile.status
}}</span>
<div class="col-2">
<div class="column">
<span class="text-grey-6">สถานะการประเม</span>
<span class="text-weight-medium text-dark">{{
formProfile.status
}}</span>
</div>
</div>
</div>
<div class="col-2">
<div class="column">
<span class="text-grey-6">ผลการประเม</span>
<span class="text-weight-medium text-dark">{{
formProfile.result
}}</span>
<div class="col-2">
<div class="column">
<span class="text-grey-6">ผลการประเม</span>
<span class="text-weight-medium text-dark">{{
formProfile.result
}}</span>
</div>
</div>
</div>
<div class="col-2">
<div class="column">
<span class="text-grey-6">คะแนนประเม</span>
<span class="text-weight-medium text-primary"
>{{ (indicatorScore + competencyScore).toFixed(2) }}
</span>
<div class="col-2">
<div class="column">
<span class="text-grey-6">คะแนนประเม</span>
<span class="text-weight-medium text-primary">
{{
store.dataEvaluation.posExecutiveName == null
? (
store.indicatorScoreVal +
store.competencyScoreVal +
store.devScoreVal
).toFixed(2)
: (
store.excusiveIndicator1ScoreVal +
store.excusiveIndicator2ScoreVal +
store.competencyScoreVal
).toFixed(2)
}}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</q-card>
</q-card>
<q-card class="q-mt-md rounded">
<TabMain
v-model:indicatorScore="indicatorScore"
v-model:competencyScore="competencyScore"
/>
</q-card>
<q-card class="q-mt-md rounded">
<TabMain />
</q-card>
</div>
</div>
</div>
<q-dialog v-model="modalEdit" persistent>
<q-card bordered style="width: 50vh">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader
:tittle="
store.dataEvaluation.evaluationStatus === 'NEW' &&
store.rolePerson === 'USER'
? 'แก้ไขผู้ประเมิน'
: 'ข้อมูลผู้ประเมิน'
"
:close="close"
/>
<q-separator />
<q-card-section>
<div class="column q-gutter-sm">
<q-select
:readonly="
!(
store.dataEvaluation.evaluationStatus === 'NEW' &&
store.rolePerson === 'USER'
)
"
v-model="evaluatorId"
outlined
label="ผู้ประเมิน"
dense
option-label="name"
option-value="id"
:options="evaluatorIdOp"
class="inputgreen"
map-options
hide-bottom-space
lazy-rules
:rules="[ (val:string) => !!val ||
`${'กรุณาเลือกผู้ประเมิน'}`, ]"
use-input
@filter="(inputValue:any,
doneFn:Function) => filterOption(inputValue, doneFn,'evaluatorIdOp'
) "
/>
<q-select
:readonly="
!(
store.dataEvaluation.evaluationStatus === 'NEW' &&
store.rolePerson === 'USER'
)
"
v-model="commanderId"
outlined
label="ผู้บังคับบัญชาเหนือขึ้นไป"
dense
option-label="name"
option-value="id"
:options="commanderIdOp"
map-options
class="inputgreen"
use-input
@filter="(inputValue:any,
doneFn:Function) => filterOption(inputValue, doneFn,'commanderIdOp'
) "
>
<template
v-if="
store.dataEvaluation.evaluationStatus === 'NEW' &&
store.rolePerson === 'USER' &&
commanderId
"
v-slot:append
>
<q-icon
name="cancel"
@click.stop.prevent="commanderId = null"
class="cursor-pointer"
/>
</template>
</q-select>
<q-select
:readonly="
!(
store.dataEvaluation.evaluationStatus === 'NEW' &&
store.rolePerson === 'USER'
)
"
v-model="commanderHighId"
outlined
label="ผู้บังคับบัญชาเหนือขึ้นไปอีกชั้นหนึ่ง"
dense
option-label="name"
option-value="id"
:options="commanderHighOp"
map-options
use-input
class="inputgreen"
@filter="(inputValue:any,
doneFn:Function) => filterOption(inputValue, doneFn,'commanderHighOp'
) "
>
<template
v-if="
store.dataEvaluation.evaluationStatus === 'NEW' &&
store.rolePerson === 'USER' &&
commanderHighId
"
v-slot:append
>
<q-icon
name="cancel"
@click.stop.prevent="commanderHighId = null"
class="cursor-pointer"
/>
</template>
</q-select>
</div>
</q-card-section>
<q-separator />
<q-card-actions
v-if="
store.dataEvaluation.evaluationStatus === 'NEW' &&
store.rolePerson === 'USER'
"
align="right"
class="bg-white text-teal"
>
<q-btn label="บันทึก" color="secondary" type="submit"
><q-tooltip>นทกขอม</q-tooltip></q-btn
>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<!-- <q-dialog v-model="modalScore" persistent>
<q-card bordered style="width: 50vh">
<q-form greedy @submit.prevent @validation-success="onSubmitScore">
<DialogHeader tittle="แก้ไขคะแนนเต็ม" :close="clearScore" />
<q-separator />
<q-card-section>
<div class="column q-gutter-sm">
<q-input
v-model="plannedPoint"
label="งานตามแผนปฏิบัติราชการประจำปี"
dense
outlined
class="inputgreen"
mask="###"
:rules="[
(val) =>
!!val ||
val == '0' ||
'กรุณากรอกคะเเนนงานตามแผนปฏิบัติราชการประจำปี หรือ 0',
]"
hide-bottom-space
lazy-rules
/>
<q-input
v-model="rolePoint"
label="งานตามหน้าที่ความรับผิดชอบหลัก"
dense
outlined
class="inputgreen"
mask="###"
:rules="[(val:string) => !!val || val == '0' || `${'กรุณากรอกคะเเนนงานตามหน้าที่ความรับผิดชอบหลัก หรือ 0'}`,]"
hide-bottom-space
lazy-rules
/>
<q-input
v-model="specialPoint"
label="งานที่ได้รับมอบหมายพิเศษ"
dense
outlined
class="inputgreen"
mask="###"
:rules="[(val:string) => !!val || val == '0' ||`${'กรุณากรอกคะเเนนงานที่ได้รับมอบหมายพิเศษ หรือ 0'}`,]"
hide-bottom-space
lazy-rules
/>
<q-input
v-model="capacityPoint"
label="สมรรถนะ"
dense
outlined
class="inputgreen"
mask="###"
:rules="[(val:string) => !!val || val == '0' ||`${'กรุณากรอกคะเเนนสมรรถนะ หรือ 0'}`,]"
hide-bottom-space
lazy-rules
/>
</div>
</q-card-section>
<q-separator />
<q-card-actions class="bg-white row justify-between">
<div class="col-8 text-red q-px-sm">
<span v-if="scoreTotal == true"
>คะแนนเตมรวมกนตองเทาก 100 คะแนน</span
>
</div>
<q-btn
label="บันทึก"
color="secondary"
type="submit"
:disable="totalScore !== 100"
><q-tooltip>นทกขอม</q-tooltip></q-btn
>
</q-card-actions>
</q-form>
</q-card>
</q-dialog> -->
</template>
<style>
.bg-toolbar {

View file

@ -9,11 +9,11 @@ import type { DataOption } from "@/modules/14_KPI/interface/index/Main";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useKPIDataStore } from "@/modules/14_KPI/store/KPIStore";
import { useKpiDataStore } from "@/modules/14_KPI/store";
const $q = useQuasar();
const router = useRouter();
const store = useKPIDataStore();
const store = useKpiDataStore();
const { showLoader, hideLoader, messageError, date2Thai, dialogConfirm } =
useCounterMixin();
@ -22,7 +22,7 @@ const filterKeyword = ref<string>("");
const rows = ref<any[]>([]);
const year = ref<number>(new Date().getFullYear());
const year = ref<number | null>(new Date().getFullYear());
const round = ref<string>("");
const roundOp = ref<DataOption[]>([]);
@ -31,7 +31,7 @@ const visibleColumns = ref<string[]>([
"name",
"createdAt",
"evaluationStatus",
"evaluationStatus",
"evaluationResults",
]);
const columns = ref<QTableProps["columns"]>([
{
@ -65,16 +65,16 @@ const columns = ref<QTableProps["columns"]>([
field: "evaluationStatus",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => store.convertResults(v),
format: (v) => store.convertStatus(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "evaluationStatus",
name: "evaluationResults",
align: "left",
label: "ผลการประเมิน",
sortable: true,
field: "evaluationStatus",
field: "evaluationResults",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => store.convertResults(v),
@ -97,20 +97,22 @@ function fetchRoundOption() {
config.API.kpiPeriod +
`?page=${1}&pageSize=${10}&keyword=${""}&year=${year.value}`
)
.then((res) => {
const data = res.data.result.data;
const list = data.map((e: any) => ({
id: e.id,
name:
e.durationKPI === "OCT"
? "รอบตุลาคม"
: e.durationKPI === "APR"
? "รอบเมษายน"
: "",
}));
roundOp.value = list;
round.value = "";
fetchList();
.then(async (res) => {
const data = await res.data.result.data;
if (res.data.result.data.length > 0) {
const list = await data.map((e: any) => ({
id: e.id,
name:
e.durationKPI === "OCT"
? "รอบตุลาคม"
: e.durationKPI === "APR"
? "รอบเมษายน"
: "",
}));
roundOp.value = list;
store.formQuery.round = list[0].id;
fetchList();
}
})
.catch((err) => {
messageError($q, err);
@ -120,13 +122,19 @@ function fetchRoundOption() {
});
}
// const status = ref<string>("");
function fetchList() {
showLoader();
const body = {
page: store.formQuery.page,
pageSize: store.formQuery.pageSize,
kpiPeriodId: store.formQuery.round,
keyword: store.formQuery.keyword,
// status: status.value,
};
http
.get(
config.API.kpiUserEvaluation +
`/admin?page=${formQuery.page}&pageSize=${formQuery.pageSize}&kpiPeriodId=${round.value}&keyword=${filterKeyword.value}`
)
.post(config.API.kpiEvaluation + `/admin`, body)
.then((res) => {
const data = res.data.result;
maxPage.value = Math.ceil(data.total / formQuery.pageSize);
@ -150,6 +158,13 @@ function redirectViewDetail(id: string) {
router.push(`KPI-list/${id}`);
}
function clearYear() {
year.value = null;
store.formQuery.round = "";
roundOp.value = [];
fetchList();
}
/**
* function updatePagination
* @param newPagination อม Pagination ใหม
@ -190,6 +205,7 @@ onMounted(async () => {
<datepicker
menu-class-name="modalfix"
v-model="year"
style="width: 150px"
:locale="'th'"
autoApply
year-picker
@ -208,6 +224,8 @@ onMounted(async () => {
hide-bottom-space
:model-value="!!year ? year + 543 : null"
:label="`${'ปีงบประมาณ'}`"
clearable
@clear="clearYear"
>
<template v-slot:prepend>
<q-icon
@ -222,18 +240,32 @@ onMounted(async () => {
</datepicker>
<q-select
v-model="round"
v-model="store.formQuery.round"
outlined
label="รอบการประเมิน"
dense
option-label="name"
option-value="id"
:options="roundOp"
style="min-width: 200px"
style="min-width: 150px"
emit-value
map-options
@update:model-value="changRound"
/>
<!-- <q-select
v-model="status"
outlined
label="สถานะการประเมิน"
dense
option-label="name"
option-value="id"
:options="store.statusOptions"
style="min-width: 180px"
emit-value
map-options
@update:model-value="changRound"
/> -->
</div>
<q-space />