start commit for admin system

This commit is contained in:
Warunee Tamkoo 2024-05-29 17:58:57 +07:00
commit badb676529
300 changed files with 58634 additions and 0 deletions

View file

@ -0,0 +1,365 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { useQuasar } from "quasar";
import { useRouter, useRoute } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
/** importType*/
import type { FormDataRole } from "@/modules/01_metadata/interface/request/Main";
import type { DataOption } from "@/modules/01_metadata/interface/index/Main";
/** importStore*/
import { useCounterMixin } from "@/stores/mixin";
/** use*/
const $q = useQuasar();
const route = useRoute();
const router = useRouter();
const { showLoader, hideLoader, messageError, date2Thai } = useCounterMixin();
const heightSize = ref<string>("224");
const id = ref<string>(route.params.id ? route.params.id.toLocaleString() : "");
const form = reactive<FormDataRole>({
position: "", //
year: null, //
round: "", //
org: "", ///
including: "", //
includingName: "", //
target: "", //
unit: "", //
weight: null, //
meaning: "", //
formula: "", //
documentInfoEvidence: "", //
node: null,
nodeId: null,
orgRevisionId: null,
date: [null, null],
});
const fieldLabels = {
score5: "5",
score4: "4",
score3: "3",
score2: "2",
score1: "1",
};
const formScore = reactive<any>({
score5: "",
score4: "",
score3: "",
score2: "",
score1: "",
});
/** Option รอบการประเมิน*/
const roundOp = ref<DataOption[]>([
{ id: "APR", name: "รอบเมษายน" },
{ id: "OCT", name: "รอบตุลาคม" },
]);
function fetchspecialByid(id: string) {
showLoader();
http
.get(config.API.kpiSpecial + `/${id}`)
.then((res) => {
console.log(res);
const data = res.data.result;
form.year = data.year;
form.round = data.period;
form.including = data.including;
form.includingName = data.includingName;
form.target = data.target;
form.unit = data.unit;
form.weight = data.weight;
formScore.score1 = data.achievement1;
formScore.score2 = data.achievement2;
formScore.score3 = data.achievement3;
formScore.score4 = data.achievement4;
formScore.score5 = data.achievement5;
form.formula = data.formula;
form.meaning = data.meaning;
form.documentInfoEvidence = data.documentInfoEvidence;
if (data.startDate && data.endDate) {
form.date = [data.startDate, data.endDate];
}
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function onResize(size: any) {
heightSize.value = `${size.height - 99}`;
}
onMounted(() => {
fetchspecialByid(id.value);
});
</script>
<template>
<div class="toptitle text-dark col-12 row items-center">
<q-btn
flat
round
dense
class="q-mr-sm"
icon="mdi-arrow-left"
color="primary"
@click="router.go(-1)"
/>
รายละเอยดงานทไดบมอบหมาย
</div>
<q-card flat>
<q-card-section>
<div class="row q-col-gutter-sm">
<div class="col-2">
<datepicker
menu-class-name="modalfix"
v-model="form.year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
@update:model-value="form.round = ''"
readonly
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
class="inputgreen"
outlined
:model-value="form.year === 0 ? null : Number(form.year) + 543"
:label="`${'ปีงบประมาณ'}`"
readonly
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-2">
<q-select
readonly
dense
outlined
v-model="form.round"
:options="roundOp"
option-label="name"
option-value="id"
emit-value
map-options
input-class="text-red"
label="รอบการประเมิน"
class="inputgreen"
/>
</div>
<div class="col-2" v-if="id">
<q-input
label="ลำดับ/รหัสตัวชี้วัด"
v-model="form.including"
outlined
readonly
dense
class="inputgreen"
/>
</div>
<div class="col-6">
<q-input
readonly
label="ชื่อตัวชี้วัด"
v-model="form.includingName"
outlined
dense
class="inputgreen"
lazy-rules
:rules="[(val:string) => !!val || `${'กรุณากรอกชื่อตัวชี้วัด'}`,]"
hide-bottom-space
/>
</div>
<div class="col-4">
<q-input
readonly
label="ค่าเป้าหมาย"
v-model="form.target"
outlined
dense
class="inputgreen"
lazy-rules
:rules="[(val:string) => !!val || `${'กรุณากรอกค่าเป้าหมาย'}`,]"
hide-bottom-space
/>
</div>
<div class="col-4">
<q-input
readonly
label="หน่วยนับ"
v-model="form.unit"
outlined
dense
class="inputgreen"
lazy-rules
:rules="[(val:string) => !!val || `${'กรุณากรอกหน่วยนับ'}`,]"
hide-bottom-space
/>
</div>
<div class="col-4">
<q-input
readonly
label="น้ำหนัก"
v-model="form.weight"
outlined
dense
class="inputgreen"
lazy-rules
:rules="[(val:string) => !!val || `${'กรุณากรอกน้ำหนัก'}`,]"
hide-bottom-space
/>
</div>
<div class="col-12">
<q-card flat bordered>
<q-resize-observer @resize="onResize" />
<q-card-section class="bg-grey-3 q-pa-sm">
<div class="row text-dark text-body2 text-weight-medium">
<div class="text-center col-4">ระดบคะแนน</div>
<div class="col-8">ผลสำเรจของงาน</div>
</div>
</q-card-section>
<q-card-section class="q-pa-none">
<div
v-for="(field, index) in Object.keys(fieldLabels)"
:key="index + 1"
>
<div class="row q-pa-sm">
<div
class="col-4 text-center text-body1 text-weight-bold self-center"
>
{{ fieldLabels[field as keyof typeof fieldLabels] }}
</div>
<div class="col-8 text-left">
<q-input
readonly
v-model="formScore[field]"
dense
outlined
class="inputgreen"
label="กรอกผลสำเร็จของงาน"
:rules="[(val:string) => !!val || `${'กรุณากรอกผลสำเร็จของงาน'}`,]"
hide-bottom-space
/>
</div>
</div>
<div
class="col-12"
v-if="index !== Object.keys(fieldLabels).length - 1"
>
<q-separator />
</div>
</div>
</q-card-section>
</q-card>
</div>
<div class="col-12">
<q-input
readonly
v-model="form.meaning"
label="นิยามหรือความหมาย"
dense
outlined
lazy-rules
class="inputgreen"
:rules="[(val:string) => !!val || `${'กรุณากรอกนิยามหรือความหมาย'}`,]"
hide-bottom-space
type="textarea"
/>
</div>
<div class="col-12">
<q-input
readonly
v-model="form.formula"
label="สูตรคำนวณ"
dense
outlined
lazy-rules
class="inputgreen"
:rules="[(val:string) => !!val || `${'กรุณากรอกสูตรคำนวณ'}`,]"
hide-bottom-space
type="textarea"
/>
</div>
<div class="col-12">
<q-input
readonly
class="inputgreen"
v-model="form.documentInfoEvidence"
label="ข้อมูลเอกสารหลักฐาน"
outlined
dense
type="textarea"
></q-input>
</div>
<div class="col-3">
<datepicker
v-model="form.date"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
range
readonly
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
readonly
dense
outlined
class="inputgreen"
:model-value="
form.date
? `${date2Thai(form.date[0])} - ${date2Thai(form.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>
</q-input>
</template>
</datepicker>
</div>
</div>
</q-card-section>
</q-card>
</template>
<style scoped></style>

View file

@ -0,0 +1,79 @@
<script setup lang="ts">
import { ref } from "vue";
import type { QTableProps } from "quasar";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
const mixin = useCounterMixin();
const { date2Thai } = mixin;
const modal = defineModel<boolean>("modal", { required: true });
const rows = defineModel<any[]>("rows", { required: true });
const visibleColumns = ref<string[]>(["createdFullName", "lastUpdatedAt"]);
const columns = ref<QTableProps["columns"]>([
{
name: "createdFullName",
align: "left",
label: "ชื่อ",
sortable: true,
field: "createdFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "lastUpdatedAt",
align: "left",
label: "เวลาที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
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: 30vw">
<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-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 == 'lastUpdatedAt'" class="table_ellipsis">
{{ col.value ? date2Thai(col.value, false, true) : "-" }}
</div>
<div v-else 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,31 @@
<script setup lang="ts">
import { ref } from "vue";
import { useRouter, useRoute } from "vue-router";
import IndicatorByPlan from "@/modules/01_metadata/components/Indicators/indicatorByPlan/IndicatorByPlan.vue";
const router = useRouter();
const route = useRoute();
const title = ref<string>(route.params.id ? "แก้ไข" : "เพิ่ม");
</script>
<template>
<div class="toptitle text-dark col-12 row items-center">
<q-btn
flat
round
dense
class="q-mr-sm"
icon="mdi-arrow-left"
color="primary"
@click="router.push(`/KPI-indicator-plan`)"
/>
{{ `${title}ตัวชี้วัดตามแผนฯ` }}
</div>
<q-card flat bordered>
<IndicatorByPlan />
</q-card>
</template>
<style scoped></style>

View file

@ -0,0 +1,678 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { QForm, useQuasar } from "quasar";
import { useRoute, useRouter } from "vue-router";
import config from "@/app.config";
import http from "@/plugins/http";
/** importType*/
import type { DataOption } from "@/modules/01_metadata/interface/index/Main";
/** importStore*/
import { usePositionEmp } from "@/modules/16_positionEmployee/store/organizational";
import { useCounterMixin } from "@/stores/mixin";
/**use*/
const $q = useQuasar();
const route = useRoute();
const router = useRouter();
const store = usePositionEmp();
const mixin = useCounterMixin();
const {
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
dialogMessageNotify,
} = mixin;
const id = ref<string>(route.params.id ? route.params.id.toLocaleString() : "");
const editCheck = ref<boolean>(route.params.id ? true : false);
/**form ตัวชี้วัดตามแผนฯ*/
const planData = reactive({
period: "", //(->APR, ->OCT)
including: "", //
includingName: "", //
target: "", //
unit: "", //
weight: null, //
achievement1: "", // 1
achievement2: "", // 2
achievement3: "", // 3
achievement4: "", // 4
achievement5: "", // 5
meaning: "", //
formula: "", //
node: null, //
nodeId: null, //id
orgRevisionId: "", //RevisionId
strategy: null, //
strategyId: "", //id
documentInfoEvidence: "", //
});
const year = ref<number | null>(0); //
const roundOp = ref<DataOption[]>([
{ id: "APR", name: "รอบเมษายน" },
{ id: "OCT", name: "รอบตุลาคม" },
]);
const nodeplan = ref<any>([]);
const nodeAgency = ref<any>([]);
const filter = ref<string>("");
const filterAgency = ref<string>("");
const expandedPlan = ref<string[]>([]);
const expandedAgency = ref<string[]>([]);
const editStatus = ref<boolean>(false);
const inputRef = ref<any>(null);
/** function fetch หาโครงสร้างที่ใช้งาน*/
function fetchOrganizationActive() {
http
.get(config.API.activeOrganization)
.then((res) => {
const data = res.data.result;
if (data) {
store.fetchDataActive(data);
store.activeId && fetchTreeAgency(store.activeId);
fetchTreeStrategy();
}
})
.catch((err) => {
messageError($q, err);
});
}
/** function fetchTree ยุทธศาสตร์ / แผน*/
function fetchTreeStrategy() {
http
.get(config.API.devStrategy)
.then((res) => {
const data = res.data.result;
nodeplan.value = data;
})
.catch((err) => {
messageError($q, err);
});
}
/** functioon fetchcTree หน่วยงาน/ส่วนราชการ*/
function fetchTreeAgency(id: string) {
http
.get(config.API.orgByid(id.toString()))
.then(async (res) => {
const data = res.data.result;
nodeAgency.value = data;
store.treeId = "";
})
.catch((err) => {
messageError($q, err);
});
}
/** function fetch ข้อมูลตัวชี้วัด*/
function fetchDataById(id: string) {
showLoader();
http
.get(config.API.kpiPlanById(id))
.then((res) => {
const data = res.data.result;
year.value = Number(data.year);
planData.period = data.round;
planData.including = data.including;
planData.includingName = data.includingName;
planData.target = data.target;
planData.unit = data.unit;
planData.weight = data.weight;
planData.achievement1 = data.achievement1;
planData.achievement2 = data.achievement2;
planData.achievement3 = data.achievement3;
planData.achievement4 = data.achievement4;
planData.achievement5 = data.achievement5;
planData.meaning = data.meaning;
planData.formula = data.formula;
planData.node = data.node;
planData.nodeId = data.nodeId;
planData.orgRevisionId = data.orgRevisionId;
planData.strategy = data.strategy;
planData.strategyId = data.strategyId;
planData.documentInfoEvidence = data.documentInfoEvidence;
// /
const arrayexpandedAgency = [
data.root,
data.child1,
data.child2,
data.child3,
data.child4,
];
expandedAgency.value = arrayexpandedAgency
.filter((e) => e !== null)
.slice(0, -1);
// /
const arrayexpandedPlan = [
data.strategyChild1,
data.strategyChild2,
data.strategyChild3,
data.strategyChild4,
data.strategyChild5,
];
expandedPlan.value = arrayexpandedPlan
.filter((e) => e !== null)
.slice(0, -1);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
setTimeout(() => {
hideLoader();
}, 1000);
});
}
/** funtion เลือกหรืออัปดเดท ยุทธศาสตร์ / แผน*/
function updateSelected(data: any) {
planData.strategyId = data.id;
planData.strategy = data.level;
}
/** funtion เลือกหรืออัปดเดท หน่วยงาน/ส่วนราชการ*/
function updateSelectedAgency(data: any, isUpdate: boolean = false) {
if (
planData.node === data.orgLevel &&
planData.nodeId === data.orgTreeId &&
!isUpdate
) {
planData.node = null;
planData.nodeId = null;
} else {
planData.node = data.orgLevel;
planData.nodeId = data.orgTreeId;
}
planData.orgRevisionId = data.orgRevisionId;
}
/**function ยืนยันการบันทึกข้อมูล */
function onSubmit() {
if (planData.nodeId == null || planData.strategyId == "") {
dialogMessageNotify(
$q,
`กรุณาเลือกหน่วยงาน/ส่วนราชการ หรือ ยุทธศาสตร์/แผน`
);
} else {
dialogConfirm(
$q,
async () => {
const body = {
year: year.value == 0 ? null : year.value?.toString(),
period: planData.period,
includingName: planData.includingName,
target: planData.target,
unit: planData.unit,
weight: planData.weight,
achievement1: planData.achievement1,
achievement2: planData.achievement2,
achievement3: planData.achievement3,
achievement4: planData.achievement4,
achievement5: planData.achievement5,
meaning: planData.meaning,
formula: planData.formula,
node: planData.node,
nodeId: planData.nodeId,
orgRevisionId: planData.orgRevisionId,
strategy: planData.strategy,
strategyId: planData.strategyId,
documentInfoEvidence: planData.documentInfoEvidence,
};
showLoader();
// editStatus.value ? editData(id.value) : addData();
try {
const url = editStatus.value
? config.API.kpiPlanById(id.value)
: config.API.kpiPlan;
const method = editStatus.value ? "put" : "post";
const res = await http[method](url, body);
success($q, "บันทึกข้อมูลสำเร็จ");
editStatus.value
? fetchDataById(id.value)
: router.push(`/KPI-indicator-plan/${res.data.result}`);
} catch (e) {
messageError($q, e);
} finally {
hideLoader();
}
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
}
onMounted(() => {
fetchOrganizationActive();
if (id.value) {
editStatus.value = true;
fetchDataById(id.value);
}
});
</script>
<template>
<q-form @submit.prevent greedy @validation-success="onSubmit()">
<div>
<div class="row q-col-gutter-md q-pa-md">
<div class="col-2" v-if="editCheck">
<q-input
outlined
v-model="planData.including"
label="ลำดับ/รหัสตัวชี้วัด"
bg-color="white"
readonly
dense
class="inputgreen"
/>
</div>
<div :class="`${editCheck ? 'col-6' : 'col-8'}`">
<q-input
outlined
v-model="planData.includingName"
label="ชื่อตัวชี้วัด"
bg-color="white"
dense
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกชิ่อตัวชี้วัด'}`]"
hide-bottom-space
/>
</div>
<div class="col-2">
<datepicker
menu-class-name="modalfix"
v-model="year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
outlined
class="inputgreen"
hide-bottom-space
:model-value="!!year ? year + 543 : null"
:label="`${'ปีงบประมาณ'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
<template v-slot:append>
<q-icon
v-if="year"
name="cancel"
class="cursor-pointer"
@click.stop.prevent="year = 0"
/>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-2">
<q-select
ref="inputRef"
dense
outlined
v-model="planData.period"
:options="roundOp"
label="รอบการประเมิน"
option-label="name"
option-value="id"
map-options
emit-value
class="inputgreen"
/>
</div>
<div class="col-4">
<q-input
outlined
v-model="planData.target"
label="ค่าเป้าหมาย"
bg-color="white"
dense
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกค่าเป้าหมาย'}`]"
hide-bottom-space
/>
</div>
<div class="col-4">
<q-input
outlined
v-model="planData.unit"
label="หน่วยนับ"
bg-color="white"
dense
lazy-rules
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกหน่วยนับ'}`]"
hide-bottom-space
/>
</div>
<div class="col-4">
<q-input
outlined
v-model="planData.weight"
label="น้ำหนัก"
type="number"
bg-color="white"
dense
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกน้ำหนัก'}`]"
hide-bottom-space
/>
</div>
<div class="col-12 row">
<div class="col-6">
<q-card flat bordered>
<q-card bordered>
<q-card-actions class="bg-grey-3 row">
<div class="col-4 flex justify-center items-center">
<div>ระดบคะแนน</div>
</div>
<div class="col-8 q-px-xl">ผลสำเรจของงาน</div>
</q-card-actions>
</q-card>
<div class="row">
<div class="col-4 flex justify-center items-center">
<div>5</div>
</div>
<div class="col-8 q-pa-sm">
<q-input
outlined
v-model="planData.achievement5"
label="กรอกผลสำเร็จของงาน"
bg-color="white"
dense
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกผลสำเร็จของงาน'}`]"
hide-bottom-space
/>
</div>
</div>
<div class="row">
<div class="col-4 flex justify-center items-center">
<div>4</div>
</div>
<div class="col-8 q-pa-sm">
<q-input
outlined
v-model="planData.achievement4"
label="กรอกผลสำเร็จของงาน"
bg-color="white"
dense
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกผลสำเร็จของงาน'}`]"
hide-bottom-space
/>
</div>
</div>
<div class="row">
<div class="col-4 flex justify-center items-center">
<div>3</div>
</div>
<div class="col-8 q-pa-sm">
<q-input
outlined
v-model="planData.achievement3"
label="กรอกผลสำเร็จของงาน"
bg-color="white"
dense
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกผลสำเร็จของงาน'}`]"
hide-bottom-space
/>
</div>
</div>
<div class="row">
<div class="col-4 flex justify-center items-center">
<div>2</div>
</div>
<div class="col-8 q-pa-sm">
<q-input
outlined
v-model="planData.achievement2"
label="กรอกผลสำเร็จของงาน"
bg-color="white"
dense
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกผลสำเร็จของงาน'}`]"
hide-bottom-space
/>
</div>
</div>
<div class="row">
<div class="col-4 flex justify-center items-center">
<div>1</div>
</div>
<div class="col-8 q-pa-sm">
<q-input
outlined
v-model="planData.achievement1"
label="กรอกผลสำเร็จของงาน"
bg-color="white"
dense
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกผลสำเร็จของงาน'}`]"
hide-bottom-space
/>
</div>
</div>
</q-card>
</div>
</div>
<div class="col-12">
<q-input
outlined
v-model="planData.meaning"
label="นิยามหรือความหมาย"
type="textarea"
bg-color="white"
dense
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกนิยามหรือความหมาย'}`]"
hide-bottom-space
/>
</div>
<div class="col-12">
<q-input
outlined
v-model="planData.formula"
label="สูตรคำนวณ"
bg-color="white"
type="textarea"
dense
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกสูตรคำนวณ'}`]"
hide-bottom-space
/>
</div>
<div class="col-6">
<q-card bordered class="col-12">
<div
class="col-xs-12 col-sm-12 text-weight-medium bg-grey-1 q-py-xs q-px-md"
>
หนวยงาน/วนราชการ
</div>
<q-separator />
<q-card-section class="q-pa-sm">
<q-input
dense
outlined
v-model="filterAgency"
label="ค้นหา"
class="inputgreen"
>
<template v-slot:append>
<q-icon
v-if="filterAgency !== ''"
name="clear"
class="cursor-pointer"
@click="filterAgency = ''"
/>
<q-icon v-else name="search" color="grey-5" />
</template>
</q-input>
<q-tree
style="height: 350px; overflow: scroll"
dense
:nodes="nodeAgency"
node-key="orgTreeId"
label-key="orgTreeName"
selected-color="primary"
:filter="filterAgency"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
v-model:expanded="expandedAgency"
v-model:selected="planData.nodeId"
>
<template v-slot:default-header="prop">
<q-item
clickable
@click.stop="updateSelectedAgency(prop.node)"
:active="planData.nodeId == prop.node.orgTreeId"
active-class="my-list-link text-primary text-weight-medium"
class="row col-12 items-center text-dark q-py-xs q-pl-sm rounded-borders my-list"
>
<div>
<div class="text-weight-medium">
{{ prop.node.orgTreeName }}
</div>
<div class="text-weight-light text-grey-8">
{{
prop.node.orgCode == null ? null : prop.node.orgCode
}}
{{
prop.node.orgTreeShortName == null
? null
: prop.node.orgTreeShortName
}}
</div>
</div>
</q-item>
</template>
</q-tree>
</q-card-section>
</q-card>
</div>
<div class="col-6">
<q-card bordered class="col-12">
<div
class="col-xs-12 col-sm-12 text-weight-medium bg-grey-1 q-py-xs q-px-md"
>
ทธศาสตร / แผน
</div>
<q-separator />
<q-card-section class="q-pa-sm">
<q-input
dense
outlined
v-model="filter"
label="ค้นหา"
class="inputgreen"
>
<template v-slot:append>
<q-icon
v-if="filter !== ''"
name="clear"
class="cursor-pointer"
@click="filter = ''"
/>
<q-icon v-else name="search" color="grey-5" />
</template>
</q-input>
<q-tree
style="height: 350px; overflow: scroll"
dense
:nodes="nodeplan"
selected-color="primary"
node-key="id"
label-key="name"
:filter="filter"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
v-model:expanded="expandedPlan"
v-model:selected="planData.strategyId"
>
<template v-slot:default-header="prop">
<q-item
clickable
@click.stop="updateSelected(prop.node)"
:active="planData.strategyId == prop.node.id"
active-class="my-list-link text-primary text-weight-medium"
class="row col-12 items-center text-dark q-py-xs q-pl-sm rounded-borders my-list"
>
<div>
<div class="text-weight-medium">
{{ prop.node.name }}
</div>
</div>
</q-item>
</template>
</q-tree>
</q-card-section>
</q-card>
</div>
<div class="col-12">
<q-input
class="inputgreen"
v-model="planData.documentInfoEvidence"
label="ข้อมูลเอกสารหลักฐาน"
outlined
dense
type="textarea"
></q-input>
</div>
</div>
<q-separator color="grey-4" />
</div>
<q-toolbar class="fit row wrap justify-end items-start content-start">
<q-btn
dense
unelevated
label="บันทึก"
id="onSubmit"
type="submit"
color="public"
class="q-px-md"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-toolbar>
</q-form>
</template>
<style lang="scss" scoped>
.my-list-link {
color: rgb(118, 168, 222);
border-radius: 5px;
background: #a3d3fb48 !important;
font-weight: 600;
border: 1px solid rgba(175, 185, 196, 0.217);
}
</style>

View file

@ -0,0 +1,640 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { useQuasar } from "quasar";
import { useRouter, useRoute } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import type { FormDataRole } from "@/modules/01_metadata/interface/request/Main";
import type { DataOption } from "@/modules/01_metadata/interface/index/Main";
const $q = useQuasar();
const route = useRoute();
const router = useRouter();
const heightSize = ref<string>("224");
const filter = ref<string>("");
const node = ref<any>([]);
const expanded = ref<string[]>([]);
const orgName = ref<string>("");
const nodeId = ref<string>("");
const id = ref<string>(route.params.id ? route.params.id.toLocaleString() : "");
const {
showLoader,
hideLoader,
messageError,
success,
dialogConfirm,
dialogMessageNotify,
} = useCounterMixin();
const title = ref<string>(route.params.id ? "แก้ไข" : "เพิ่ม");
const modalDialogSelect = ref<boolean>(false);
const form = reactive<FormDataRole>({
position: "", //
year: new Date().getFullYear(), //
round: "", //
org: "", ///
including: "", //
includingName: "", //
target: "", //
unit: "", //
weight: "", //
meaning: "", //
formula: "", //
documentInfoEvidence: "", //
node: null,
nodeId: null,
orgRevisionId: null,
});
const fieldLabels = {
score5: "5",
score4: "4",
score3: "3",
score2: "2",
score1: "1",
};
const formScore = reactive<any>({
score5: "",
score4: "",
score3: "",
score2: "",
score1: "",
});
const positionOp = ref<DataOption[]>([]);
const positionMainOp = ref<DataOption[]>([]);
/** Option รอบการประเมิน*/
const roundOp = ref<DataOption[]>([
{ id: "APR", name: "รอบเมษายน" },
{ id: "OCT", name: "รอบตุลาคม" },
]);
/**
* function นหาขอมลของ Option
* @param val าทองการฟลเตอร
* @param update พเดทค
* @param refData ดาตาทองการฟลเตอร
*/
function filterOption(val: any, update: Function) {
update(() => {
positionOp.value = positionMainOp.value.filter(
(v: any) => v.name.indexOf(val) > -1
);
});
}
/** ดึงข้อมูลตำแหน่ง */
function getOptions() {
http.get(config.API.orgSalaryPosition).then((res) => {
const dataOp = res.data.result;
const uniqueNames = new Set();
const filteredData = dataOp
.filter((item: any) => {
if (!uniqueNames.has(item.positionName)) {
uniqueNames.add(item.positionName);
return true;
}
return false;
})
.map((item: any) => ({
id: item.positionName,
name: item.positionName,
}));
positionMainOp.value = filteredData;
});
}
/** เปิด Dialog หน่วยงาน */
function selectAgency() {
modalDialogSelect.value = true;
}
/** บันทึกข้อมูล */
function onSubmit() {
const url = id.value
? config.API.kpiRoleMainList + `/${id.value}`
: config.API.kpiRoleMainList;
const body = {
year: form.year == 0 ? null : form.year?.toString(),
position: form.position, //
period: form.round, //(->APR, ->OCT)
includingName: form.includingName, //
target: form.target, //
unit: form.unit, //
weight: form.weight, //
achievement1: formScore.score1, // 1
achievement2: formScore.score2, // 2
achievement3: formScore.score3, // 3
achievement4: formScore.score4, // 4
achievement5: formScore.score5, // 5
meaning: form.meaning, //
formula: form.formula, //
documentInfoEvidence: form.documentInfoEvidence, //
node: form.node, //
nodeId: form.nodeId, //id
orgRevisionId: form.orgRevisionId, //RevisionId
};
if (form.nodeId == null) {
dialogMessageNotify($q, "กรุณาเลือกหน่วยงาน/ส่วนราชการ");
} else {
dialogConfirm($q, () => {
showLoader();
http[id.value ? "put" : "post"](url, body)
.then(() => {
success($q, "บันทึกสำเร็จ");
id.value ? getDetail() : router.push(`/KPI-indicator-role`);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
}
//
//
function getDetail() {
showLoader();
http
.get(config.API.kpiRoleMainList + `/${id.value}`)
.then((res) => {
const data = res.data.result;
form.position = data.position;
form.year = data.year == null ? 0 : data.year;
form.round = data.round;
form.including = data.including;
form.includingName = data.includingName;
form.target = data.target;
form.unit = data.unit;
form.weight = data.weight;
form.meaning = data.meaning;
form.formula = data.formula;
form.documentInfoEvidence = data.documentInfoEvidence;
formScore.score1 = data.achievement1;
formScore.score2 = data.achievement2;
formScore.score3 = data.achievement3;
formScore.score4 = data.achievement4;
formScore.score5 = data.achievement5;
form.node = data.node;
form.nodeId = data.nodeId;
form.orgRevisionId = data.orgRevisionId;
const arrayExpanded = [
data.root,
data.child1,
data.child2,
data.child3,
data.child4,
];
expanded.value = arrayExpanded.filter((e) => e !== null).slice(0, -1);
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
function fetchActive() {
showLoader();
http
.get(config.API.activeOrganization)
.then((res) => {
const data = res.data.result;
fetchTree(data.activeId);
})
.catch((err) => {
messageError($q, err);
hideLoader();
});
}
async function fetchTree(id: string) {
showLoader();
http
.get(config.API.orgByid(id.toString()))
.then((res) => {
const data = res.data.result;
node.value = data;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function updateSelected(data: any) {
nodeId.value = data.orgTreeId;
orgName.value = data.orgTreeName;
form.node = data.orgLevel;
form.nodeId = data.orgTreeId;
form.orgRevisionId = data.orgRevisionId;
}
function onResize(size: any) {
heightSize.value = `${size.height - 99}`;
}
function setModel(val: string) {
form.position = val;
}
onMounted(() => {
fetchActive();
getOptions();
if (id.value !== "") {
getDetail();
}
});
</script>
<template>
<div class="toptitle text-dark col-12 row items-center">
<q-btn
flat
round
dense
class="q-mr-sm"
icon="mdi-arrow-left"
color="primary"
@click="router.go(-1)"
/>
{{ `${title}ตัวชี้วัดตามตำแหน่ง` }}
</div>
<q-card flat>
<q-form greedy @submit.prevent @validation-success="onSubmit">
<q-card-section>
<div class="row q-col-gutter-sm">
<div class="col-8">
<q-select
dense
:model-value="form.position"
label="ตำแหน่งในสายงาน"
outlined
emit-value
map-options
fill-input
hide-selected
lazy-rules
:rules="[(val:string) => !!val || `${'กรุณาเลือกตำแหน่งในสายงาน'}`,]"
hide-bottom-space
option-label="name"
option-value="id"
class="inputgreen"
:options="positionOp"
use-input
@input-value="setModel"
@filter="(inputValue:any,doneFn:Function) => filterOption(inputValue, doneFn) "
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไมอม
</q-item-section>
</q-item>
</template></q-select
>
</div>
<div class="col-2">
<datepicker
menu-class-name="modalfix"
v-model="form.year"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
class="inputgreen"
outlined
:model-value="
form.year === 0 ? null : Number(form.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="form.year"
name="cancel"
class="cursor-pointer"
@click.stop.prevent="form.year = 0"
/>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-2">
<q-select
dense
outlined
v-model="form.round"
:options="roundOp"
option-label="name"
option-value="id"
emit-value
map-options
input-class="text-red"
label="รอบการประเมิน"
class="inputgreen"
/>
</div>
<div class="col-2" v-if="id">
<q-input
label="ลำดับ/รหัสตัวชี้วัด"
v-model="form.including"
outlined
readonly
dense
class="inputgreen"
/>
</div>
<div class="col-4">
<q-input
label="ชื่อตัวชี้วัด"
v-model="form.includingName"
outlined
dense
class="inputgreen"
lazy-rules
:rules="[(val:string) => !!val || `${'กรุณากรอกชื่อตัวชี้วัด'}`,]"
hide-bottom-space
/>
</div>
<div class="col-2">
<q-input
label="ค่าเป้าหมาย"
v-model="form.target"
outlined
dense
class="inputgreen"
lazy-rules
:rules="[(val:string) => !!val || `${'กรุณากรอกค่าเป้าหมาย'}`,]"
hide-bottom-space
/>
</div>
<div class="col-2">
<q-input
label="หน่วยนับ"
v-model="form.unit"
outlined
dense
class="inputgreen"
lazy-rules
:rules="[(val:string) => !!val || `${'กรุณากรอกหน่วยนับ'}`,]"
hide-bottom-space
/>
</div>
<div class="col-2">
<q-input
label="น้ำหนัก"
v-model="form.weight"
outlined
dense
class="inputgreen"
lazy-rules
:rules="[(val:string) => !!val || `${'กรุณากรอกน้ำหนัก'}`,]"
hide-bottom-space
/>
</div>
<div class="col-4">
<q-card flat bordered class="full-height">
<q-card-section class="bg-grey-3 q-pa-sm">
<div class="text-dark text-body2 text-weight-medium">
หนวยงาน/วนราชการ
</div>
</q-card-section>
<q-card-section class="q-pa-sm">
<q-input
dense
outlined
v-model="filter"
label="ค้นหา"
class="inputgreen"
>
<template v-slot:append>
<q-icon
v-if="filter !== ''"
name="clear"
class="cursor-pointer"
@click="filter = ''"
/>
<q-icon v-else name="search" color="grey-5" />
</template>
</q-input>
<q-scroll-area
visible
:style="{ height: `${heightSize}px`, marginTop: '5px' }"
>
<q-tree
dense
:nodes="node"
node-key="orgTreeId"
label-key="orgTreeName"
v-model:expanded="expanded"
:filter="filter"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
>
<template v-slot:default-header="prop">
<q-item
clickable
@click.stop="updateSelected(prop.node)"
:active="form.nodeId === prop.node.orgTreeId"
active-class="my-list-link text-primary text-weight-medium"
class="row col-12 items-center text-dark q-py-xs q-pl-sm rounded-borders my-list"
>
<div>
<div class="text-weight-medium">
{{ prop.node.orgTreeName }}
</div>
<div class="text-weight-light text-grey-8">
{{
prop.node.orgCode == null
? null
: prop.node.orgCode
}}
{{
prop.node.orgTreeShortName == null
? null
: prop.node.orgTreeShortName
}}
</div>
</div>
</q-item>
</template>
</q-tree>
</q-scroll-area>
</q-card-section>
</q-card>
</div>
<div class="col-8">
<q-card flat bordered>
<q-resize-observer @resize="onResize" />
<q-card-section class="bg-grey-3 q-pa-sm">
<div class="row text-dark text-body2 text-weight-medium">
<div class="text-center col-4">ระดบคะแนน</div>
<div class="col-8">ผลสำเรจของงาน</div>
</div>
</q-card-section>
<q-card-section class="q-pa-none">
<div
v-for="(field, index) in Object.keys(fieldLabels)"
:key="index + 1"
>
<div class="row q-pa-sm">
<div
class="col-4 text-center text-body1 text-weight-bold self-center"
>
{{ fieldLabels[field as keyof typeof fieldLabels] }}
</div>
<div class="col-8 text-left">
<!-- <q-field
class="q_field_p_none"
ref="fieldRef"
v-model="formScore[field]"
label-slot
borderless
:rules="[(val) => !!val || 'กรุณากรอกมาตรฐานพฤติกรรม']"
hide-bottom-space
>
<template #control>
<q-editor
class="full-width"
v-model="formScore[field]"
:dense="$q.screen.lt.md"
min-height="5rem"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
:fonts="{
arial: 'Arial',
arial_black: 'Arial Black',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
}"
/>
</template>
</q-field> -->
<q-input
v-model="formScore[field]"
dense
outlined
class="inputgreen"
label="กรอกผลสำเร็จของงาน"
:rules="[(val:string) => !!val || `${'กรุณากรอกผลสำเร็จของงาน'}`,]"
hide-bottom-space
/>
</div>
</div>
<div
class="col-12"
v-if="index !== Object.keys(fieldLabels).length - 1"
>
<q-separator />
</div>
</div>
</q-card-section>
</q-card>
</div>
<div class="col-12">
<q-input
v-model="form.meaning"
label="นิยามหรือความหมาย"
dense
outlined
lazy-rules
class="inputgreen"
:rules="[(val:string) => !!val || `${'กรุณากรอกนิยามหรือความหมาย'}`,]"
hide-bottom-space
type="textarea"
/>
</div>
<div class="col-12">
<q-input
v-model="form.formula"
label="สูตรคำนวณ"
dense
outlined
lazy-rules
class="inputgreen"
:rules="[(val:string) => !!val || `${'กรุณากรอกสูตรคำนวณ'}`,]"
hide-bottom-space
type="textarea"
/>
</div>
<div class="col-12">
<q-input
class="inputgreen"
v-model="form.documentInfoEvidence"
label="ข้อมูลเอกสารหลักฐาน"
outlined
dense
type="textarea"
></q-input>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn color="public" label="บันทึก" type="submit" unelevated>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</template>
<style scoped>
.my-list-link {
color: rgb(118, 168, 222);
border-radius: 5px;
background: #a3d3fb48 !important;
font-weight: 600;
border: 1px solid rgba(175, 185, 196, 0.217);
}
</style>

View file

@ -0,0 +1,855 @@
<!-- tab ปฏ หนาปฏนวนหย -->
<template>
<div class="q-mt-md">
<div class="row q-gutter-sm q-pb-sm main-content">
<div class="demo-app-main">
<!-- แสดงปฏ -->
<FullCalendar
ref="fullCalendar"
class="demo-app-calendar"
:options="calendarOptions"
>
<template v-slot:eventContent="arg">
<b>{{ arg.timeText }}</b>
<i>{{ arg.event.title }}</i>
<q-tooltip style="font-size: 15px">{{ arg.event.title }}</q-tooltip>
</template>
</FullCalendar>
</div>
</div>
<div class="row q-col-gutter-md">
<div class="items-center row">
<q-icon color="blue" name="mdi-circle" class="q-mr-sm" />
นทำงาน 5
</div>
<div class="items-center row">
<q-icon color="orange" name="mdi-circle" class="q-mr-sm" />
นทำงาน 6
</div>
</div>
</div>
<!-- modal เพมวนหย -->
<q-dialog v-model="modalAdd" persistent>
<q-card style="min-width: 550px">
<q-form ref="formDate" @submit.prevent.stop="onSubmit">
<q-card-section class="row items-center q-pa-sm">
<div class="text-bold" v-if="showEdit">แก้ไขวันหยุด</div>
<div class="text-bold" v-else>เพิ่มวันหยุด</div>
<q-space />
<q-btn
icon="close"
unelevated
round
dense
v-close-popup
style="color: #ff8080; background-color: #ffdede"
/>
</q-card-section>
<q-separator />
<q-card-section class="q-p-md row q-gutter-y-md">
<div class="row col-12">
<div class="col-12" v-if="!showEdit">
{{ dateThaiRange(dateRange) }}
</div>
<datepicker
v-else
:readonly="!edit"
v-model="dateRange"
:locale="'th'"
autoApply
range
:enableTimePicker="false"
week-start="0"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
:class="getClass(edit)"
hide-bottom-space
:outlined="edit"
dense
label="วันที่"
:borderless="!edit"
:model-value="dateThaiRange(dateRange)"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<q-input
:class="getClass(edit)"
hide-bottom-space
:outlined="edit"
label="คำอธิบาย"
dense
lazy-rules
:readonly="!edit"
:borderless="!edit"
v-model="name"
autogrow
:rules="[(val) => (val && val.length > 0) || '']"
/>
<q-option-group
v-if="showEdit == false"
v-model="category"
:options="categoryOptions"
color="primary"
inline
/>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="text-primary">
<q-btn
v-if="showEdit && edit"
flat
color="red"
@click="cancelClick()"
label="ยกเลิกแก้ไข"
>
<!-- icon="mdi-undo"
<q-tooltip>ยกเล</q-tooltip> -->
</q-btn>
<q-btn
v-if="showEdit && edit"
unelevated
color="red"
@click="deleteClick()"
label="ลบวันหยุด"
>
<!-- icon="mdi-delete"
<q-tooltip>ลบ</q-tooltip> -->
</q-btn>
<q-btn
v-if="edit"
unelevated
color="public"
label="บันทึก"
type="submit"
>
<!-- icon="mdi-content-save-outline"
<q-tooltip>นท</q-tooltip> -->
</q-btn>
<q-btn
v-if="!edit"
outline
color="primary"
@click="editClick"
label="แก้ไขข้อมูล"
>
<!-- icon="mdi-pencil-outline"
<q-tooltip>แกไขขอม</q-tooltip> -->
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<!-- modal ลบวนหย -->
<q-dialog v-model="modalDelete" persistent>
<q-card style="min-width: 550px">
<q-card-section class="row items-center q-pb-xs">
<div class="text-bold">องการลบขอมลนหรอไม?</div>
<q-space />
<q-btn
icon="close"
unelevated
round
dense
v-close-popup
style="color: #ff8080; background-color: #ffdede"
/>
</q-card-section>
<q-separator />
<q-card-section class="row items-center">
<div class="q-pr-md">
<q-avatar
icon="mdi-trash-can-outline"
font-size="25px"
size="lg"
color="red-1"
text-color="red"
/>
</div>
<div class="col text-dark">
<span>อมลทกำลงถกลบนจะมผลใชงานทนท</span>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn label="ยกเลิก" flat color="grey-8" @click="cancelClick" />
<q-btn label="ตกลง" color="primary" @click="deleteData" />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from "vue";
import { useQuasar } from "quasar";
import FullCalendar from "@fullcalendar/vue3";
import dayGridPlugin from "@fullcalendar/daygrid";
import timeGridPlugin from "@fullcalendar/timegrid";
import interactionPlugin from "@fullcalendar/interaction";
import allLocales from "@fullcalendar/core/locales-all";
import listPlugin from "@fullcalendar/list";
import http from "@/plugins/http";
import config from "@/app.config";
import type {
RequestItemsObject,
DataDateRowObject,
DataDateAddObject,
} from "@/modules/01_metadata/interface/index/Calendar";
import { useCounterMixin } from "@/stores/mixin";
const props = defineProps({
dateYear: {
//filter
type: Number,
default: () => new Date().getFullYear(),
},
dateMonth: {
//filter
type: Number,
default: () => new Date().getMonth(),
},
refreshData: {
// main refresh data
type: Boolean,
required: true,
},
fetchDataSummaryCalendar: {
//
type: Function,
default: () => console.log("not function"),
},
});
const mixin = useCounterMixin(); //
const { success, dateToISO, date2Thai, messageError, showLoader, hideLoader } =
mixin;
const $q = useQuasar(); // noti quasar
const modalAdd = ref<boolean>(false); //modal
const modalDelete = ref<boolean>(false); //modal
const edit = ref<boolean>(false); //modal
const showEdit = ref<boolean>(false); // event
const name = ref<string>(""); //
const type = ref<string>(""); //
const isSpecial = ref<boolean>(true); //
const id = ref<string>(""); //id
const fullCalendar = ref<any>(); //ref calendar
const dateRange = ref<[Date, Date]>([new Date(), new Date()]); //
const formDate = ref<any>(); //ref validate
const category = ref<string>("all");
const categoryOptions = ref<any>([
{
label: "ทั้งหมด",
value: "all",
},
{
label: "ทำงาน 5 วัน",
value: "normal",
},
{
label: "ทำงาน 6 วัน",
value: "6days",
},
]);
const dataHistory = ref<RequestItemsObject[]>([]);
const dataNormalRaw = ref<RequestItemsObject[]>([]);
const dataSixDaysRaw = ref<RequestItemsObject[]>([]);
/**
* เรยกฟงกนทงหมดตอนเรยกใชไฟล
*/
onMounted(async () => {
hideLoader();
const calen = fullCalendar.value.getApi();
const date = new Date(props.dateYear, props.dateMonth);
calen.gotoDate(date);
await fetchData();
});
/**
* props(นเดอนปเลอก) ตอนอพเดท าฏนใหพเดทใหม
*/
watch(props, async (count, prevCount) => {
const calen = fullCalendar.value.getApi();
const date = new Date(props.dateYear, props.dateMonth);
calen.gotoDate(date);
await fetchData();
});
/**
* เลอกคาปฏนในชองวาง
* @param selectInfo าทเลอกในฏ
*/
const handleDateSelect = async (selectInfo: any) => {
edit.value = true;
category.value = "all";
selectInfo.start;
const checkNormalDate = dataNormalRaw.value.filter(
(r: RequestItemsObject) =>
dateToISO(new Date(r.holidayDate)) == dateToISO(selectInfo.start)
);
const checkSixDaysDate = dataSixDaysRaw.value.filter(
(r: RequestItemsObject) =>
dateToISO(new Date(r.holidayDate)) == dateToISO(selectInfo.start)
);
if (checkNormalDate.length == 0 || checkSixDaysDate.length == 0) {
name.value = "";
isSpecial.value = true;
dateRange.value = [
selectInfo.startStr,
new Date(
new Date(selectInfo.endStr).setDate(
new Date(selectInfo.endStr).getDate() - 1
)
),
];
showEdit.value = false;
modalAdd.value = true;
}
};
/**
* เลอกค event ในปฏ
* @param selectInfo าทเลอกในฏ
*/
const handleEventClick = async (selectInfo: any) => {
modalAdd.value = true;
edit.value = false;
showEdit.value = true;
name.value = selectInfo.event.title;
type.value = selectInfo.event.id;
isSpecial.value = true;
id.value = selectInfo.event.groupId;
dataHistory.value = selectInfo.event.extendedProps.dataRangeRow;
dateRange.value = [
selectInfo.event.startStr,
selectInfo.event.endStr == ""
? selectInfo.event.startStr
: dateToISO(
new Date(
new Date(selectInfo.event.endStr).setDate(
new Date(selectInfo.event.endStr).getDate() - 1
)
)
),
];
};
/**
* กดปมแกไขใหแสดง modal แกไข
*/
const editClick = async () => {
showEdit.value = true;
edit.value = true;
};
/**
* กดปมลบใหแสดง modal ลบ
*/
const deleteClick = async () => {
modalDelete.value = true;
};
/**
* มยกเลกใหอนกลบไป modal เลอกแกไขหรอลบ
*/
const cancelClick = async () => {
modalDelete.value = false;
modalAdd.value = true;
edit.value = false;
};
/**
* option calendar
*/
const calendarOptions = ref<any>({
plugins: [
dayGridPlugin,
timeGridPlugin,
interactionPlugin, // needed for dateClick
listPlugin,
],
headerToolbar: null,
selectable: true,
select: handleDateSelect,
eventClick: handleEventClick,
locale: "th",
locales: allLocales,
height: "100%",
eventColor: "#e4f3ff",
eventTextColor: "#50a5fc",
eventBorderColor: "#50a5fc",
events: <any>[],
firstDay: 0,
});
/**
* มบนทกขอมลวนหย
*/
const onSubmit = async () => {
if (showEdit.value === true) {
await editData();
} else {
await addDate();
}
};
/**
* fetch นหยดในปฏ
*/
const fetchData = async () => {
calendarOptions.value.events = [];
showLoader();
await http
.get(
config.API.listHolidayHistoryYearMonth(
props.dateYear,
props.dateMonth + 1
)
)
.then((res) => {
const dataNormal = res.data.result.normal;
const dataSixDays = res.data.result.sixDays;
const dateStart = ref<Date | null>();
const firstEvent = ref<boolean>(true);
const dateRow = ref<DataDateRowObject[]>([]);
dataNormalRaw.value = res.data.result.normal;
dataSixDaysRaw.value = res.data.result.sixDays;
dataNormal.map((e: RequestItemsObject, index: number) => {
dateRow.value.push({
holidayDate: new Date(e.holidayDate),
name: e.name,
isSpecial: true,
id: e.id,
});
if (
index == dataNormal.length - 1 ||
dataNormal[index + 1].name != e.name ||
(dataNormal[index + 1].name == e.name &&
dateToISO(new Date(dataNormal[index + 1].holidayDate)) !=
dateToISO(
new Date(
new Date(e.holidayDate).setDate(
new Date(e.holidayDate).getDate() + 1
)
)
))
) {
firstEvent.value = true;
calendarOptions.value.events.push({
id: "normal",
groupId: e.id,
title:
dateToISO(new Date(e.holidayDate)) ==
dateToISO(new Date(e.originalDate))
? e.name
: `ชดเชย ${e.name}`,
start: dateStart.value ? dateStart.value : new Date(e.holidayDate),
end: new Date(
new Date(e.holidayDate).setDate(
new Date(e.holidayDate).getDate() + 1
)
),
isSpecial: true,
allDay: true,
dataRangeRow: dateRow.value,
backgroundColor: "#CCE5FF",
textColor: "#0080FF",
});
dateStart.value = null;
dateRow.value = [];
} else if (firstEvent.value == true) {
firstEvent.value = false;
dateStart.value = new Date(e.holidayDate);
}
});
dataSixDays.map((e: RequestItemsObject, index: number) => {
dateRow.value.push({
holidayDate: new Date(e.holidayDate),
name: e.name,
isSpecial: true,
id: e.id,
});
if (
index == dataSixDays.length - 1 ||
dataSixDays[index + 1].name != e.name ||
(dataSixDays[index + 1].name == e.name &&
dateToISO(new Date(dataSixDays[index + 1].holidayDate)) !=
dateToISO(
new Date(
new Date(e.holidayDate).setDate(
new Date(e.holidayDate).getDate() + 1
)
)
))
) {
firstEvent.value = true;
calendarOptions.value.events.push({
id: "sixdays",
groupId: e.id,
title:
dateToISO(new Date(e.holidayDate)) ==
dateToISO(new Date(e.originalDate))
? e.name
: `ชดเชย ${e.name}`,
start: dateStart.value ? dateStart.value : new Date(e.holidayDate),
end: new Date(
new Date(e.holidayDate).setDate(
new Date(e.holidayDate).getDate() + 1
)
),
isSpecial: true,
allDay: true,
dataRangeRow: dateRow.value,
backgroundColor: "#FFE5CC",
textColor: "#FF8000",
});
dateStart.value = null;
dateRow.value = [];
} else if (firstEvent.value == true) {
firstEvent.value = false;
dateStart.value = new Date(e.holidayDate);
}
});
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await props.fetchDataSummaryCalendar();
});
};
/**
* นทกแกไขวนหย
*/
const editData = async () => {
await formDate.value.validate().then(async (result: boolean) => {
if (result) {
const dataEdit = ref<DataDateAddObject[]>([]);
let i = 0;
const dateStart = ref<Date>(dateRange.value[0]);
do {
i = i + 1;
dataEdit.value.push({
year: new Date(dateStart.value).getFullYear(),
holidayDate: dateToISO(new Date(dateStart.value)),
name: name.value,
isSpecial: true,
});
dateStart.value = new Date(
new Date(dateStart.value).setDate(
new Date(dateStart.value).getDate() + 1
)
);
} while (new Date(dateStart.value) <= new Date(dateRange.value[1]));
const _dataHistory = ref<RequestItemsObject[]>([]);
dataHistory.value.map((e: RequestItemsObject, index: number) => {
_dataHistory.value.push({
...e,
holidayDate: dateToISO(new Date(e.holidayDate)),
});
});
showLoader();
await http
.post(config.API.listHolidayHistoryEdit(type.value), {
history: _dataHistory.value,
updated: dataEdit.value,
})
.then((res) => {
modalAdd.value = false;
success($q, "แก้ไขข้อมูลสำเร็จ");
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
}
});
};
/**
* นทกเพมวนหย
*/
const addDate = async () => {
await formDate.value.validate().then(async (result: boolean) => {
if (result) {
const dataAdd = ref<DataDateAddObject[]>([]);
let i = 0;
const dateStart = ref<Date>(dateRange.value[0]);
do {
i = i + 1;
dataAdd.value.push({
year: new Date(dateStart.value).getFullYear(),
holidayDate: dateToISO(new Date(dateStart.value)),
name: name.value,
isSpecial: true,
});
dateStart.value = new Date(
new Date(dateStart.value).setDate(
new Date(dateStart.value).getDate() + 1
)
);
} while (new Date(dateStart.value) <= new Date(dateRange.value[1]));
showLoader();
await http
.post(config.API.listHolidayHistoryAdd(category.value), dataAdd.value)
.then((res) => {
modalAdd.value = false;
success($q, "เพิ่มข้อมูลสำเร็จ");
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
}
});
};
/**
* ลบขอมลวนหย
*/
const deleteData = async () => {
modalDelete.value = false;
const dataDelete = ref<DataDateAddObject[]>([]);
let i = 0;
const dateStart = ref<Date>(dateRange.value[0]);
do {
i = i + 1;
dataDelete.value.push({
year: new Date(dateStart.value).getFullYear(),
holidayDate: dateToISO(new Date(dateStart.value)),
name: name.value,
isSpecial: true,
});
dateStart.value = new Date(
new Date(dateStart.value).setDate(new Date(dateStart.value).getDate() + 1)
);
} while (new Date(dateStart.value) <= new Date(dateRange.value[1]));
showLoader();
await http
.post(config.API.listHolidayHistoryDelete(type.value), dataDelete.value)
.then((res) => {
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
modalAdd.value = false;
await fetchData();
});
};
/**
* แปลงชวงวนทา2คาเปนวนเดยวกนจะโชววนเดยวแตาไมเทากนจะแสดงเปนชวง
* @param val วงวนท
*/
const dateThaiRange = (val: [Date, Date]) => {
if (val === null) {
} else if (date2Thai(val[0]) === date2Thai(val[1])) {
return `${date2Thai(val[0])}`;
} else {
return `${date2Thai(val[0])} - ${date2Thai(val[1])} `;
}
};
const getClass = (val: boolean) => {
return {
"full-width inputgreen cursor-pointer": val,
"full-width cursor-pointer": !val,
};
};
</script>
<style scope lang="scss">
.main-content {
height: 70vh;
}
.color-main {
color: #18a259;
}
.padding-content {
padding: 10px;
}
.demo-app-main {
flex-grow: 1;
/* padding: 3em; */
}
.fc {
/* the calendar root */
max-width: 1100px;
margin: 0 auto;
background-color: white;
border-radius: 10px;
}
.fc-day-today {
background-color: rgb(255, 255, 255) !important;
}
// .fc-day-sun {
// background-color: rgba(207, 205, 205, 0.177) !important;
// width: 80px;
// }
// .fc-day-sat {
// background-color: rgba(207, 205, 205, 0.177) !important;
// width: 80px;
// }
.fc-day-today .fc-daygrid-day-number {
display: flex;
justify-content: center;
align-items: center;
/* border: 2px solid #17a259; */
border-radius: 50%;
height: 25px;
width: 25px;
font-weight: bold;
color: white !important;
background: #17a259;
}
.fc-day-today .fc-daygrid-day-frame {
padding: 5%;
}
.fc .fc-button-group > .fc-button {
color: black;
background-color: #fafafa;
border: none;
}
.fc .fc-button-group > .fc-button:active {
color: white;
background-color: #22a15e;
border: none;
}
.fc .fc-button-group > .fc-button.fc-button-active {
color: white;
background-color: #22a15e;
border: none;
}
.fc-header-toolbar {
background-color: white;
padding: 0px 10px 0px 10px;
border-radius: 10px 10px 0px 0px;
}
.fc .fc-scrollgrid-liquid > thead {
background-color: #f8f8f8;
}
.dp-custom-cell {
border-radius: 50%;
}
.dp__today {
border: 1px solid var(--q-primary);
}
.dp__range_end,
.dp__range_start,
.dp__active_date {
background: var(--q-primary);
color: var(--dp-primary-text-color);
}
.datepicker .q-field__label {
padding-left: 5px;
}
.datepicker .q-field__messages {
padding-left: 20px;
}
.datepicker .q-field__native {
padding-left: 5px;
color: var(--q-primary) !important;
}
.datepicker .q-field__prepend {
padding-left: 6px;
}
.datepicker .q-field__append {
padding-right: 6px;
}
.datepicker .q-field__after {
display: flex;
justify-content: flex-end;
align-items: center;
font-weight: 500;
}
.fc .fc-popover {
z-index: 6000;
}
.fc-direction-ltr .fc-daygrid-event.fc-event-end,
.fc-direction-rtl .fc-daygrid-event.fc-event-start {
cursor: pointer;
}
.subName {
display: flex;
justify-content: flex-end;
align-items: center;
font-weight: 500;
}
.subInput {
display: flex;
align-items: center;
}
.fc-event {
overflow: hidden;
border-color: transparent !important;
font-weight: 500;
}
.fc-event-main {
text-align: center;
}
.fc-direction-ltr .fc-daygrid-event.fc-event-end,
.fc-direction-rtl .fc-daygrid-event.fc-event-start {
padding-left: 0px;
}
</style>

View file

@ -0,0 +1,651 @@
<!-- tab รายการ หนาปฏนวนหย -->
<template>
<q-card class="q-mt-md" flat bordered>
<!-- list รายการนหย -->
<q-tabs
dense
v-model="currentTab"
indicator-color="primary"
active-color="primary bg-teal-1"
class="text-body2 text-grey-7"
>
<q-tab
v-for="tab in tabs"
:key="tab.value"
v-on:click="changeTab(tab.value)"
:label="tab.label"
:name="tab.value"
class="q-py-xs col-6 row"
/>
</q-tabs>
<q-separator />
<q-tab-panels v-model="currentTab" animated>
<q-tab-panel name="normal">
<q-table
ref="table"
flat
bordered
class="custom-header-table"
virtual-scroll
:rows="calendarData"
:columns="columns"
dense
:rows-per-page-options="[0]"
hide-header
>
<template v-slot:body="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.name == 'week'" class="">
{{ dayThaiRange(props.row.dateRange) }}
</div>
<div v-else-if="col.name == 'holidayDate'" class="">
{{ dateThaiRange(props.row.dateRange) }}
</div>
<div v-else class="my-table-details">
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
flat
round
color="grey"
@click.stop
size="10px"
icon="more_vert"
>
<q-menu>
<q-list>
<q-item
clickable
v-close-popup
@click="editCalendar(props.row)"
>
<q-item-section>
<q-item-label>แกไขวนหย</q-item-label>
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="deleteClick(props.row)"
>
<q-item-section>
<q-item-label>ลบวนหย</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</q-td>
</q-tr>
</template>
</q-table>
</q-tab-panel>
<q-tab-panel name="6day">
<q-table
ref="table"
flat
bordered
class="custom-header-table"
virtual-scroll
:rows="calendarData"
:columns="columns"
dense
:rows-per-page-options="[0]"
hide-header
>
<template v-slot:body="props">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.name == 'week'" class="">
{{ dayThaiRange(props.row.dateRange) }}
</div>
<div v-else-if="col.name == 'holidayDate'" class="">
{{ dateThaiRange(props.row.dateRange) }}
</div>
<div v-else class="my-table-details">
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
flat
round
color="grey"
@click.stop
size="10px"
icon="more_vert"
>
<q-menu>
<q-list>
<q-item
clickable
v-close-popup
@click="editCalendar(props.row)"
>
<q-item-section>
<q-item-label>แกไขวนหย</q-item-label>
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="deleteClick(props.row)"
>
<q-item-section>
<q-item-label>ลบวนหย</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-menu>
</q-btn>
</q-td>
</q-tr>
</template>
</q-table>
</q-tab-panel>
</q-tab-panels>
</q-card>
<!-- modal เพมวนหย -->
<q-dialog v-model="modalAdd" persistent>
<q-card style="min-width: 550px">
<q-form ref="formDate" @submit.prevent.stop="editData">
<q-card-section class="row items-center q-pb-xs">
<div class="text-bold">แกไขวนหย</div>
<q-space />
<q-btn
icon="close"
unelevated
round
dense
v-close-popup
style="color: #ff8080; background-color: #ffdede"
/>
</q-card-section>
<q-separator />
<q-card-section class="q-p-sm">
<div class="row col-12 q-col-gutter-sm">
<div class="col-2 subName">
<label>เลอกวนท</label>
</div>
<div class="col-10">
<datepicker
v-model="dateAdd"
:locale="'th'"
autoApply
range
:enableTimePicker="false"
week-start="0"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
outlined
dense
class="full-width datepicker"
:model-value="dateThaiRange(dateAdd)"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</div>
<div class="row col-12 q-col-gutter-sm">
<div class="col-2 subName">
<label>คำอธบาย</label>
</div>
<div class="col-10">
<q-input
dense
borderless
class="full-width datepicker q-pb-none"
v-model="name"
type="textarea"
:rules="[(val) => (val && val.length > 0) || '']"
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="text-primary">
<q-btn unelevated label="บันทึก" color="public" type="submit">
<!-- icon="mdi-content-save-outline"
<q-tooltip>นท</q-tooltip> -->
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<!-- modal ลบวนหย -->
<q-dialog v-model="modalDelete" persistent>
<q-card style="min-width: 550px">
<q-card-section class="row items-center q-pb-xs">
<div class="text-bold">องการลบขอมลนหรอไม?</div>
<q-space />
<q-btn
icon="close"
unelevated
round
dense
v-close-popup
style="color: #ff8080; background-color: #ffdede"
/>
</q-card-section>
<q-separator />
<q-card-section class="row items-center">
<div class="q-pr-md">
<q-avatar
icon="mdi-trash-can-outline"
font-size="25px"
size="lg"
color="red-1"
text-color="red"
/>
</div>
<div class="col text-dark">
<span>อมลทกำลงถกลบนจะมผลใชงานทนท</span>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn label="ตกลง" color="primary" @click="deleteConfirm" />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import type {
RequestItemsObject,
DataDateRowObject,
DataDateAddObject,
DataDateListsObject,
TabsObject,
} from "@/modules/01_metadata/interface/index/Calendar";
import { useCounterMixin } from "@/stores/mixin";
const props = defineProps({
dateYear: {
//filter
type: Number,
required: true,
},
refreshData: {
// main refresh data
type: Boolean,
required: true,
},
fetchDataSummaryCalendar: {
//
type: Function,
default: () => console.log("not function"),
},
});
const mixin = useCounterMixin(); //
const {
success,
dateToISO,
dateMonth2Thai,
weekThai,
messageError,
showLoader,
hideLoader,
} = mixin;
const $q = useQuasar(); // noti quasar
const calendarData = ref<DataDateListsObject[]>([]); //data
const modalAdd = ref<boolean>(false); //modal
const modalDelete = ref<boolean>(false); //modal
const name = ref<string>(""); //
const isSpecial = ref<boolean>(true); //
const dateAdd = ref<[Date, Date]>([new Date(), new Date()]); //
const rowData = ref<DataDateListsObject>(); //data row
const formDate = ref<any>(); //ref validate
const currentTab = ref<string>("normal"); // tab
const tabs = ref<TabsObject[]>([
{ label: "ทำงานจันทร์-ศุกร์ (5 วัน)", value: "normal" },
{ label: "ทำงานจันทร์-เสาร์ (6 วัน)", value: "6day" },
]); //tab
//columns
const columns = ref<any>([
{
name: "week",
align: "left",
label: "-",
sortable: true,
field: "week",
style: "font-size: 15px",
},
{
name: "holidayDate",
align: "left",
label: "-",
sortable: true,
field: "holidayDate",
style: "font-size: 15px",
},
{
name: "detail",
align: "left",
label: "-",
sortable: true,
field: "detail",
style: "font-size: 15px",
},
]);
/**
* เรยกฟงกนทงหมดตอนเรยกใชไฟล
*/
onMounted(async () => {
await fetchData();
});
/**
* props(นปเลอก และเพมหรอแกไข) ตอนอพเดท าฏนใหพเดทใหม
*/
watch(props, async (count, prevCount) => {
await fetchData();
});
/**
* กดปมแกไขวนหย
* @param val data นหยดท row
*/
const editCalendar = async (val: DataDateListsObject) => {
rowData.value = val;
dateAdd.value = [val.dateRange[0], val.dateRange[1]];
name.value = val.detail;
isSpecial.value = true;
modalAdd.value = true;
};
/**
* กดปมลบวนหย
* @param val data นหยดท row
*/
const deleteClick = async (val: DataDateListsObject) => {
rowData.value = val;
modalDelete.value = true;
};
/**
* fetch นหยดในรายการ
*/
const fetchData = async () => {
calendarData.value = [];
showLoader();
await http
.get(config.API.listHolidayHistoryYear(props.dateYear))
.then((res) => {
let data = res.data.result.normal;
if (currentTab.value == "6day") {
data = res.data.result.sixDays;
}
const dateStart = ref<Date | null>();
const firstEvent = ref<boolean>(true);
const dateRow = ref<DataDateRowObject[]>([]);
data.map((e: RequestItemsObject, index: number) => {
dateRow.value.push({
holidayDate: new Date(e.holidayDate),
name: e.name,
isSpecial: true,
id: e.id,
});
if (
index == data.length - 1 ||
data[index + 1].name != e.name ||
(data[index + 1].name == e.name &&
dateToISO(new Date(data[index + 1].holidayDate)) !=
dateToISO(
new Date(
new Date(e.holidayDate).setDate(
new Date(e.holidayDate).getDate() + 1
)
)
))
) {
firstEvent.value = true;
calendarData.value.push({
id: e.id,
dateRange: [
dateStart.value ? dateStart.value : new Date(e.holidayDate),
new Date(e.holidayDate),
],
dataRangeRow: dateRow.value,
detail:
dateToISO(new Date(e.holidayDate)) ==
dateToISO(new Date(e.originalDate))
? e.name
: `ชดเชย ${e.name}`,
isSpecial: true,
});
dateStart.value = null;
dateRow.value = [];
} else if (firstEvent.value == true) {
firstEvent.value = false;
dateStart.value = new Date(e.holidayDate);
}
});
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await props.fetchDataSummaryCalendar();
});
};
/**
* ลบขอมลวนหย
*/
const deleteConfirm = async () => {
modalDelete.value = false;
const dataDelete = ref<DataDateAddObject[]>([]);
if (rowData.value != null) {
await rowData.value.dataRangeRow.map((e: DataDateRowObject) => {
dataDelete.value.push({
year: new Date(e.holidayDate).getFullYear(),
holidayDate: dateToISO(e.holidayDate),
name: e.name,
isSpecial: true,
});
});
} else {
return;
}
showLoader();
await http
.post(
config.API.listHolidayHistoryDelete(currentTab.value),
dataDelete.value
)
.then((res) => {
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
};
/**
* นทกแกไขวนหย
*/
const editData = async () => {
await formDate.value.validate().then(async (result: boolean) => {
if (result) {
const dataEdit = ref<DataDateAddObject[]>([]);
let i = 0;
const dateStart = ref<Date>(dateAdd.value[0]);
do {
i = i + 1;
dataEdit.value.push({
year: new Date(dateStart.value).getFullYear(),
holidayDate: dateToISO(new Date(dateStart.value)),
name: name.value,
isSpecial: true,
});
dateStart.value = new Date(
new Date(dateStart.value).setDate(
new Date(dateStart.value).getDate() + 1
)
);
} while (new Date(dateStart.value) <= new Date(dateAdd.value[1]));
const _dataHistory = ref<DataDateAddObject[]>([]);
if (rowData.value != null) {
rowData.value.dataRangeRow.map(
(e: DataDateRowObject, index: number) => {
_dataHistory.value.push({
year: new Date(e.holidayDate).getFullYear(),
holidayDate: dateToISO(e.holidayDate),
name: e.name,
isSpecial: true,
});
}
);
}
showLoader();
await http
.post(config.API.listHolidayHistoryEdit(currentTab.value), {
history: _dataHistory.value,
updated: dataEdit.value,
})
.then((res) => {
modalAdd.value = false;
success($q, "แก้ไขข้อมูลสำเร็จ");
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
await fetchData();
});
} else {
}
});
};
/**
* เปลยน tab นหยดตามประเภท
* @param tab tab ประเภทวนหยดทเลอก
*/
const changeTab = async (tab: string) => {
currentTab.value = tab;
await fetchData();
};
/**
* แปลงชวงวนทา2คาเปนวนเดยวกนจะโชววนเดยวแตาไมเทากนจะแสดงเปนชวง
* @param val วงวนท
*/
const dateThaiRange = (val: [Date, Date]) => {
if (val === null) {
return "";
} else if (dateMonth2Thai(val[0], true) === dateMonth2Thai(val[1], true)) {
return `${dateMonth2Thai(val[0], true)}`;
} else {
return `${dateMonth2Thai(val[0], true)} - ${dateMonth2Thai(val[1], true)}`;
}
};
/**
* แปลงชวงวนทา2คาเปนวนเดยวกนจะโชววนเดยวแตาไมเทากนจะแสดงเปนชวง(เชนวนจนทร -นศกร)
* @param val วงวนท
*/
const dayThaiRange = (val: [Date, Date]) => {
if (val === null) {
} else if (dateToISO(val[0]) == dateToISO(val[1])) {
return `${weekThai(new Date(val[0]).getDay())}`;
} else {
return `${weekThai(new Date(val[0]).getDay())} - ${weekThai(
new Date(val[1]).getDay()
)}`;
}
};
</script>
<style lang="scss" scope>
.custom-header-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;
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;
}
}
.my-table-details {
white-space: -moz-pre-wrap !important;
/* Mozilla, since 1999 */
white-space: -webkit-pre-wrap;
/* Chrome & Safari */
white-space: -pre-wrap;
/* Opera 4-6 */
white-space: -o-pre-wrap;
/* Opera 7 */
white-space: pre-wrap;
/* CSS3 */
word-wrap: break-word;
/* Internet Explorer 5.5+ */
word-break: break-all;
white-space: normal;
}
.table_ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: wrap;
max-width: 250px;
}
</style>

View file

@ -0,0 +1,589 @@
<!-- page:ดการขอมลหล tab นหย -->
<template>
<div>
<div class="row col-12 q-col-gutter-sm">
<div class="row items-center">
<!-- filter เลอกเดอนป -->
<datepicker
v-model="dateMonth"
:locale="'th'"
autoApply
month-picker
:enableTimePicker="false"
v-if="currentTab === 'calendar'"
@update:modelValue="updateMonth"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:model-value="monthYearThai(dateMonth)"
dense
outlined
style="width: 130px"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
<!-- filter เลอกป -->
<datepicker
v-model="dateYear"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
v-if="currentTab === 'list'"
@update:modelValue="updateYear"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:model-value="dateYear + 543"
dense
outlined
style="width: 100px"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
<div class="q-ml-sm">
<!-- icon เพมวนหย -->
<q-btn round dense flat size="13px" class="q-px-sm">
<q-icon
name="mdi-plus"
size="25px"
color="primary"
@click="addCalendar()"
/>
<q-tooltip>เพมวนหย</q-tooltip>
</q-btn>
<!-- icon ดลอกวนหย -->
<q-btn round dense flat size="13px" class="q-px-sm">
<q-icon
name="mdi-content-copy"
size="22px"
color="blue-6"
@click="copyCalendar()"
/>
<q-tooltip>ดลอกวนหย</q-tooltip>
</q-btn>
</div>
</div>
<q-space />
<div class="justify-center row q-gutter-md items-center">
<!-- tab ปแบบแสดงวนหย ปฏนกบรายการ -->
<q-tabs
v-model="currentTab"
indicator-color="transparent"
align="left"
active-color="activetab"
class="text-nativetab"
inline-label
dense
>
<q-btn
name="calendar"
round
flat
icon="mdi-calendar-month"
@click="currentTab = 'calendar'"
:color="currentTab == 'calendar' ? 'primary' : ''"
class="q-mr-sm"
>
<q-tooltip>ปฏ</q-tooltip>
</q-btn>
<q-separator vertical inset />
<q-btn
name="list"
round
flat
icon="mdi-format-list-bulleted"
@click="currentTab = 'list'"
:color="currentTab == 'list' ? 'primary' : ''"
class="q-ml-sm"
>
<q-tooltip>รายการ</q-tooltip>
</q-btn>
</q-tabs>
</div>
</div>
<div>
<!-- component ปฏนวนหย -->
<subCalendarComponent
v-if="currentTab === 'calendar'"
:dateYear="dateMonth.year"
:dateMonth="dateMonth.month"
:refreshData="refreshData"
:fetchDataSummaryCalendar="fetchDataSummaryCalendar"
/>
<!-- component รายการวนหย -->
<subCalendarListComponent
v-if="currentTab === 'list'"
:dateYear="dateYear"
:refreshData="refreshData"
:fetchDataSummaryCalendar="fetchDataSummaryCalendar"
/>
</div>
</div>
<!-- modal เพมวนหย -->
<q-dialog v-model="modalAdd" persistent>
<q-card style="min-width: 550px">
<q-form ref="formDate" @submit.prevent.stop="onSubmit">
<q-card-section class="row items-center q-py-sm">
<div class="text-bold">เพมวนหย</div>
<q-space />
<q-btn
icon="close"
unelevated
round
dense
v-close-popup
style="color: #ff8080; background-color: #ffdede"
/>
</q-card-section>
<q-separator />
<q-card-section class="q-p-md row q-gutter-y-sm">
<datepicker
v-model="dateAdd"
:locale="'th'"
autoApply
range
:enableTimePicker="false"
week-start="0"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
:model-value="dateThaiRange(dateAdd)"
outlined
label="เลือกวันที่"
dense
class="full-width datepicker"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
<q-input
dense
label="คำอธิบาย"
outlined
class="full-width datepicker q-pb-none"
v-model="name"
type="textarea"
:rules="[(val) => (val && val.length > 0) || '']"
/>
<q-option-group
dense
v-model="category"
:options="categoryOptions"
color="primary"
inline
/>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="text-primary">
<q-btn unelevated label="บันทึก" color="public" type="submit">
<!-- icon="mdi-content-save-outline"
<q-tooltip>นท</q-tooltip> -->
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<!-- modal ดลอกวนหย -->
<q-dialog v-model="modalCopy" persistent>
<q-card style="min-width: 500px">
<q-form @submit.prevent.stop="onSubmitCopy">
<q-card-section class="row items-center q-py-sm">
<div class="text-bold">ดลอกวนหยดปอนหน</div>
<q-space />
<q-btn
icon="close"
unelevated
round
dense
v-close-popup
style="color: #ff8080; background-color: #ffdede"
/>
</q-card-section>
<q-separator />
<q-card-section class="q-p-sm">
<div class="row col-12 q-col-gutter-sm">
<datepicker
v-model="dateYearStart"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:model-value="dateYearStart + 543"
dense
outlined
class="q-pb-sm"
label="ปีที่คัดลอกวันหยุด"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
<datepicker
v-model="dateYearEnd"
:locale="'th'"
autoApply
year-picker
:enableTimePicker="false"
:min-date="minDate"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:model-value="dateYearEnd + 543"
dense
outlined
label="ปีที่ลงวันหยุดคัดลอก"
class="q-pb-sm"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="text-primary">
<q-btn unelevated label="บันทึก" color="public" type="submit">
<!-- icon="mdi-content-save-outline"
<q-tooltip>นท</q-tooltip> -->
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<script setup lang="ts">
import { ref, watch, onMounted } from "vue";
import http from "@/plugins/http";
import config from "@/app.config";
import { useQuasar } from "quasar";
import type {
DataDateMonthObject,
DataDateAddObject,
} from "@/modules/01_metadata/interface/index/Calendar";
import subCalendarComponent from "@/modules/01_metadata/components/calendar/Calendar.vue";
import subCalendarListComponent from "@/modules/01_metadata/components/calendar/CalendarList.vue";
import { useCounterMixin } from "@/stores/mixin";
const props = defineProps({
dateYear: Number, // parent
fetchDataSummaryCalendar: {
//
type: Function,
default: () => console.log("not function"),
},
});
const $q = useQuasar(); // noti quasar
const mixin = useCounterMixin(); //
const {
success,
dateToISO,
monthYear2Thai,
date2Thai,
messageError,
dialogMessage,
showLoader,
hideLoader,
} = mixin;
const currentTab = ref<string>("calendar"); // tab calendar= list=
const modalAdd = ref<boolean>(false); // modal add calendar
const modalCopy = ref<boolean>(false); // modal copy calendar
const dateMonth = ref<DataDateMonthObject>({
month: new Date().getMonth(),
year: new Date().getFullYear(),
}); // tab
const dateYear = ref<number>(new Date().getFullYear()); // tab
const dateYearStart = ref<number>(new Date().getFullYear()); // copy
const dateYearEnd = ref<number>(new Date().getFullYear()); // copy
const dateAdd = ref<[Date, Date]>([new Date(), new Date()]); //
const formDate = ref<any>(); //ref validate
const name = ref<string>(""); //
const isSpecial = ref<boolean>(true); //
const category = ref<string>("all");
const categoryOptions = ref<any>([
{
label: "ทั้งหมด",
value: "all",
},
{
label: "ทำงาน 5 วัน",
value: "normal",
},
{
label: "ทำงาน 6 วัน",
value: "6days",
},
]);
const minDate = ref<Date>();
const refreshData = ref<boolean>(false); // component refresh data
const emit = defineEmits(["update:dateYear"]);
/**
* การเลอก datepicker ใหแปลงคาเป text แสดง
*/
watch(dateYearStart, () => {
dateYearEnd.value = dateYearStart.value + 1;
minDate.value = new Date(`${dateYearStart.value + 1}-01-01`);
});
/**
* เรยกฟงกนทงหมดตอนเรยกใชไฟล
*/
onMounted(async () => {
dateYearEnd.value = dateYearStart.value + 1;
minDate.value = new Date(`${dateYearStart.value + 1}-01-01`);
});
/**
* าเดอนปกเลอกใน component ปฏนจะใหพเดทคาสรปวนหยดรวม
* @param e เลอกเดอนป tab ปฏ
*/
const updateMonth = async (e: DataDateMonthObject) => {
if (e != null) {
dateYear.value = e.year;
emit("update:dateYear", e.year);
await props.fetchDataSummaryCalendar();
}
};
/**
* าปกเลอกใน component รายการจะใหพเดทคาสรปวนหยดรวม
* @param e เลอกป tab รายการ
*/
const updateYear = async (e: number) => {
dateMonth.value = { month: 0, year: e };
emit("update:dateYear", dateYear.value);
await props.fetchDataSummaryCalendar();
};
/**
* งกนปมเพมวนหยดแบบเลอกวนได
*/
const addCalendar = () => {
dateAdd.value = [new Date(), new Date()];
name.value = "";
category.value = "all";
isSpecial.value = true;
modalAdd.value = true;
};
/**
* งก copy นหยดจากปงไปอกป
*/
const copyCalendar = () => {
modalCopy.value = true;
dateYearStart.value = new Date().getFullYear();
dateYearEnd.value = new Date().getFullYear() + 1;
};
/**
* งกนปมบนทกเพมวนหย
*/
const onSubmit = async () => {
await formDate.value.validate().then(async (result: boolean) => {
if (result) {
const dataAdd = ref<DataDateAddObject[]>([]);
let i = 0;
const dateStart = ref<Date>(dateAdd.value[0]);
do {
i = i + 1;
dataAdd.value.push({
year: new Date(dateStart.value).getFullYear(),
holidayDate: dateToISO(new Date(dateStart.value)),
name: name.value,
isSpecial: true,
});
dateStart.value = new Date(
new Date(dateStart.value).setDate(
new Date(dateStart.value).getDate() + 1
)
);
} while (new Date(dateStart.value) <= new Date(dateAdd.value[1]));
showLoader();
await http
.post(config.API.listHolidayHistoryAdd(category.value), dataAdd.value)
.then(() => {
modalAdd.value = false;
success($q, "เพิ่มวันหยุดสำเร็จ");
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
refreshData.value = !refreshData.value;
hideLoader();
});
}
});
await props.fetchDataSummaryCalendar();
};
/**
* งกนคดลอกวนหย
*/
const onSubmitCopy = async () => {
if (dateYearStart.value >= dateYearEnd.value) {
dialogMessage(
$q,
"ไม่สามารถคัดลอกวันหยุดได้",
"ปีที่เริ่มต้นคัดลอกต้องมากกว่าปีที่จะลงวันคัดลอก",
"warning",
undefined,
"orange",
undefined,
undefined,
true
);
return;
}
showLoader();
await http
.post(config.API.listHolidayCopy, {
fromYear: dateYearStart.value,
toYear: dateYearEnd.value,
})
.then(() => {
modalCopy.value = false;
success($q, "คัดลอกวันหยุดสำเร็จ");
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
refreshData.value = !refreshData.value;
await props.fetchDataSummaryCalendar();
});
};
/**
* แปลง และเดอนเปนภาษาไทย
* @param val datepicker แบบเลอกปและเดอน
*/
const monthYearThai = (val: DataDateMonthObject) => {
if (val == null) return "";
else return monthYear2Thai(val.month, val.year);
};
/**
* แปลงชวงวนทา2คาเปนวนเดยวกนจะโชววนเดยวแตาไมเทากนจะแสดงเปนชวง
* @param val วงวนท
*/
const dateThaiRange = (val: [Date, Date] | []) => {
if (val.length === 0) {
} else if (date2Thai(val[0]) === date2Thai(val[1])) {
return `${date2Thai(val[0])}`;
} else {
return `${date2Thai(val[0])} - ${date2Thai(val[1])} `;
}
};
</script>
<style scoped>
.my-menu-link {
color: black;
background: #d0d0d0;
}
.my-list-link {
color: black;
border-radius: 5px;
background: #f4f4f4;
}
.my-list {
padding: 10px 10px;
}
.my-card {
width: 100%;
max-width: 160px;
}
.sub-card {
height: 100%;
max-height: 265px;
}
.cardNum {
border-radius: 5px;
}
.fc-direction-ltr .fc-daygrid-event.fc-event-end,
.fc-direction-rtl .fc-daygrid-event.fc-event-start {
padding-left: 5px;
}
</style>

View file

@ -0,0 +1,271 @@
<script setup lang="ts">
import { ref, onMounted, reactive, watch } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
import { useRouter } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
import type {
DataOption,
NewPagination,
} from "@/modules/01_metadata/interface/index/Main";
import type { FormQueryCapacity } from "@/modules/01_metadata/interface/request/Main";
import type { ResDataCapacity } from "@/modules/01_metadata/interface/response/Main";
import { useKPIDataStore } from "@/modules/01_metadata/stores/KPIStore";
const total = ref<number>();
const store = useKPIDataStore();
const router = useRouter();
const $q = useQuasar();
const mixin = useCounterMixin();
const { dialogRemove, messageError, showLoader, hideLoader, success } = mixin;
const competencyTypeOp = ref<DataOption[]>([
{
id: "HEAD",
name: "สมรรถนะหลัก",
},
{
id: "GROUP",
name: "สมรรถนะประจำกลุ่มงาน",
},
{
id: "EXECUTIVE",
name: "สมรรถนะประจำผู้บริหารกรุงเทพมหานคร",
},
{
id: "DIRECTOR",
name: "สมรรถนะเฉพาะสำหรับตำแหน่ง ผอ.เขต ผช.ผอ.เขต และหัวหน้าฝ่ายในสังกัด สนง.เขต",
},
{
id: "INSPECTOR",
name: "สมรรถนะเฉพาะสำหรับตำแหน่งผู้ตรวจราชการ กทม. และผู้ตรวจราชการ",
},
]);
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" }),
},
]);
const visibleColumns = ref<string[]>(["name"]);
const rows = ref<ResDataCapacity[]>([]);
const formQuery = reactive<FormQueryCapacity>({
page: 1,
pageSize: 10,
keyword: "",
});
const totalList = ref<number>(1); //
/** ดึงข้อมูล */
async function fetchList() {
showLoader();
await http
.get(
config.API.kpiCapacity +
`?page=${formQuery.page}&pageSize=${formQuery.pageSize}&keyword=${formQuery.keyword}&type=${store.competencyTypeVal}`
)
.then(async (res) => {
total.value = res.data.result.total;
const data: ResDataCapacity[] = res.data.result.data;
totalList.value = Math.ceil(res.data.result.total / formQuery.pageSize);
rows.value = data;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function onViewDetail(id: string) {
router.push(`/KPI-competency/${id}`);
}
function deleteData(id: string) {
dialogRemove($q, () => {
http
.delete(config.API.kpiCapacity + `/${id}`)
.then(() => {
fetchList();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
/** เปลี่ยนเป็นหน้าเพิ่มข้อมูล */
function onAdd() {
router.push(`/KPI-competency/add`);
}
function fetchNewList() {
formQuery.page = 1;
fetchList();
}
/**
* function updatePagination
* @param newPagination อม Pagination ใหม
*/
function updatePagination(newPagination: NewPagination) {
formQuery.page = 1;
formQuery.pageSize = newPagination.rowsPerPage;
}
watch(
() => formQuery.pageSize,
() => {
fetchNewList();
}
);
onMounted(() => {
fetchList();
});
</script>
<template>
<q-toolbar style="padding: 0">
<q-select
v-model="store.competencyTypeVal"
outlined
label="ประเภทสมรรถนะ"
dense
option-label="name"
option-value="id"
:options="competencyTypeOp"
style="min-width: 200px"
emit-value
map-options
@update:model-value="fetchNewList"
/>
<q-btn flat round color="primary" icon="add" @click="onAdd()">
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input
outlined
dense
v-model="formQuery.keyword"
label="ค้นหา"
@keyup.enter="fetchNewList()"
>
<template v-slot:append>
<q-icon v-if="formQuery.keyword == ''" name="search" />
<q-icon
v-if="formQuery.keyword !== ''"
name="clear"
class="cursor-pointer"
@click="(formQuery.keyword = ''), fetchNewList()"
/>
</template>
</q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="rows"
row-key="id"
flat
bordered
:paging="true"
dense
:rows-per-page-options="[10, 25, 50, 100]"
class="custom-header-table"
:visible-columns="visibleColumns"
@update:pagination="updatePagination"
>
<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">
{{ col.value }}
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
size="12px"
icon="edit"
clickable
@click.stop="onViewDetail(props.row.id)"
v-close-popup
>
<q-tooltip>แกไข</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="deleteData(props.row.id)"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
<template v-slot:pagination="scope">
งหมด {{ total }} รายการ
<q-pagination
v-model="formQuery.page"
active-color="primary"
color="dark"
:max="Number(totalList)"
size="sm"
boundary-links
direction-links
:max-pages="5"
@update:model-value="fetchList"
></q-pagination>
</template>
</d-table>
</template>

View file

@ -0,0 +1,354 @@
<script setup lang="ts">
import { ref, onMounted, reactive, watch } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
import dialogHeader from "@/components/DialogHeader.vue";
import type {
DataOption,
NewPagination,
} from "@/modules/14_KPI/interface/index/Main";
import http from "@/plugins/http";
import config from "@/app.config";
const total = ref<number>();
const modal = ref<boolean>(false);
const rows = ref<any[]>([]);
const groupName = ref<string>("");
const editStatus = ref<boolean>(false);
const editId = ref<string>("");
const competencyTypeOp = ref<DataOption[]>([
{
id: "ID1",
name: "สมรรถนะหลัก",
},
{
id: "ID2",
name: "สมรรถนะประจำกลุ่มงาน",
},
{
id: "ID3",
name: "สมรรถนะประจำผู้บริหารกรุงเทพมหานคร",
},
{
id: "ID4",
name: "สมรรถนะเฉพาะสำหรับตำแหน่ง ผอ.เขต ผช.ผอ.เขต และหัวหน้าฝ่ายในสังกัด สนง.เขต",
},
{
id: "ID5",
name: "สมรรถนะเฉพาะสำหรับตำแหน่งผู้ตรวจราชการ กทม. และผู้ตรวจราชการ",
},
]);
const columns = ref<QTableProps["columns"]>([
{
name: "nameGroupKPI",
align: "left",
label: "รายการกลุ่มงาน",
sortable: true,
field: "nameGroupKPI",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const $q = useQuasar();
const mixin = useCounterMixin();
const {
dialogRemove,
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
} = mixin;
const visibleColumns = ref<string[]>(["nameGroupKPI"]);
const formQuery = reactive({
page: 1,
pageSize: 10,
keyword: "",
});
const totalList = ref<number>(1); //
/** ดึงข้อมูล */
async function fetchData() {
showLoader();
await http
.get(
config.API.kpiGroup +
`?page=${formQuery.page}&pageSize=${formQuery.pageSize}&keyword=${formQuery.keyword}`
)
.then(async (res) => {
total.value = res.data.result.total;
const data = res.data.result;
totalList.value = Math.ceil(res.data.result.total / formQuery.pageSize);
rows.value = data.data;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
await http
.post(config.API.kpiGroup, {
nameGroupKPI: groupName.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(id: string) {
await http
.put(config.API.kpiGroupById(id), {
nameGroupKPI: groupName.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.kpiGroupById(id))
.then(() => {
fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** เปลี่ยนเป็นหน้าเพิ่มข้อมูล */
function onAdd() {
modal.value = true;
}
function closeDialog() {
modal.value = false;
editStatus.value = false;
groupName.value = "";
}
function onEdit(data: any) {
modal.value = true;
editStatus.value = true;
groupName.value = data.nameGroupKPI;
editId.value = data.id;
}
async function onSubmit() {
dialogConfirm(
$q,
async () => {
editStatus.value ? editData(editId.value) : addData();
closeDialog();
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
/**
* function updatePagination
* @param newPagination อม Pagination ใหม
*/
function updatePagination(newPagination: NewPagination) {
formQuery.page = 1;
formQuery.pageSize = newPagination.rowsPerPage;
}
function fetchNewList() {
formQuery.page = 1;
fetchData();
}
watch(
() => formQuery.pageSize,
() => {
fetchNewList();
}
);
onMounted(async () => {
fetchData();
});
</script>
<template>
<q-toolbar style="padding: 0">
<q-btn flat round color="primary" icon="add" @click="onAdd()">
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input
outlined
dense
v-model="formQuery.keyword"
label="ค้นหา"
@keyup.enter="fetchNewList()"
>
<template v-slot:append>
<q-icon v-if="formQuery.keyword == ''" name="search" />
<q-icon
v-if="formQuery.keyword !== ''"
name="clear"
class="cursor-pointer"
@click="(formQuery.keyword = ''), fetchNewList()"
/> </template
></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="rows"
row-key="id"
flat
bordered
:paging="true"
dense
class="custom-header-table"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumns"
@update:pagination="updatePagination"
>
<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">
{{ col.value }}
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click="onEdit(props.row)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
<template v-slot:pagination="scope">
งหมด {{ total }} รายการ
<q-pagination
v-model="formQuery.page"
active-color="primary"
color="dark"
:max="Number(totalList)"
size="sm"
boundary-links
direction-links
:max-pages="5"
@update:model-value="fetchData"
></q-pagination>
</template>
</d-table>
<q-dialog v-model="modal" persistent>
<q-card flat bordered style="min-width: 50vh">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<dialog-header
:tittle="editStatus ? 'แก้ไขกลุ่มงาน' : 'เพิ่มกลุ่มงาน'"
:close="closeDialog"
/>
<q-separator />
<q-card-section>
<q-input
label="กลุ่มงาน"
v-model="groupName"
outlined
dense
class="inputgreen"
:rules="[(val:string) => !!val || `${'กรุณากรอกกลุ่มงาน'}`,]"
hide-bottom-space
/>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn
type="submit"
for="#submitForm"
unelevated
dense
class="q-px-md items-center"
color="public"
label="บันทึก"
/>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>

View file

@ -0,0 +1,588 @@
<script setup lang="ts">
import { ref, onMounted, reactive, watch } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
import { useRouter } from "vue-router";
import Header from "@/components/DialogHeader.vue";
import type {
DataOption,
NewPagination,
} from "@/modules/01_metadata/interface/index/Main";
import http from "@/plugins/http";
import config from "@/app.config";
const total = ref<number>();
const id = ref<string>("");
const modal = ref<boolean>(false);
const rows = ref<any>([]);
const editStatus = ref<boolean>(false);
const groupName = ref<any>();
const position = ref<any>();
const competency = ref<any>();
const groupNameOp = ref<DataOption[]>([]);
const groupNameOpMain = ref<DataOption[]>([]);
const positionOp = ref<DataOption[]>([]);
const positionMainOp = ref<DataOption[]>([]);
const competencyOp = ref<DataOption[]>([]);
const competencyOpMain = ref<DataOption[]>([]);
const columns = ref<QTableProps["columns"]>([
{
name: "groupName",
align: "left",
label: "กลุ่มงาน",
sortable: true,
field: "groupName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "positions",
align: "left",
label: "ตำแหน่ง",
sortable: true,
field: "positions",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "capacitys",
align: "left",
label: "สมรรถนะประจำกลุ่มงาน",
sortable: true,
field: "capacitys",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const $q = useQuasar();
const mixin = useCounterMixin();
const {
dialogRemove,
messageError,
showLoader,
hideLoader,
success,
dialogConfirm,
} = mixin;
const competencyType = ref<string>("ID1");
const filterKeyword = ref<string>("");
const visibleColumns = ref<string[]>(["groupName", "positions", "capacitys"]);
const formQuery = reactive({
page: 1,
pageSize: 10,
keyword: "",
});
const totalList = ref<number>(1); //
/** ดึงข้อมูล */
async function getData() {
showLoader();
http
.get(
config.API.kpiLink +
`?page=${formQuery.page}&pageSize=${formQuery.pageSize}&keyword=${formQuery.keyword}`
)
.then((res) => {
total.value = res.data.result.total;
const data = res.data.result;
totalList.value = Math.ceil(res.data.result.total / formQuery.pageSize);
rows.value = data.data;
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.kpiLink + `/${id}`)
.then(() => {
success($q, "ลบข้อมูลสำเร็จ");
close();
getData();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** ดึงข้อมูล */
async function getListGroup() {
showLoader();
await http
.get(config.API.kpiGroup + `?pageSize=50`)
.then(async (res) => {
const dataOp = res.data.result.data;
const uniqueNames = new Set();
const filteredData = dataOp
.filter((item: any) => {
if (!uniqueNames.has(item.id)) {
uniqueNames.add(item.nameGroupKPI);
return true;
}
return false;
})
.map((item: any) => ({
id: item.id,
name: item.nameGroupKPI,
}));
groupNameOpMain.value = filteredData;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** ดึงข้อมูล */
async function getCompetency() {
showLoader();
await http
.get(config.API.kpiCapacity + `?type=GROUP&pageSize=50`)
.then(async (res) => {
const dataOp = res.data.result.data;
const uniqueNames = new Set();
const filteredData = dataOp
.filter((item: any) => {
if (!uniqueNames.has(item.id)) {
uniqueNames.add(item.name);
return true;
}
return false;
})
.map((item: any) => ({
id: item.id,
name: item.name,
}));
competencyOpMain.value = filteredData;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** เปลี่ยนเป็นหน้าเพิ่มข้อมูล */
function onAdd() {
getOptions();
getListGroup();
getCompetency();
modal.value = true;
}
async function onEdit(data: any) {
id.value = data;
await getOptions();
await getListGroup();
await getCompetency();
await getDataEdit(id.value);
modal.value = true;
editStatus.value = true;
}
function getDataEdit(id: string) {
showLoader();
http
.get(config.API.kpiLink + `/${id}`)
.then((res) => {
const data = res.data.result;
groupName.value = {
id: data.groupId,
name: data.groupName,
};
position.value = data.positions.map((i: any) => i.name);
competency.value = data.capacitys.map((i: any) => ({
id: i.id,
name: i.name,
}));
})
.finally(() => {
hideLoader();
});
}
function onSubmit() {
const url = editStatus.value
? config.API.kpiLink + `/${id.value}`
: config.API.kpiLink;
const body = {
kpiGroupId: groupName.value.id,
positions: position.value,
kpiCapacityIds: competency.value.map((i: any) => i.id),
};
dialogConfirm($q, () => {
http[editStatus.value ? "put" : "post"](url, body)
.then(() => {
success($q, "บันทึกสำเร็จ");
close();
getData();
})
.finally(() => {});
});
}
function close() {
modal.value = false;
editStatus.value = false;
groupName.value = "";
position.value = null;
competency.value = null;
}
function getOptions() {
http.get(config.API.orgSalaryPosition).then((res) => {
const dataOp = res.data.result;
const uniqueNames = new Set();
const filteredData = dataOp
.filter((item: any) => {
if (!uniqueNames.has(item.positionName)) {
uniqueNames.add(item.positionName);
return true;
}
return false;
})
.map((item: any) => ({
id: item.positionName,
name: item.positionName,
}));
positionMainOp.value = filteredData;
});
}
/**
* function นหาขอมลของ Option
* @param val าทองการฟลเตอร
* @param update พเดทค
* @param refData ดาตาทองการฟลเตอร
*/
function filterOptionGroup(val: any, update: Function) {
update(() => {
groupNameOp.value = groupNameOpMain.value.filter(
(v: any) => v.name.indexOf(val) > -1
);
});
}
/**
* function นหาขอมลของ Option
* @param val าทองการฟลเตอร
* @param update พเดทค
* @param refData ดาตาทองการฟลเตอร
*/
function filterOption(val: any, update: Function) {
update(() => {
positionOp.value = positionMainOp.value.filter(
(v: any) => v.name.indexOf(val) > -1
);
});
}
/**
* function นหาขอมลของ Option
* @param val าทองการฟลเตอร
* @param update พเดทค
* @param refData ดาตาทองการฟลเตอร
*/
function filterOptionCompetency(val: any, update: Function) {
update(() => {
competencyOp.value = competencyOpMain.value.filter(
(v: any) => v.name.indexOf(val) > -1
);
});
}
/**
* function updatePagination
* @param newPagination อม Pagination ใหม
*/
function updatePagination(newPagination: NewPagination) {
formQuery.page = 1;
formQuery.pageSize = newPagination.rowsPerPage;
}
function fetchNewList() {
formQuery.page = 1;
getData();
}
watch(
() => formQuery.pageSize,
() => {
fetchNewList();
}
);
onMounted(async () => {
getData();
});
</script>
<template>
<q-toolbar style="padding: 0">
<q-btn flat round color="primary" icon="add" @click="onAdd()">
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input
outlined
dense
v-model="formQuery.keyword"
label="ค้นหา"
@keyup.enter="fetchNewList()"
>
<template v-slot:append>
<q-icon v-if="formQuery.keyword == ''" name="search" />
<q-icon
v-if="formQuery.keyword !== ''"
name="clear"
class="cursor-pointer"
@click="(formQuery.keyword = ''), fetchNewList()"
/> </template
></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
>
</q-select>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="rows"
row-key="id"
flat
bordered
dense
class="custom-header-table"
:visible-columns="visibleColumns"
:separator="'cell'"
:rows-per-page-options="[10, 25, 50, 100]"
@update:pagination="updatePagination"
>
<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 == 'positions'">
<div v-if="col.value.length !== 0">
<div v-for="(pos, index) in col.value" :key="pos.id">
{{ pos ? `- ${pos.name}` : "-" }}
</div>
</div>
<div v-else>-</div>
</div>
<div v-else-if="col.name == 'capacitys'">
<div v-if="col.value.length !== 0">
<div v-for="competency in col.value" :key="competency.id">
{{ competency ? `- ${competency.name}` : "-" }}
</div>
</div>
<div v-else>-</div>
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click="onEdit(props.row.id)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
<template v-slot:pagination="scope">
งหมด {{ total }} รายการ
<q-pagination
v-model="formQuery.page"
active-color="primary"
color="dark"
:max="Number(totalList)"
size="sm"
boundary-links
direction-links
:max-pages="5"
@update:model-value="getData"
></q-pagination>
</template>
</d-table>
<q-dialog v-model="modal" persistent>
<q-card flat bordered style="min-width: 80vh">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<Header
:tittle="
editStatus ? 'แก้ไขกลุ่มงานและตำแหน่ง' : 'เพิ่มกลุ่มงานและตำแหน่ง'
"
:close="close"
/>
<q-separator />
<q-card-section>
<div class="row q-col-gutter-sm">
<div class="col-4">
<q-select
dense
v-model="groupName"
label="กลุ่มงาน"
outlined
class="inputgreen"
map-options
option-label="name"
option-value="id"
:options="groupNameOp"
use-input
@filter="(inputValue:any,doneFn:Function) => filterOptionGroup(inputValue, doneFn) "
hide-bottom-space
lazy-rules
:rules="[(val:string) => !!val || `${'กรุณาเลือกกลุ่มงาน'}`,]"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไมอม
</q-item-section>
</q-item>
</template></q-select
>
</div>
<div class="col-4">
<q-select
dense
v-model="position"
label="ตำแหน่ง"
outlined
multiple
emit-value
map-options
class="inputgreen"
option-label="name"
option-value="name"
:options="positionOp"
:rules="[(val:string) => !!val || `${'กรุณาเลือกตำแหน่ง'}`,]"
hide-bottom-space
lazy-rules
use-input
@filter="(inputValue:any,doneFn:Function) => filterOption(inputValue, doneFn) "
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไมอม
</q-item-section>
</q-item>
</template></q-select
>
</div>
<div class="col-4">
<q-select
dense
class="inputgreen"
v-model="competency"
label="สมรรถนะประจำกลุ่ม"
outlined
multiple
map-options
option-label="name"
option-value="id"
:rules="[(val:string) => !!val || `${'กรุณาเลือกสมรรถนะประจำกลุ่ม'}`,]"
hide-bottom-space
lazy-rules
:options="competencyOp"
use-input
@filter="(inputValue:any,doneFn:Function) => filterOptionCompetency(inputValue, doneFn) "
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไมอม
</q-item-section>
</q-item>
</template></q-select
>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn
type="submit"
for="#submitForm"
unelevated
dense
class="q-px-md items-center"
color="public"
label="บันทึก"
/>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>

View file

@ -0,0 +1,153 @@
<script setup lang="ts">
import { ref, reactive, onMounted, watch } from "vue";
import { useCounterMixin } from "@/stores/mixin";
import http from "@/plugins/http";
import config from "@/app.config";
import { useQuasar } from "quasar";
const $q = useQuasar();
const dataLevel = ref<any>();
const { showLoader, hideLoader, success } = useCounterMixin();
const fieldLabels = {
score5: "5",
score4: "4",
score3: "3",
score2: "2",
score1: "1",
};
const formScore = reactive<any>({
score5: "",
score4: "",
score3: "",
score2: "",
score1: "",
});
interface FormScore {
score5: string;
score4: string;
score3: string;
score2: string;
score1: string;
[key: string]: any;
}
function onSubmit() {
const body = {
formScore: dataLevel.value.map((item: any) => {
const { level, ...rest } = item;
return rest;
}),
};
http
.put(config.API.kpiEvaluation, body.formScore)
.then((res) => {
success($q, "บันทึกสำเร็จ");
})
.finally(() => {});
}
function getData() {
showLoader();
http
.get(config.API.kpiEvaluation)
.then((res) => {
dataLevel.value = res.data.result.data;
})
.finally(() => {
hideLoader();
});
}
onMounted(() => {
getData();
});
</script>
<template>
<q-card flat bordered>
<q-form greedy @submit.prevent @validation-success="onSubmit">
<q-card-section class="bg-grey-3 q-pa-sm">
<div class="row text-dark text-body2 text-weight-medium">
<div class="text-center col-8">เกณฑการประเม</div>
<div class="text-center col-4">ระดบคะแนน</div>
</div>
</q-card-section>
<q-card-section class="q-pa-none">
<div v-for="(field, index) in dataLevel" :key="field.id">
<div class="row q-pa-sm">
<div class="col-8 text-left">
<q-field
class="q_field_p_none"
ref="fieldRef"
v-model="field.description"
label-slot
borderless
:rules="[(val) => !!val || 'กรุณากรอกมาตรฐานพฤติกรรม']"
hide-bottom-space
>
<template #control>
<q-editor
class="full-width"
v-model="field.description"
:dense="$q.screen.lt.md"
min-height="5rem"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
:fonts="{
arial: 'Arial',
arial_black: 'Arial Black',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
}"
/>
</template>
</q-field>
<!-- <q-input
v-model="formScore[field]"
dense
outlined
class="inputgreen"
label="กรอกข้อความเพื่อไว้ใช้อ้างอิงเท่านั้น"
:rules="[(val:string) => !!val || `${'กรุณากรอกข้อความเพื่อไว้ใช้อ้างอิงเท่านั้น'}`,]"
hide-bottom-space
/> -->
</div>
<div
class="col-4 text-center text-body1 text-weight-bold self-center"
>
{{ field.level }}
</div>
</div>
<div
class="col-12"
v-if="index !== Object.keys(fieldLabels).length - 1"
>
<q-separator />
</div>
</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>
</template>

View file

@ -0,0 +1,328 @@
<script setup lang="ts"></script>
<template>
<div class="display-table">
<q-card flat bordered class="q-mb-lg">
<q-card-section>
<div class="text-h6 text-bold">ประเภทสมรรถนะ</div>
</q-card-section>
<q-card-section class="q-pt-none">
ใหประเมนจากสมรรถนะทเกยวของกบการปฏราชการตามท .. กำหนด
รายละเอยดดงน
</q-card-section>
<q-separator inset />
<div style="border: 1px solid black">
<div class="row text-center" style="background-color: #e0e4f4">
<div class="col-3 text-bold flex justify-center items-center">
ประเภทสมรรถนะ
</div>
<div class="col-5">
<div class="row text-bold flex justify-center">
ประเภทและระดบตำแหน
</div>
<div class="row">
<div class="col-3">
<div class="text-bold">วไป</div>
<div>ปฏงาน</div>
<div>ชำนาญงาน</div>
<div>อาวโส</div>
</div>
<div class="col-3">
<div class="text-bold">ชาการ</div>
<div>ปฏการ</div>
<div>ชำนาญการ</div>
<div>ชำนาญการพเศษ</div>
<div>เชยวชาญ</div>
<div>ทรงคณว</div>
</div>
<div class="col-3">
<div class="text-bold">อำนวยการ</div>
<div> </div>
</div>
<div class="col-3">
<div class="text-bold">บรหาร</div>
<div> </div>
</div>
</div>
</div>
<div class="col-4 column">
<div class="row text-bold flex justify-center">ดำรงตำแหน</div>
<div class="row" style="flex-grow: 1">
<div class="col-6">
ตรวจราชการกรงเทพมหานครและผตรวจราชการ
</div>
<div class="col-6 flex justify-center">อำนวยการเขต</div>
</div>
</div>
</div>
<div class="row">
<div class="col-3 q-pa-sm">
<div class="text-bold">สมรรถนะหล</div>
<div>(5 สมรรถนะ)</div>
</div>
<div class="col-5 column">
<div class="row col-grow">
<div class="col-3 flex items-center justify-center">
<q-icon name="check" size="32px" />
</div>
<div class="col-3 flex items-center justify-center">
<q-icon name="check" size="32px" />
</div>
<div class="col-3 flex items-center justify-center">
<q-icon name="check" size="32px" />
</div>
<div class="col-3 flex items-center justify-center">
<q-icon name="check" size="32px" />
</div>
</div>
</div>
<div class="col-4 column">
<div class="row col-grow">
<div class="col-6 flex items-center justify-center">
<q-icon name="check" size="32px" />
</div>
<div class="col-6 flex items-center justify-center">
<q-icon name="check" size="32px" />
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-3 q-pa-sm">
<div class="text-bold">สมรรถนะประจำกลมงาน</div>
<div>(3 สมรรถนะ ตามกลมงาน)</div>
</div>
<div class="col-5 column">
<div class="row col-grow">
<div class="col-3 flex items-center justify-center">
<q-icon name="check" size="32px" />
</div>
<div class="col-3 flex items-center justify-center">
<q-icon name="check" size="32px" />
</div>
<div class="col-3"></div>
<div class="col-3"></div>
</div>
</div>
<div class="col-4 column">
<div class="row col-grow">
<div class="col-6"></div>
<div class="col-6"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-3 q-pa-sm">
<div class="text-bold">สมรรถนะประจำผบรหารกรงเทพมหานคร</div>
<div>(7 สมรรถนะ)</div>
</div>
<div class="col-5 column">
<div class="row col-grow">
<div class="col-3"></div>
<div class="col-3"></div>
<div class="col-3 flex items-center justify-center">
<q-icon name="check" size="32px" />
</div>
<div class="col-3 flex items-center justify-center">
<q-icon name="check" size="32px" />
</div>
</div>
</div>
<div class="col-4 column">
<div class="row col-grow">
<div class="col-6"></div>
<div class="col-6"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-3 q-pa-sm">
<div class="text-bold">สมรรถนะเฉพาะสำหรบตำแหนงผอำนวยการเขต</div>
<div>(4 สมรรถนะ)</div>
</div>
<div class="col-5 column">
<div class="row col-grow">
<div class="col-3"></div>
<div class="col-3"></div>
<div class="col-3"></div>
<div class="col-3"></div>
</div>
</div>
<div class="col-4 column">
<div class="row col-grow">
<div class="col-6"></div>
<div class="col-6 flex items-center justify-center">
<q-icon name="check" size="32px" />
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-3 q-pa-sm">
<div class="text-bold">
สมรรถนะเฉพาะสำหรบตำแหนงผตรวจราชการกรงเทพมหานครและผตรวจราชการ
</div>
<div>(2 สมรรถนะ)</div>
</div>
<div class="col-5 column">
<div class="row col-grow">
<div class="col-3"></div>
<div class="col-3"></div>
<div class="col-3"></div>
<div class="col-3"></div>
</div>
</div>
<div class="col-4 column">
<div class="row col-grow">
<div class="col-6 flex items-center justify-center">
<q-icon name="check" size="32px" />
</div>
<div class="col-6"></div>
</div>
</div>
</div>
<div class="row text-bold">
<div class="col-3 q-pa-sm">
<div class="text-bold flex justify-end">รวมสมรรถนะ</div>
</div>
<div class="col-5 column">
<div class="row col-grow">
<div class="col-3 flex justify-center items-center">8</div>
<div class="col-3 flex justify-center items-center">8</div>
<div class="col-3 flex justify-center items-center">12</div>
<div class="col-3 flex justify-center items-center">12</div>
</div>
</div>
<div class="col-4 column">
<div class="row col-grow">
<div class="col-6 flex justify-center items-center">7</div>
<div class="col-6 flex justify-center items-center">9</div>
</div>
</div>
</div>
</div>
</q-card>
<q-card flat bordered>
<q-card-section>
<div class="text-h6 text-bold">ระดบสมรรถนะ</div>
</q-card-section>
<q-card-section class="q-pt-none">
สมรรถนะหลกและสมรรถนะประจำกลมงาน
กำหนดระดบสมรรถนะทคาดหวงจำแนกตามประเภทและระดบตำแหนงของขาราชการกรงเทพมหานครสาม
รายละเอยดดงน
</q-card-section>
<q-separator inset />
<div style="border: 1px solid black">
<div
class="row text-bold text-center"
style="background-color: #c0d4ec"
>
<div class="col-3">ประเภทตำแหน</div>
<div class="col-3">ระดบตำแหน</div>
<div class="col-3">สมรรถนะหล</div>
<div class="col-3">สมรรถนะประจำกลมงาน</div>
</div>
<div class="row text-center">
<div class="col-3 flex justify-center items-center">บรหาร</div>
<div class="col-6">
<div class="row">
<div class="col-6"></div>
<div class="col-6">5</div>
</div>
<div class="row">
<div class="col-6"></div>
<div class="col-6">4</div>
</div>
</div>
<div class="col-3" style="background-color: #bcbcc4"></div>
</div>
<div class="row text-center">
<div class="col-3 flex justify-center items-center">อำนวยการ</div>
<div class="col-6">
<div class="row">
<div class="col-6"></div>
<div class="col-6">4</div>
</div>
<div class="row">
<div class="col-6"></div>
<div class="col-6">3</div>
</div>
</div>
<div class="col-3" style="background-color: #bcbcc4"></div>
</div>
<div class="row text-center">
<div class="col-3 flex justify-center items-center">ชาการ</div>
<div class="col-9">
<div class="row">
<div class="col-4">ทรงคณว</div>
<div class="col-4">5</div>
<div class="col-4">5</div>
</div>
<div class="row">
<div class="col-4">เชยวชาญ</div>
<div class="col-4">4</div>
<div class="col-4">4</div>
</div>
<div class="row">
<div class="col-4">ชำนาญการพเศษ</div>
<div class="col-4">3</div>
<div class="col-4">4</div>
</div>
<div class="row">
<div class="col-4">ชำนาญการ</div>
<div class="col-4">2</div>
<div class="col-4">3</div>
</div>
<div class="row">
<div class="col-4">ปฏการ</div>
<div class="col-4">1</div>
<div class="col-4">2</div>
</div>
</div>
</div>
<div class="row text-center">
<div class="col-3 flex justify-center items-center">วไป</div>
<div class="col-9">
<div class="row">
<div class="col-4">กษะพเศษ</div>
<div class="col-4">4</div>
<div class="col-4">4</div>
</div>
<div class="row">
<div class="col-4">อาวโส</div>
<div class="col-4">3</div>
<div class="col-4">3</div>
</div>
<div class="row">
<div class="col-4">ชำนาญงาน</div>
<div class="col-4">2</div>
<div class="col-4">2</div>
</div>
<div class="row">
<div class="col-4">ปฏงาน</div>
<div class="col-4">1</div>
<div class="col-4">1</div>
</div>
</div>
</div>
</div>
</q-card>
</div>
</template>
<style scoped lang="scss">
.display-table {
& .row:not(:last-child) {
border-bottom: 1px solid black;
}
& [class^="col-"]:not(:last-child) {
border-right: 1px solid black;
}
}
</style>

View file

@ -0,0 +1,121 @@
<script setup lang="ts">
import { ref, reactive } from "vue";
import { useRouter } from "vue-router";
import Main from "@/modules/01_metadata/components/competency/Forms/Main.vue";
// import FormMain from "@/modules/14_KPI/components/competency/Forms/01_FormMain.vue";
// import FormGroup from "@/modules/14_KPI/components/competency/Forms/02_FormGroup.vue";
// import FormExecutive from "@/modules/14_KPI/components/competency/Forms/03_FormExecutive.vue";
// import FormExecutivePosition from "@/modules/14_KPI/components/competency/Forms/04_FormExecutivePosition.vue";
// import FormExecutiveLevel from "@/modules/14_KPI/components/competency/Forms/05_FormExecutiveLevel.vue";
import { useKPIDataStore } from "@/modules/01_metadata/stores/KPIStore";
import type { DataOption } from "@/modules/01_metadata/interface/index/Main";
import type { FormCompetency } from "@/modules/01_metadata/interface/request/Main";
const router = useRouter();
const store = useKPIDataStore();
const formData = reactive<FormCompetency>({
competencyType: "",
competencyName: "",
definition: "",
level_1: ["", "", "", "", "", ""],
level_2: "",
level_3: "",
level_4: "",
level_5: "",
evaluation: "",
});
const competencyTypeOp = ref<DataOption[]>([
{
id: "HEAD",
name: "สมรรถนะหลัก",
},
{
id: "GROUP",
name: "สมรรถนะประจำกลุ่มงาน",
},
{
id: "EXECUTIVE",
name: "สมรรถนะประจำผู้บริหารกรุงเทพมหานคร",
},
{
id: "DIRECTOR",
name: "สมรรถนะเฉพาะสำหรับตำแหน่ง ผอ.เขต ผช.ผอ.เขต และหัวหน้าฝ่ายในสังกัด สนง.เขต",
},
{
id: "INSPECTOR",
name: "สมรรถนะเฉพาะสำหรับตำแหน่งผู้ตรวจราชการ กทม. และผู้ตรวจราชการ",
},
]);
const itemsFormCard = ref<any>([
{
id: "",
name: "",
},
]);
function ocClickAdd() {
if (itemsFormCard.value.length !== 6) {
const data = {
id: "",
name: "",
};
itemsFormCard.value.push(data);
}
}
/** บันทึก */
function onSubmit() {
console.log(formData);
}
</script>
<template>
<div>
<q-btn
icon="mdi-arrow-left"
unelevated
round
dense
flat
color="primary"
class="q-mr-sm"
@click="router.go(-1)"
/>
<span class="toptitle text-dark">เพ/แกไขสมรรถนะ</span>
</div>
<q-card flat bordered>
<!-- @submit.prevent @validation-success="onSubmit" -->
<!-- <q-form greedy> -->
<q-card-section>
<div class="row q-col-gutter-sm">
<div class="col-12">
<q-select
v-model="store.competencyTypeVal"
outlined
label="เลือกประเภทสมรรถนะ"
dense
option-label="name"
option-value="id"
:options="competencyTypeOp"
emit-value
map-options
:rules="[(val:string) => !!val || `${'กรุณาเลือกประเภทสมรรถนะ'}`,]"
hide-bottom-space
/>
</div>
<Main />
<!-- <FormMain v-if="store.competencyType === 'ID1'" />
<FormGroup v-else-if="store.competencyType === 'ID2'" />
<FormExecutive v-else-if="store.competencyType === 'ID3'" />
<FormExecutivePosition v-else-if="store.competencyType === 'ID4'" />
<FormExecutiveLevel v-else-if="store.competencyType === 'ID5'" /> -->
</div>
</q-card-section>
</q-card>
</template>

View file

@ -0,0 +1,205 @@
<script setup lang="ts">
import { reactive } from "vue";
import { useCounterMixin } from "@/stores/mixin";
import { useKPIDataStore } from "@/modules/01_metadata/stores/KPIStore";
import { useRouter } from "vue-router";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
const $q = useQuasar();
const mixin = useCounterMixin();
const { dialogConfirm, showLoader, hideLoader, success, messageError } = mixin;
const router = useRouter();
const store = useKPIDataStore();
const formData = reactive({
competencyType: "",
competencyName: "",
definition: "",
levels: [
{
level: "1",
description: "",
},
],
evaluation: "",
});
function ocClickAdd() {
if (formData.levels.length !== 6) {
const levelName = formData.levels.length + 1;
const data = {
level: levelName.toString(),
description: "",
};
formData.levels.push(data);
}
}
function onSubmit() {
dialogConfirm($q, () => {
const body = {
competencyType: store.competencyTypeVal,
competencyName: formData.competencyName,
definition: formData.definition,
levels: formData.levels,
evaluation: formData.evaluation,
};
// showLoader()
// http
// .put(config.API.???,body)
// .then((res)=>{
// success($q,'')
// router.push(`/KPI-competency`)
// }).catch((e)=>{
// messageError($q,e)
// }).finally(()=>{
// hideLoader()
// })
console.log(body);
});
}
</script>
<template>
<q-form greedy @submit.prevent @validation-success="onSubmit" class="col-12">
<div class="row q-col-gutter-sm">
<div class="col-12">
<q-input
v-model="formData.competencyName"
dense
outlined
label="ชื่อสมรรถนะ"
:rules="[(val:string) => !!val || `${'กรุณากรอกชื่อสมรรถนะ'}`,]"
hide-bottom-space
/>
</div>
<div class="col-12">
<q-input
v-model="formData.definition"
label="คำจำกัดความ"
dense
type="textarea"
outlined
:rules="[(val:string) => !!val || `${'กรุณากรอกคำจำกัดความ'}`,]"
hide-bottom-space
/>
</div>
<div class="col-12">
<q-card flat bordered>
<q-card-section class="bg-grey-4">
<div
class="row items-center text-dark text-body2 text-weight-medium"
>
<div class="col-3">
<div class="row items-center">
<div class="col-1">
<q-btn
dense
flat
round
color="primary"
icon="add"
@click="ocClickAdd"
>
<q-tooltip>เพ</q-tooltip></q-btn
>
</div>
<div class="col-11 text-center">
<span>ระด</span>
</div>
</div>
</div>
<div class="col-4">
<span>คำอธบายระด</span>
</div>
</div>
</q-card-section>
<q-card-section>
<div
class="row q-pa-sm"
v-for="(items, index) in formData.levels"
key="index"
>
<div
class="col-3 text-center self-center text-body1 text-weight-medium"
>
<span>{{ items.level }}</span>
</div>
<div class="col-9">
<q-field
class="q_field_p_none"
ref="fieldRef"
v-model="formData.levels[index].description"
label-slot
borderless
:rules="[(val) => !!val || 'กรุณากรอกมาตรฐานพฤติกรรม']"
hide-bottom-space
>
<template #control>
<q-editor
class="full-width"
v-model="formData.levels[index].description"
:dense="$q.screen.lt.md"
min-height="5rem"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
:fonts="{
arial: 'Arial',
arial_black: 'Arial Black',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
}"
/>
</template>
</q-field>
</div>
</div>
</q-card-section>
</q-card>
</div>
<div class="col-12">
<q-input
v-model="formData.evaluation"
label="กำหนดเกณฑ์การประเมิน"
dense
type="textarea"
outlined
/>
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12 row justify-end">
<q-btn
dense
unelevated
label="บันทึก"
color="public"
class="q-px-md"
type="submit"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</div>
</div>
</q-form>
</template>
<style scoped></style>

View file

@ -0,0 +1,204 @@
<script setup lang="ts">
import { ref, reactive } from "vue";
import { useCounterMixin } from "@/stores/mixin";
import { useKPIDataStore } from "@/modules/01_metadata/stores/KPIStore";
import { useRouter } from "vue-router";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
const $q = useQuasar();
const mixin = useCounterMixin();
const { dialogConfirm, showLoader, hideLoader, success, messageError } = mixin;
const router = useRouter;
const store = useKPIDataStore();
interface DetailLevelType {
level_01: any;
level_02: any;
level_03: any;
level_04: any;
level_05: any;
}
interface FormGroup {
name: string;
definition: string;
detailLevel: DetailLevelType[];
evaluation: string;
}
const fieldLabels = {
score1: "1",
score2: "2",
score3: "3",
score4: "4",
score5: "5",
};
const formScore = reactive<any>({
score1: "",
score2: "",
score3: "",
score4: "",
score5: "",
});
const formData = reactive<FormGroup>({
name: "",
definition: "",
detailLevel: [],
evaluation: "",
});
function onSubmit() {
dialogConfirm($q, () => {
const body = {
competencyType: store.competencyTypeVal,
competencyName: formData.definition,
definition: formData.definition,
levels1: formScore.score1,
levels2: formScore.score2,
levels3: formScore.score3,
levels4: formScore.score4,
levels5: formScore.score5,
evaluation: formData.evaluation,
};
// showLoader()
// http
// .put(config.API.???,body)
// .then((res)=>{
// success($q,'')
// router.push(`/KPI-competency`)
// }).catch((e)=>{
// messageError($q,e)
// }).finally(()=>{
// hideLoader()
// })
console.log(body);
});
}
</script>
<template>
<q-form greedy @submit.prevent @validation-success="onSubmit" class="col-12">
<div class="row q-col-gutter-sm">
<div class="col-12">
<q-input
label="ชื่อสมรรถนะ"
outlined
v-model="formData.name"
dense
:rules="[(val:string) => !!val || `${'กรุณากรอกชื่อสมรรถนะ'}`,]"
hide-bottom-space
/>
</div>
<div class="col-12">
<q-input
label="คำจำกัดความ"
outlined
v-model="formData.definition"
dense
type="textarea"
:rules="[(val:string) => !!val || `${'กรุณากรอกคำจำกัดความ'}`,]"
hide-bottom-space
/>
</div>
<div class="col-12">
<q-card flat bordered>
<q-card-section class="bg-grey-4 q-pa-sm">
<div class="row text-dark text-body2 text-weight-medium">
<div class="text-center col-4">ระด</div>
<div class="col-8">มาตรฐานพฤตกรรม</div>
</div>
</q-card-section>
<q-card-section class="q-pa-none">
<div
v-for="(field, index) in Object.keys(fieldLabels)"
:key="index + 1"
>
<div class="row q-pa-sm">
<div
class="col-4 text-center text-body1 text-weight-bold self-center"
>
{{ fieldLabels[field as keyof typeof fieldLabels] }}
</div>
<div class="col-8 text-left">
<q-field
class="q_field_p_none"
ref="fieldRef"
v-model="formScore[field]"
label-slot
borderless
:rules="[(val) => !!val || 'กรุณากรอกมาตรฐานพฤติกรรม']"
hide-bottom-space
>
<template #control>
<q-editor
class="full-width"
v-model="formScore[field]"
:dense="$q.screen.lt.md"
min-height="5rem"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
:fonts="{
arial: 'Arial',
arial_black: 'Arial Black',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
}"
/>
</template>
</q-field>
</div>
</div>
<div
class="col-12"
v-if="index !== Object.keys(fieldLabels).length - 1"
>
<q-separator />
</div>
</div>
</q-card-section>
</q-card>
</div>
<div class="col-12">
<q-input
v-model="formData.evaluation"
label="กำหนดเกณฑ์การประเมิน"
dense
type="textarea"
outlined
/>
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12 row justify-end">
<q-btn
dense
unelevated
label="บันทึก"
color="public"
class="q-px-md"
type="submit"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</div>
</div>
</q-form>
</template>

View file

@ -0,0 +1,321 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { useCounterMixin } from "@/stores/mixin";
import { useKPIDataStore } from "@/modules/01_metadata/stores/KPIStore";
import { useRouter } from "vue-router";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
const $q = useQuasar();
const { dialogConfirm, showLoader, success, hideLoader, messageError } =
useCounterMixin();
const router = useRouter;
const store = useKPIDataStore();
const formData = reactive({
competencyType: "",
competencyName: "",
definition: "",
level: ["", "", "", "", "", ""],
form: [
{
posType: "",
posLevel: "",
description: "",
description2: "",
},
],
evaluation: "",
});
const dataLevel = ref<any>([]);
const typeOp = ref<any>([]);
const levelOp = ref<any>([]);
const typeOpsMain = ref<any>([]);
const levelOpsMain = ref<any>([]);
function ocClickAdd() {
if (formData.form.length !== 6) {
const data = {
posType: "",
posLevel: "",
description: "",
description2: "",
};
formData.form.push(data);
}
}
/** function เรียกรายการประเภทตำแหน่ง */
function fetchType() {
showLoader();
http
.get(config.API.orgPosType)
.then((res) => {
const data = res.data.result;
dataLevel.value = data;
typeOpsMain.value = data.map((e: any) => ({
id: e.id,
name: e.posTypeName,
}));
typeOp.value = typeOpsMain.value;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function updateSelectType(val: string, index: number) {
const listLevel = dataLevel.value.find((e: any) => e.id === val);
levelOpsMain.value = listLevel.posLevels.map((e: any) => ({
id: e.id,
name: e.posLevelName,
}));
levelOp.value = levelOpsMain.value;
formData.form[index].posLevel = "";
}
function onSubmit() {
dialogConfirm($q, () => {
const body = {
competencyType: store.competencyTypeVal,
competencyName: formData.competencyName,
definition: formData.definition,
postype: formData.form,
evaluation: formData.evaluation,
};
// showLoader()
// http
// .put(config.API.???,body)
// .then((res)=>{
// success($q,'')
// router.push(`/KPI-competency`)
// }).catch((e)=>{
// messageError($q,e)
// }).finally(()=>{
// hideLoader()
// })
console.log(body);
});
}
onMounted(() => {
fetchType();
});
</script>
<template>
<q-form greedy @submit.prevent @validation-success="onSubmit" class="col-12">
<div class="row q-col-gutter-sm">
<div class="col-12">
<q-input
v-model="formData.competencyName"
dense
outlined
label="ชื่อสมรรถนะ"
:rules="[(val:string) => !!val || `${'กรุณากรอกชื่อสมรรถนะ'}`,]"
hide-bottom-space
/>
</div>
<div class="col-12">
<q-input
v-model="formData.definition"
label="คำจำกัดความ"
dense
type="textarea"
outlined
:rules="[(val:string) => !!val || `${'กรุณากรอกคำจำกัดความ'}`,]"
hide-bottom-space
/>
</div>
<div class="col-12">
<q-card flat bordered>
<q-card-section class="bg-grey-4">
<div
class="row items-center text-dark text-body2 text-weight-medium"
>
<div class="col-3">
<div class="row items-center">
<div class="col-1">
<q-btn
dense
flat
round
color="primary"
icon="add"
@click="ocClickAdd"
>
<q-tooltip>เพ</q-tooltip></q-btn
>
</div>
<div class="col-11 text-center">
<span>ประเภทตำแหน</span>
</div>
</div>
</div>
<div class="col-3 text-center">
<span>ระด</span>
</div>
<div class="col-6">
<span>มาตรฐานพฤตกรรม</span>
</div>
</div>
</q-card-section>
<q-card-section>
<div
class="row q-pa-sm q-col-gutter-sm"
v-for="(items, index) in formData.form"
key="index"
>
<div class="col-3 text-center">
<q-select
v-model="formData.form[index].posType"
outlined
label="ประเภทตำแหน่ง"
dense
option-label="name"
option-value="id"
:options="typeOp"
emit-value
map-options
:rules="[(val:string) => !!val || `${'กรุณาเลือกประเภทตำแหน่ง'}`,]"
hide-bottom-space
@update:model-value="
updateSelectType(formData.form[index].posType, index)
"
/>
</div>
<div class="col-3 text-center">
<q-select
v-model="formData.form[index].posLevel"
outlined
label="ระดับตำแหน่ง"
dense
option-label="name"
option-value="id"
:options="levelOp"
emit-value
map-options
:rules="[(val:string) => !!val || `${'กรุณาเลือกระดับตำแหน่ง'}`,]"
hide-bottom-space
/>
</div>
<div class="col-6">
<q-field
class="q_field_p_none"
ref="fieldRef"
v-model="formData.form[index].description"
label-slot
borderless
:rules="[(val) => !!val || 'กรุณากรอกมาตรฐานพฤติกรรม']"
hide-bottom-space
>
<template #control>
<q-editor
class="full-width"
v-model="formData.form[index].description"
:dense="$q.screen.lt.md"
min-height="5rem"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
:fonts="{
arial: 'Arial',
arial_black: 'Arial Black',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
}"
/>
</template>
</q-field>
<q-field
class="q_field_p_none"
ref="fieldRef"
v-model="formData.form[index].description2"
label-slot
borderless
:rules="[(val) => !!val || 'กรุณากรอกมาตรฐานพฤติกรรม']"
hide-bottom-space
>
<template #control>
<q-editor
class="full-width"
v-model="formData.form[index].description2"
:dense="$q.screen.lt.md"
min-height="5rem"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
:fonts="{
arial: 'Arial',
arial_black: 'Arial Black',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
}"
/>
</template>
</q-field>
</div>
<div class="col-12"><q-separator /></div>
</div>
</q-card-section>
</q-card>
</div>
<div class="col-12">
<q-input
v-model="formData.evaluation"
label="กำหนดเกณฑ์การประเมิน"
dense
type="textarea"
outlined
/>
</div>
<div class="col-12"><q-separator /></div>
<div class="col-12 row justify-end">
<q-btn
dense
unelevated
label="บันทึก"
color="public"
class="q-px-md"
type="submit"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</div>
</div>
</q-form>
</template>
<style scoped></style>

View file

@ -0,0 +1,319 @@
<script setup lang="ts">
import { ref, reactive } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useKPIDataStore } from "@/modules/01_metadata/stores/KPIStore";
import { useRouter } from "vue-router";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
const router = useRouter();
const mixin = useCounterMixin();
const $q = useQuasar();
const mode = ref<string>("table");
const {
dialogRemove,
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
date2Thai,
} = mixin;
const store = useKPIDataStore();
const formData = reactive({
competencyType: "",
competencyName: "",
definition: "",
level1: "",
level2: "",
level3: "",
evaluation: "",
});
function onSubmit() {
dialogConfirm($q, () => {
const body = {
competencyType: store.competencyTypeVal,
competencyName: formData.competencyName,
definition: formData.definition,
levels1: formData.level1,
levels2: formData.level2,
levels3: formData.level3,
evaluation: formData.evaluation,
};
// showLoader()
// http
// .put(config.API.???,body)
// .then((res)=>{
// success($q,'')
// router.push(`/KPI-competency`)
// }).catch((e)=>{
// messageError($q,e)
// }).finally(()=>{
// hideLoader()
// })
console.log(body);
});
}
</script>
<template>
<div class="full-width">
<q-form @submit.prevent greedy @validation-success="onSubmit()">
<div class="col-12">
<q-input
outlined
v-model="formData.competencyName"
dense
label="ชื่อสมรรถนะ"
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกชื่อสมรรถนะ'}`]"
/>
</div>
<q-card-section class="col-12 q-px-none">
<div>
<q-field
class="q_field_p_none"
ref="fieldRef"
v-model="formData.definition"
label-slot
borderless
:rules="[(val) => !!val || 'กรุณากรอกคำจำกัดความ']"
hide-bottom-space
>
<template #control>
<q-editor
class="full-width"
v-model="formData.definition"
:dense="$q.screen.lt.md"
min-height="7rem"
placeholder="คำจำกัดความ"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
:fonts="{
arial: 'Arial',
arial_black: 'Arial Black',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
}"
/>
</template>
</q-field>
</div>
</q-card-section>
<div class="col-12">
<q-card flat bordered>
<q-card-section class="bg-grey-4 q-py-sm">
<div class="row text-dark text-body2 text-weight-medium">
<div class="col-6 q-pl-xl">ระบบตำแหน</div>
<div class="col-6">
คำอธบายระด/พฤตกรรมทคาดหว/พฤตกรรมยอย
</div>
</div>
</q-card-section>
<div class="row items-center">
<q-card-section class="q-pa-none q-pl-xl col-6">
<span class="text-dark text-subtitle1 text-weight-medium">
L1 ระด วหนาฝายในสงก
</span>
</q-card-section>
<q-card-section class="q-pa-none col-6">
<div class="q-mr-md">
<q-field
ref="fieldRef"
v-model="formData.level1"
label-slot
borderless
:rules="[
(val) =>
!!val || 'กรุณาระบุคำอธิบาย L1 ระดับ หัวหน้าฝ่ายในสังกัด',
]"
hide-bottom-space
>
<template #control>
<q-editor
v-model="formData.level1"
:dense="$q.screen.lt.md"
min-height="5rem"
class="full-width"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
:fonts="{
arial: 'Arial',
arial_black: 'Arial Black',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
}"
/>
</template>
</q-field>
</div>
</q-card-section>
</div>
<div class="row items-center">
<q-card-section class="q-pa-none q-pl-xl col-6">
<span class="text-dark text-subtitle1 text-weight-medium">
L2 ระด วยผอำนวยการเขต
</span>
</q-card-section>
<q-card-section class="q-pa-none col-6">
<div class="q-mr-md">
<q-field
ref="fieldRef"
v-model="formData.level2"
label-slot
borderless
:rules="[
(val) =>
!!val ||
'กรุณาระบุคำอธิบาย L2 ระดับ ผู้ช่วยผู้อำนวยการเขต',
]"
hide-bottom-space
>
<template #control>
<q-editor
v-model="formData.level2"
:dense="$q.screen.lt.md"
min-height="5rem"
class="full-width"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
:fonts="{
arial: 'Arial',
arial_black: 'Arial Black',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
}"
/>
</template>
</q-field>
</div>
</q-card-section>
</div>
<div class="row items-center">
<q-card-section class="q-pa-none q-pl-xl col-6">
<span class="text-dark text-subtitle1 text-weight-medium">
L3 ระด อำนวยการเขต
</span>
</q-card-section>
<q-card-section class="q-pa-none col-6">
<div class="q-mr-md">
<q-field
ref="fieldRef"
v-model="formData.level3"
label-slot
borderless
:rules="[
(val) =>
!!val || 'กรุณาระบุคำอธิบาย L3 ระดับ ผู้อำนวยการเขต',
]"
hide-bottom-space
>
<template #control>
<q-editor
v-model="formData.level3"
:dense="$q.screen.lt.md"
min-height="5rem"
class="full-width"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
:fonts="{
arial: 'Arial',
arial_black: 'Arial Black',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
}"
/>
</template>
</q-field>
</div>
</q-card-section>
</div>
</q-card>
<div class="col-12 q-mt-md">
<q-input
v-model="formData.evaluation"
label="กำหนดเกณฑ์การประเมิน"
dense
type="textarea"
outlined
/>
</div>
<q-separator color="grey-4" />
<div class="col-12 row justify-end q-mt-md">
<q-btn
dense
unelevated
label="บันทึก"
color="public"
class="q-px-md"
type="submit"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</div>
</div>
</q-form>
</div>
</template>

View file

@ -0,0 +1,261 @@
<script setup lang="ts">
import { ref, reactive } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useKPIDataStore } from "@/modules/01_metadata/stores/KPIStore";
import { useRouter } from "vue-router";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
const router = useRouter();
const mixin = useCounterMixin();
const $q = useQuasar();
const mode = ref<string>("table");
const {
dialogRemove,
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
date2Thai,
} = mixin;
const store = useKPIDataStore();
const formData = reactive({
competencyType: "",
competencyName: "",
definition: "",
level1: "",
level2: "",
evaluation: "",
});
function onSubmit() {
dialogConfirm($q, () => {
const body = {
competencyType: store.competencyTypeVal,
competencyName: formData.competencyName,
definition: formData.definition,
levels1: formData.level1,
levels2: formData.level2,
evaluation: formData.evaluation,
};
// showLoader()
// http
// .put(config.API.???,body)
// .then((res)=>{
// success($q,'')
// router.push(`/KPI-competency`)
// }).catch((e)=>{
// messageError($q,e)
// }).finally(()=>{
// hideLoader()
// })
console.log(body);
});
}
</script>
<template>
<div class="full-width">
<q-form @submit.prevent greedy @validation-success="onSubmit()">
<div class="col-12">
<q-input
outlined
v-model="formData.competencyName"
dense
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกชื่อสมรรถนะ'}`]"
label="ชื่อสมรรถนะ"
/>
</div>
<q-card-section class="col-12 q-px-none">
<div>
<q-field
class="q_field_p_none"
ref="fieldRef"
v-model="formData.definition"
label-slot
borderless
:rules="[(val) => !!val || 'กรุณากรอกคำจำกัดความ']"
hide-bottom-space
>
<template #control>
<q-editor
class="full-width"
v-model="formData.definition"
:dense="$q.screen.lt.md"
min-height="7rem"
placeholder="คำจำกัดความ"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
:fonts="{
arial: 'Arial',
arial_black: 'Arial Black',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
}"
/>
</template>
</q-field>
</div>
</q-card-section>
<div class="col-12">
<q-card flat bordered>
<q-card-section class="bg-grey-4 q-py-sm">
<div class="row text-dark text-body2 text-weight-medium">
<div class="col-6 q-pl-xl">ระบบตำแหน</div>
<div class="col-6">
คำอธบายระด/พฤตกรรมทคาดหว/พฤตกรรมยอย
</div>
</div>
</q-card-section>
<div class="row items-center">
<q-card-section class="q-pa-none q-pl-xl col-6">
<span class="text-dark text-subtitle1 text-weight-medium">
L1 ประเภทอำนวยการ ระดบส
</span>
</q-card-section>
<q-card-section class="q-pa-none col-6">
<div class="q-mr-md">
<q-field
ref="fieldRef"
v-model="formData.level1"
label-slot
borderless
:rules="[
(val) =>
!!val || 'กรุณาระบุคำอธิบาย L1 ประเภทอำนวยการ ระดับสูง',
]"
hide-bottom-space
>
<template #control>
<q-editor
v-model="formData.level1"
:dense="$q.screen.lt.md"
min-height="5rem"
class="full-width"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
:fonts="{
arial: 'Arial',
arial_black: 'Arial Black',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
}"
/>
</template>
</q-field>
</div>
</q-card-section>
</div>
<div class="row items-center">
<q-card-section class="q-pa-none q-pl-xl col-6">
<span class="text-dark text-subtitle1 text-weight-medium">
L2 ประเภทบรหาร ระดบส
</span>
</q-card-section>
<q-card-section class="q-pa-none col-6">
<div class="q-mr-md">
<q-field
ref="fieldRef"
v-model="formData.level2"
label-slot
borderless
:rules="[
(val) =>
!!val || 'กรุณาระบุคำอธิบาย L2 ประเภทบริหาร ระดับสูง',
]"
hide-bottom-space
>
<template #control>
<q-editor
v-model="formData.level2"
:dense="$q.screen.lt.md"
min-height="5rem"
class="full-width"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
:fonts="{
arial: 'Arial',
arial_black: 'Arial Black',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
}"
/>
</template>
</q-field>
</div>
</q-card-section>
</div>
</q-card>
<div class="col-12 q-mt-md">
<q-input
v-model="formData.evaluation"
outlined
label="กำหนดเกณฑ์การประเมิน"
type="textarea"
dense
/>
</div>
<q-separator color="grey-4" />
<div class="col-12 row justify-end q-mt-md">
<q-btn
dense
unelevated
label="บันทึก"
color="public"
class="q-px-md"
type="submit"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</div>
</div>
</q-form>
</div>
</template>

View file

@ -0,0 +1,295 @@
div
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
/** importStore*/
import { useCounterMixin } from "@/stores/mixin";
import { useKPIDataStore } from "@/modules/01_metadata/stores/KPIStore";
/**use*/
const $q = useQuasar();
const mixin = useCounterMixin();
const { dialogConfirm, showLoader, hideLoader, success, messageError } = mixin;
const router = useRouter();
const route = useRoute();
const store = useKPIDataStore();
const competencyId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const formData = reactive({
competencyType: "",
competencyName: "",
definition: "",
levels: [
{
level: "1",
description: "",
},
],
});
function fetchDetail() {
showLoader();
http
.get(config.API.kpiCapacity + `/${competencyId.value}`)
.then((res) => {
const data = res.data.result;
formData.competencyType = data.type;
formData.competencyName = data.name;
formData.definition = data.description;
formData.levels = data.capacityDetails;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function onClickAddLevels() {
const levelName = formData.levels.length + 1;
const data = {
level:
(store.competencyTypeVal === "HEAD" ||
store.competencyTypeVal === "GROUP") &&
levelName <= 6
? levelName.toString()
: "",
description: "",
};
levelName <= 6 && formData.levels.push(data);
}
function onSubmit() {
dialogConfirm($q, async () => {
const formBody = {
type: store.competencyTypeVal,
name: formData.competencyName,
description: formData.definition,
capacityDetails: formData.levels,
};
try {
const url = competencyId.value
? config.API.kpiCapacity + `/${competencyId.value}`
: config.API.kpiCapacity;
const method = competencyId.value ? "put" : "post";
await http[method](url, formBody);
if (!competencyId.value) {
router.push(`/KPI-competency`);
} else {
fetchDetail();
}
success($q, "บันทึกข้อมูลสำเร็จ");
} catch (err) {
messageError($q, err);
} finally {
hideLoader();
}
});
}
function onDeleteLevels(index: number) {
formData.levels.splice(index, 1);
}
onMounted(() => {
if (competencyId.value) {
fetchDetail();
}
});
</script>
<template>
<q-form greedy @submit.prevent @validation-success="onSubmit" class="col-12">
<div class="row">
<div class="col-12">
<q-input
v-model="formData.competencyName"
dense
outlined
label="ชื่อสมรรถนะ"
:rules="[(val:string) => !!val || `${'กรุณากรอกชื่อสมรรถนะ'}`,]"
hide-bottom-space
/>
</div>
<q-card-section class="col-12 q-px-none">
<div>
<q-field
class="q_field_p_none"
ref="fieldRef"
v-model="formData.definition"
label-slot
borderless
:rules="[(val) => !!val || 'กรุณากรอกคำจำกัดความสมรรถนะ']"
hide-bottom-space
>
<template #control>
<q-editor
class="full-width"
v-model="formData.definition"
:dense="$q.screen.lt.md"
min-height="7rem"
placeholder="คำจำกัดความสมรรถนะ"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
:fonts="{
arial: 'Arial',
arial_black: 'Arial Black',
comic_sans: 'Comic Sans MS',
courier_new: 'Courier New',
impact: 'Impact',
lucida_grande: 'Lucida Grande',
times_new_roman: 'Times New Roman',
verdana: 'Verdana',
}"
/>
</template>
</q-field>
</div>
</q-card-section>
<div class="col-12">
<q-card flat bordered>
<q-card-section class="bg-grey-4">
<div
class="row items-center text-dark text-body2 text-weight-medium"
>
<div class="col-3">
<div class="row items-center">
<div class="col-1">
<q-btn
dense
flat
round
color="primary"
icon="add"
@click="onClickAddLevels"
>
<q-tooltip>เพ</q-tooltip></q-btn
>
</div>
<div class="col-11 text-center">
<span>ระดบสมรรถนะ</span>
</div>
</div>
</div>
<div class="col-4">
<span>พฤตกรรมทคาดหว/พฤตกรรมยอย</span>
</div>
</div>
</q-card-section>
<q-card-section>
<div
class="row q-pa-sm"
v-for="(items, index) in formData.levels"
key="index"
>
<div class="col-3 align-center q-pr-lg">
<q-input
:readonly="
store.competencyTypeVal === 'HEAD' ||
store.competencyTypeVal === 'GROUP'
"
v-model="formData.levels[index].level"
dense
outlined
:rules="[(val:string) => !!val || `${'กรุณากรอกระดับสมรรถนะ'}`,]"
hide-bottom-space
/>
</div>
<div class="col-9">
<div class="row q-col-gutter-md">
<div class="col-11">
<q-field
class="q_field_p_none"
ref="fieldRef"
v-model="formData.levels[index].description"
label-slot
borderless
:rules="[
(val) =>
!!val || 'กรุณากรอกพฤติกรรมที่คาดหวัง/พฤติกรรมย่อย',
]"
hide-bottom-space
>
<template #control>
<q-editor
class="full-width"
v-model="formData.levels[index].description"
:dense="$q.screen.lt.md"
min-height="5rem"
:toolbar="[
[
'bold',
'italic',
'strike',
'underline',
'subscript',
'superscript',
],
['unordered', 'ordered'],
]"
/>
</template>
</q-field>
</div>
<div class="col-1 text-center">
<q-btn
dense
flat
round
color="red"
icon="delete"
@click="onDeleteLevels(index)"
v-if="
(store.competencyTypeVal === 'HEAD' && index > 4) ||
(store.competencyTypeVal === 'GROUP' && index > 4) ||
((store.competencyTypeVal === 'EXECUTIVE' ||
store.competencyTypeVal === 'DIRECTOR' ||
store.competencyTypeVal === 'INSPECTOR') &&
index > 0)
"
>
<q-tooltip>ลบ</q-tooltip></q-btn
>
</div>
</div>
</div>
</div>
</q-card-section>
</q-card>
</div>
<div class="col-12 q-my-sm"><q-separator /></div>
<div class="col-12 row justify-end">
<q-btn
dense
unelevated
label="บันทึก"
color="public"
class="q-px-md"
type="submit"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</div>
</div>
</q-form>
</template>
<style scoped></style>

View file

@ -0,0 +1,31 @@
<script setup lang="ts">
import { ref } from "vue";
import { useRouter } from "vue-router";
import InsigniaList from "@/modules/01_metadata/components/insignia/InsigniaList.vue";
const router = useRouter();
const nameId = ref<string>("");
</script>
<template>
<div class="toptitle text-dark col-12 row items-center">
<q-btn
icon="mdi-arrow-left"
unelevated
round
dense
flat
color="primary"
class="q-mr-sm"
@click="router.go(-1)"
/>
รายการขอมลเครองราชอสรยาภรณ {{ nameId }}
</div>
<q-card flat bordered>
<InsigniaList v-model:insigniaTypeName="nameId" />
</q-card>
</template>
<style scoped></style>

View file

@ -0,0 +1,571 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRoute } from "vue-router";
import { useInsigniaDataStore } from "@/modules/01_metadata/stores/InsigniaStore";
import dialogHeader from "@/components/DialogHeader.vue";
import TableDraggable from "@/modules/01_metadata/components/insignia/TableDraggable.vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
const store = useInsigniaDataStore();
const mixin = useCounterMixin();
const insigniaTypeName = defineModel<string>("insigniaTypeName", {
required: true,
});
const {
dialogRemove,
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
} = mixin;
const $q = useQuasar();
const columns = ref<QTableProps["columns"]>([
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: false,
field: "no",
headerStyle: "font-size: 14px; width:0px",
style: "font-size: 14px",
},
{
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: "shortName",
align: "left",
label: "ชื่อย่อ",
sortable: true,
field: "shortName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "insigniaType",
align: "left",
label: "ลำดับชั้นเครื่องราชฯ",
sortable: true,
field: "insigniaType",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "center",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "center",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "isActive",
align: "left",
label: "สถานะ",
sortable: true,
field: "isActive",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "note",
align: "left",
label: "หมายเหตุ",
sortable: true,
field: "note",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const route = useRoute();
const id = ref<string>(route.params.id.toString());
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const isActive = ref<boolean>(false);
const shortName = ref<string>("");
const note = ref<string>("");
const name = ref<string>("");
const insigniaTypeId = ref<string>("");
const nameRef = ref<any>(null);
const shortNameRef = ref<any>(null);
const dialogStatus = ref<string>("");
const editId = ref<string>("");
const visibleColumns = ref<string[]>([
"no",
"name",
"shortName",
"insigniaType",
"createdAt",
"lastUpdatedAt",
"lastUpdateFullName",
"isActive",
"note",
]);
function closeDialog() {
dialog.value = false;
}
function validateForm() {
nameRef.value.validate();
shortNameRef.value.validate();
onSubmit();
}
async function fetchData(id: string) {
showLoader();
await http
.get(config.API.insigniaTypeNewIdOrg(id))
.then(async (res) => {
insigniaTypeId.value = res.data.result.name;
store.fetchData(res.data.result.insignias, res.data.result.name);
insigniaTypeName.value = res.data.result.name;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(async () => {
await fetchData(id.value);
});
async function onSubmit() {
if (name.value.length > 0 && shortName.value.length > 0) {
dialogConfirm(
$q,
async () => {
dialogStatus.value === "create" ? addData() : editData(editId.value);
closeDialog();
name.value = "";
shortName.value = "";
isActive.value = false;
note.value = "";
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
}
async function addData() {
await http
.post(config.API.insigniaOrg, {
name: name.value,
isActive: isActive.value,
shortName: shortName.value,
note: note.value == "" ? "-" : note.value,
insigniaTypeId: id.value,
})
.then(() => {
fetchData(id.value);
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(idData: string) {
await http
.put(config.API.insigniaNewIdOrg(idData), {
name: name.value,
isActive: isActive.value,
shortName: shortName.value,
note: note.value == "" ? "-" : note.value,
insigniaTypeId: id.value,
})
.then(() => {
fetchData(id.value);
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(idData: string) {
await http
.delete(config.API.insigniaNewIdOrg(idData))
.then(() => {
fetchData(id.value);
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
import { defineEmits } from "vue";
const emit = defineEmits(["nameType"]);
const dialogOrder = ref<boolean>(false);
</script>
<template>
<div class="q-pa-md">
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-btn
v-if="store.row.length > 0"
flat
round
color="blue-6"
icon="mdi-sort"
@click.stop="() => (dialogOrder = true)"
>
<q-tooltip> ดลำดบการแสดงผล </q-tooltip>
</q-btn>
<TableDraggable v-model:modal="dialogOrder" :fetchData="fetchData" />
<q-space />
<div class="row q-gutter-sm">
<q-input outlined dense v-model="filterKeyword" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="store.row"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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-bold">{{ 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, index) in props.cols" :key="col.id">
<div v-if="col.name == 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-else-if="col.name == 'isActive'">
<q-icon
v-if="col.value == false"
name="mdi-close"
color="red"
class="text-h5"
/>
<q-icon
v-else
name="mdi-check"
color="positive"
class="text-h5"
/>
</div>
<div v-else>
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
dialog = true;
editId = props.row.id;
name = props.row.name;
shortName = props.row.shortName;
note = props.row.note;
isActive = props.row.isActive;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
<q-dialog v-model="dialog" class="dialog" persistent>
<q-card style="min-width: 350px">
<form @submit.prevent="validateForm">
<q-card-section class="flex justify-between" style="padding: 0">
<dialog-header
:tittle="dialogStatus == 'edit' ? 'แก้ไขข้อมูล' : 'เพิ่มข้อมูล'"
:close="closeDialog"
/>
</q-card-section>
<q-separator color="grey-4" />
<q-card-section class="q-pa-none">
<div class="col-12 q-ma-md">
<q-input
outlined
:model-value="insigniaTypeId"
label="ลำดับชั้นเครื่องราชฯ"
dense
lazy-rules
borderless
hide-bottom-space
readonly
/>
</div>
<div class="col-12 q-ma-md">
<q-input
ref="nameRef"
outlined
v-model="name"
label="ชื่อเครื่องราชฯ"
dense
lazy-rules
borderless
:rules="[
(val) => val.length > 0 || 'กรุณากรอกลำดับชั้นเครื่องราชฯ',
]"
hide-bottom-space
/>
</div>
<div class="col-12 q-ma-md">
<q-input
ref="shortNameRef"
outlined
v-model="shortName"
label="ชื่อย่อ"
dense
lazy-rules
borderless
:rules="[
(val) => val.length > 0 || 'กรุณากรอกลำดับชั้นเครื่องราชฯ',
]"
hide-bottom-space
/>
</div>
<div class="col-12 q-ma-md">
<q-input
outlined
v-model="note"
label="หมายเหตุ"
dense
type="textarea"
borderless
hide-bottom-space
/>
</div>
<div
class="col q-ma-md q-pa-sm bg-white border_custom text-weight-medium"
>
<div class="row items-center q-my-sm justify-between">
<p class="q-ma-none">สถานะการใชงาน</p>
<label class="toggle-control">
<input type="checkbox" dense v-model="isActive" @change="" />
<span class="control"></span>
</label>
</div>
</div>
</q-card-section>
<q-card-actions align="right">
<q-btn
id="onSubmit"
type="submit"
dense
unelevated
label="บันทึก"
color="public"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</form>
</q-card>
</q-dialog>
</div>
</template>
<style scoped lang="scss">
.border_custom {
border-radius: 6px !important;
border: 1px solid #e1e1e1;
}
$toggle-background-color-on: #06884d;
$toggle-background-color-off: darkgray;
$toggle-control-color: white;
$toggle-width: 40px;
$toggle-height: 25px;
$toggle-gutter: 3px;
$toggle-radius: 50%;
$toggle-control-speed: 0.15s;
$toggle-control-ease: ease-in;
// These are our computed variables
// change at your own risk.
$toggle-radius: $toggle-height / 2;
$toggle-control-size: $toggle-height - ($toggle-gutter * 2);
.toggle-control {
display: block;
position: relative;
padding-left: $toggle-width;
margin-bottom: 12px;
cursor: pointer;
font-size: 22px;
user-select: none;
input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
input:checked ~ .control {
background-color: $toggle-background-color-on;
&:after {
left: $toggle-width - $toggle-control-size - $toggle-gutter;
}
}
.control {
position: absolute;
top: -7px;
left: -15px;
height: $toggle-height;
width: $toggle-width;
border-radius: $toggle-radius;
background-color: $toggle-background-color-off;
transition: background-color $toggle-control-speed $toggle-control-ease;
&:after {
content: "";
position: absolute;
left: $toggle-gutter;
top: $toggle-gutter;
width: $toggle-control-size;
height: $toggle-control-size;
border-radius: $toggle-radius;
background: $toggle-control-color;
transition: left $toggle-control-speed $toggle-control-ease;
}
}
}
</style>

View file

@ -0,0 +1,442 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRouter } from "vue-router";
import { useInsigniaDataStore } from "@/modules/01_metadata/stores/InsigniaStore";
import dialogHeader from "@/components/DialogHeader.vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
const store = useInsigniaDataStore();
const router = useRouter();
const mixin = useCounterMixin();
const {
dialogRemove,
dialogConfirm,
success,
messageError,
showLoader,
hideLoader,
} = mixin;
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: "createdAt",
align: "center",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "center",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "isActive",
align: "left",
label: "สถานะ",
sortable: true,
field: "isActive",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const $q = useQuasar();
const editId = ref<string>("");
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const isActive = ref<boolean>(false);
const name = ref<string>("");
const nameRef = ref<any>(null);
const dialogStatus = ref<string>("");
const visibleColumns = ref<string[]>([
"name",
"createdAt",
"lastUpdatedAt",
"lastUpdateFullName",
"isActive",
]);
async function fetchData() {
showLoader();
await http
.get(config.API.insigniaTypeOrg)
.then(async (res) => {
store.fetchData(res.data.result);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(async () => {
fetchData();
});
function closeDialog() {
dialog.value = false;
}
function onclickDetail(id: string) {
router.push(`/master-data/insignia/detail/${id}`);
}
async function addData() {
await http
.post(config.API.insigniaTypeOrg, {
name: name.value,
isActive: isActive.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(id: string) {
await http
.put(config.API.insigniaTypeNewIdOrg(id), {
name: name.value,
isActive: isActive.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.insigniaTypeNewIdOrg(id))
.then(() => {
fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function validateForm() {
nameRef.value.validate();
onSubmit();
}
async function onSubmit() {
if (name.value.length > 0) {
dialogConfirm(
$q,
async () => {
dialogStatus.value === "create" ? addData() : editData(editId.value);
closeDialog();
name.value = "";
isActive.value = false;
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
}
</script>
<template>
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input outlined dense v-model="filterKeyword" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="store.row"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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"
@click.stop="onclickDetail(props.row.id)"
>
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name == 'isActive'">
<q-icon
v-if="col.value == false"
name="mdi-close"
color="red"
class="text-h5"
/>
<q-icon v-else name="mdi-check" color="positive" class="text-h5" />
</div>
<div v-else>
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
dialog = true;
editId = props.row.id;
name = props.row.name;
isActive = props.row.isActive;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
<q-dialog v-model="dialog" class="dialog" persistent>
<q-card style="min-width: 350px">
<form @submit.prevent="validateForm">
<q-card-section class="flex justify-between" style="padding: 0">
<dialog-header
:tittle="dialogStatus === 'edit' ? 'แก้ไขข้อมูล' : 'เพิ่มข้อมูล'"
:close="closeDialog"
/>
</q-card-section>
<q-separator color="grey-4" />
<q-card-section class="q-pa-none">
<div class="col-12 q-ma-md">
<q-input
ref="nameRef"
outlined
v-model="name"
label="ลำดับชั้นเครื่องราชฯ"
dense
lazy-rules
borderless
:rules="[
(val) => val.length > 0 || 'กรุณากรอกลำดับชั้นเครื่องราชฯ',
]"
hide-bottom-space
/>
</div>
<div
class="col q-ma-md q-pa-sm bg-white border_custom text-weight-medium"
>
<div class="row items-center q-my-sm justify-between">
<p class="q-ma-none">สถานะการใชงาน</p>
<label class="toggle-control">
<input type="checkbox" dense v-model="isActive" @change="" />
<span class="control"></span>
</label>
</div>
</div>
</q-card-section>
<q-card-actions align="right">
<q-btn
id="onSubmit"
type="submit"
dense
unelevated
label="บันทึก"
color="public"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</form>
</q-card>
</q-dialog>
</template>
<style scoped lang="scss">
.border_custom {
border-radius: 6px !important;
border: 1px solid #e1e1e1;
}
$toggle-background-color-on: #06884d;
$toggle-background-color-off: darkgray;
$toggle-control-color: white;
$toggle-width: 40px;
$toggle-height: 25px;
$toggle-gutter: 3px;
$toggle-radius: 50%;
$toggle-control-speed: 0.15s;
$toggle-control-ease: ease-in;
// These are our computed variables
// change at your own risk.
$toggle-radius: $toggle-height / 2;
$toggle-control-size: $toggle-height - ($toggle-gutter * 2);
.toggle-control {
display: block;
position: relative;
padding-left: $toggle-width;
margin-bottom: 12px;
cursor: pointer;
font-size: 22px;
user-select: none;
input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
input:checked ~ .control {
background-color: $toggle-background-color-on;
&:after {
left: $toggle-width - $toggle-control-size - $toggle-gutter;
}
}
.control {
position: absolute;
top: -7px;
left: -15px;
height: $toggle-height;
width: $toggle-width;
border-radius: $toggle-radius;
background-color: $toggle-background-color-off;
transition: background-color $toggle-control-speed $toggle-control-ease;
&:after {
content: "";
position: absolute;
left: $toggle-gutter;
top: $toggle-gutter;
width: $toggle-control-size;
height: $toggle-control-size;
border-radius: $toggle-radius;
background: $toggle-control-color;
transition: left $toggle-control-speed $toggle-control-ease;
}
}
}
</style>

View file

@ -0,0 +1,163 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRouter, useRoute } from "vue-router";
import { useInsigniaDataStore } from "@/modules/01_metadata/stores/InsigniaStore";
import dialogHeader from "@/components/DialogHeader.vue";
import { useQuasar } from "quasar";
import DialogHeader from "@/components/DialogHeader.vue";
import http from "@/plugins/http";
import config from "@/app.config";
const store = useInsigniaDataStore();
const mixin = useCounterMixin();
const { dialogRemove, dialogConfirm, showLoader, hideLoader, messageError } =
mixin;
const $q = useQuasar();
const route = useRoute();
const modal = defineModel("modal", { type: Boolean });
const columns = ref<QTableProps["columns"]>([
{
name: "name",
required: true,
label: "ชื่อเครื่องราชฯ",
align: "left",
field: "name",
sortable: true,
},
{
name: "shortName",
align: "center",
label: "ชื่อย่อ",
field: "shortName",
},
{
name: "insigniaType",
align: "left",
label: "ลำดับชั้นเครื่องราชฯ",
field: "insigniaType",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sortable: true,
},
]);
const id = ref<string>(route.params.id.toString());
const props = defineProps({
fetchData: {
type: Function,
default: () => console.log("not function"),
},
});
function onDrop(from: any, to: any) {
onDropRow(from, to);
}
const rows = ref<any>([]);
function onDropRow(from: any, to: any) {
rows.value.splice(to, 0, rows.value.splice(from, 1)[0]);
}
async function save() {
const dataPost = await rows.value.map((obj: any) => {
return obj.id;
});
modal.value = false;
showLoader();
await http
.put(config.API.insigniaSortOrg(id.value), { id: dataPost })
.then(() => {
store.row = rows.value;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
// props.fetchData(id.value);
});
}
async function onSubmit() {
dialogConfirm($q, async () => {
save();
});
}
watch(modal, () => {
if (modal.value === true) {
// rows.value = store.row;
rows.value = [...store.row]; // sort store
}
});
</script>
<template>
<q-dialog v-model="modal" class="dialog" persistent>
<q-card style="min-width: 50vw" class="bg-grey-11">
<form @submit.prevent="onSubmit">
<DialogHeader
tittle="จัดลำดับการแสดงผล"
:close="() => (modal = false)"
/>
<q-separator />
<q-card-section class="q-pa-md bg-grey-1">
<q-table
v-draggable-table="{
options: {
mode: 'row',
onlyBody: true,
dragHandler: 'th,td',
},
onDrop,
}"
flat
bordered
:rows="rows"
:columns="columns"
:rows-per-page-options="[100]"
row-key="name"
hide-bottom
hide-pagination
hide-header
/>
<!-- <q-table
v-draggable-table="{
options: {
mode: 'row',
onlyBody: true,
dragHandler: 'tr',
},
onDrop,
}"
ref="table"
:columns="columns"
:rows="rows"
row-key="name"
flat
bordered
hide-bottom
/> -->
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
type="submit"
dense
unelevated
label="บันทึก"
color="public"
class="q-px-md"
>
<!-- icon="mdi-content-save-outline" -->
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</form>
</q-card>
</q-dialog>
</template>

View file

@ -0,0 +1,257 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { usePersonalDataStore } from "@/modules/01_metadata/stores/personalStore";
import { useQuasar } from "quasar";
import DialogForm from "@/modules/01_metadata/components/personal/DialogForm.vue";
import http from "@/plugins/http";
import config from "@/app.config";
const TABLE_COLUMNS = [
{
name: "prefix",
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: "createdAt",
align: "left",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
] as const satisfies QTableProps["columns"];
const $q = useQuasar();
const store = usePersonalDataStore();
const mixin = useCounterMixin();
const { dialogRemove, messageError, showLoader, hideLoader, success } = mixin;
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const prefix = ref<string>("");
const editId = ref<string>("");
const dialogStatus = ref<string>("");
const personalName = ref<string>("คำนำหน้าชื่อ");
const visibleColumns = ref<string[]>([
"prefix",
"createdAt",
"lastUpdatedAt",
"lastUpdateFullName",
]);
async function fetchData() {
showLoader();
await http
.get(config.API.orgPrefix)
.then(async (res) => {
store.save(res.data.result);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
await http
.post(config.API.orgPrefix, {
name: prefix.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(id: string) {
await http
.put(config.API.orgPrefixId(id), {
name: prefix.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.orgPrefixId(id))
.then(() => {
fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(async () => {
fetchData();
});
</script>
<template>
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input outlined dense v-model="filterKeyword" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="TABLE_COLUMNS"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="TABLE_COLUMNS"
:rows="store.row"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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">
{{ col.value }}
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
dialog = true;
prefix = props.row.name;
editId = props.row.id;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
<DialogForm
v-model:dialog="dialog"
v-model:data="prefix"
v-model:personalName="personalName"
v-model:dialogStatus="dialogStatus"
v-model:editId="editId"
:addData="addData"
:fetch-data="fetchData"
:edit-data="editData"
/>
</template>

View file

@ -0,0 +1,263 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRouter } from "vue-router";
import { usePersonalDataStore } from "@/modules/01_metadata/stores/personalStore";
import dialogHeader from "@/components/DialogHeader.vue";
import { useQuasar } from "quasar";
import DialogForm from "@/modules/01_metadata/components/personal/DialogForm.vue";
import http from "@/plugins/http";
import config from "@/app.config";
const columns = [
{
name: "rank",
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: "createdAt",
align: "left",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
] as const satisfies QTableProps["columns"];
const store = usePersonalDataStore();
const router = useRouter();
const mixin = useCounterMixin();
const { dialogRemove, messageError, showLoader, hideLoader, success } = mixin;
const $q = useQuasar();
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const rank = ref<string>("");
const dialogStatus = ref<string>("");
const personalName = ref<string>("ยศ");
const editId = ref<string>("");
const visibleColumns = ref<string[]>([
"rank",
"createdAt",
"lastUpdatedAt",
"lastUpdateFullName",
]);
async function fetchData() {
showLoader();
await http
.get(config.API.orgRank)
.then(async (res) => {
store.save(res.data.result);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
await http
.post(config.API.orgRank, {
name: rank.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(id: string) {
await http
.put(config.API.orgRankId(id), {
name: rank.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.orgRankId(id))
.then(() => {
fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(async () => {
fetchData();
});
</script>
<template>
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input outlined dense v-model="filterKeyword" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="store.row"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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">
<div>
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
dialog = true;
rank = props.row.name;
editId = props.row.id;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
<DialogForm
v-model:dialog="dialog"
v-model:data="rank"
v-model:personalName="personalName"
v-model:dialogStatus="dialogStatus"
v-model:editId="editId"
:addData="addData"
:fetch-data="fetchData"
:edit-data="editData"
/>
</template>

View file

@ -0,0 +1,263 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRouter } from "vue-router";
import { usePersonalDataStore } from "@/modules/01_metadata/stores/personalStore";
import dialogHeader from "@/components/DialogHeader.vue";
import { useQuasar } from "quasar";
import DialogForm from "@/modules/01_metadata/components/personal/DialogForm.vue";
import http from "@/plugins/http";
import config from "@/app.config";
const store = usePersonalDataStore();
const router = useRouter();
const mixin = useCounterMixin();
const { dialogRemove, messageError, showLoader, hideLoader, success } = mixin;
const columns = [
{
name: "bloodGroup",
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: "createdAt",
align: "left",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
] as const satisfies QTableProps["columns"];
const $q = useQuasar();
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const editId = ref<string>("");
const bloodGroup = ref<string>("");
const dialogStatus = ref<string>("");
const personalName = ref<string>("กลุ่มเลือด");
const visibleColumns = ref<string[]>([
"bloodGroup",
"createdAt",
"lastUpdatedAt",
"lastUpdateFullName",
]);
async function fetchData() {
showLoader();
await http
.get(config.API.orgBloodGroup)
.then(async (res) => {
store.save(res.data.result);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
await http
.post(config.API.orgBloodGroup, {
name: bloodGroup.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(id: string) {
await http
.put(config.API.orgBloodGroupId(id), {
name: bloodGroup.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.orgBloodGroupId(id))
.then(() => {
fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(async () => {
fetchData();
});
</script>
<template>
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input outlined dense v-model="filterKeyword" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="store.row"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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">
<div>
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
dialog = true;
bloodGroup = props.row.name;
editId = props.row.id;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
<DialogForm
v-model:dialog="dialog"
v-model:data="bloodGroup"
v-model:personalName="personalName"
v-model:dialogStatus="dialogStatus"
v-model:editId="editId"
:addData="addData"
:fetch-data="fetchData"
:edit-data="editData"
/>
</template>

View file

@ -0,0 +1,263 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRouter } from "vue-router";
import { usePersonalDataStore } from "@/modules/01_metadata/stores/personalStore";
import dialogHeader from "@/components/DialogHeader.vue";
import { useQuasar } from "quasar";
import DialogForm from "@/modules/01_metadata/components/personal/DialogForm.vue";
import http from "@/plugins/http";
import config from "@/app.config";
const store = usePersonalDataStore();
const router = useRouter();
const mixin = useCounterMixin();
const { dialogRemove, messageError, showLoader, hideLoader, success } = mixin;
const columns = [
{
name: "gender",
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: "createdAt",
align: "left",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
] as const satisfies QTableProps["columns"];
const $q = useQuasar();
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const gender = ref<string>("");
const editId = ref<string>("");
const dialogStatus = ref<string>("");
const personalName = ref<string>("เพศ");
const visibleColumns = ref<string[]>([
"gender",
"createdAt",
"lastUpdatedAt",
"lastUpdateFullName",
]);
async function fetchData() {
showLoader();
await http
.get(config.API.orgGender)
.then(async (res) => {
store.save(res.data.result);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
await http
.post(config.API.orgGender, {
name: gender.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(id: string) {
await http
.put(config.API.orgGenderId(id), {
name: gender.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.orgGenderId(id))
.then(() => {
fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(async () => {
fetchData();
});
</script>
<template>
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input outlined dense v-model="filterKeyword" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="store.row"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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">
<div>
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
dialog = true;
gender = props.row.name;
editId = props.row.id;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
<DialogForm
v-model:dialog="dialog"
v-model:data="gender"
v-model:personalName="personalName"
v-model:dialogStatus="dialogStatus"
v-model:editId="editId"
:addData="addData"
:fetch-data="fetchData"
:edit-data="editData"
/>
</template>

View file

@ -0,0 +1,263 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRouter } from "vue-router";
import { usePersonalDataStore } from "@/modules/01_metadata/stores/personalStore";
import dialogHeader from "@/components/DialogHeader.vue";
import { useQuasar } from "quasar";
import DialogForm from "@/modules/01_metadata/components/personal/DialogForm.vue";
import http from "@/plugins/http";
import config from "@/app.config";
const store = usePersonalDataStore();
const router = useRouter();
const mixin = useCounterMixin();
const { dialogRemove, messageError, showLoader, hideLoader, success } = mixin;
const columns = [
{
name: "religion",
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: "createdAt",
align: "left",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
] as const satisfies QTableProps["columns"];
const $q = useQuasar();
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const religion = ref<string>("");
const editId = ref<string>("");
const dialogStatus = ref<string>("");
const personalName = ref<string>("ศาสนา");
const visibleColumns = ref<string[]>([
"religion",
"createdAt",
"lastUpdatedAt",
"lastUpdateFullName",
]);
async function fetchData() {
showLoader();
await http
.get(config.API.orgReligion)
.then(async (res) => {
store.save(res.data.result);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
await http
.post(config.API.orgReligion, {
name: religion.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(id: string) {
await http
.put(config.API.orgReligionId(id), {
name: religion.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.orgReligionId(id))
.then(() => {
fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(async () => {
fetchData();
});
</script>
<template>
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input outlined dense v-model="filterKeyword" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="store.row"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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">
<div>
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
dialog = true;
religion = props.row.name;
editId = props.row.id;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
<DialogForm
v-model:dialog="dialog"
v-model:data="religion"
v-model:personalName="personalName"
v-model:dialogStatus="dialogStatus"
v-model:editId="editId"
:addData="addData"
:fetch-data="fetchData"
:edit-data="editData"
/>
</template>

View file

@ -0,0 +1,291 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRouter } from "vue-router";
import { usePersonalDataStore } from "@/modules/01_metadata/stores/personalStore";
import { useQuasar } from "quasar";
import DialogForm from "@/modules/01_metadata/components/personal/DialogForm.vue";
import http from "@/plugins/http";
import config from "@/app.config";
const store = usePersonalDataStore();
const router = useRouter();
const mixin = useCounterMixin();
const { dialogRemove, messageError, showLoader, hideLoader, success } = mixin;
const columns = [
{
name: "relationship",
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: "createdAt",
align: "left",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
] as const satisfies QTableProps["columns"];
const $q = useQuasar();
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const relationship = ref<string>("");
const dialogStatus = ref<string>("");
const personalName = ref<string>("สถานภาพ");
const visibleColumns = ref<string[]>([
"relationship",
"createdAt",
"lastUpdatedAt",
"lastUpdateFullName",
]);
const editId = ref<string>("");
const data = [
{
id: "1",
name: "เคยสมรสแต่ไม่ทราบสถานภาพสมรส",
createdAt: new Date(),
lastUpdatedAt: new Date(),
lastUpdateFullName: "สาวิตรี ศรีสมัย",
},
{
id: "2",
name: "สมรส",
createdAt: new Date(),
lastUpdatedAt: new Date(),
lastUpdateFullName: "System Administrator",
},
{
id: "3",
name: "แยกกันอยู่",
createdAt: new Date(),
lastUpdatedAt: new Date(),
lastUpdateFullName: "คณะกรรมการ ตรวจรับ",
},
{
id: "4",
name: "โสด",
createdAt: new Date(),
lastUpdatedAt: new Date(),
lastUpdateFullName: "คณะกรรมการ ตรวจรับ",
},
];
async function fetchData() {
showLoader();
await http
.get(config.API.orgRelationship)
.then(async (res) => {
store.save(res.data.result);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
await http
.post(config.API.orgRelationship, {
name: relationship.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(id: string) {
await http
.put(config.API.orgRelationshipId(id), {
name: relationship.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.orgRelationshipId(id))
.then(() => {
fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(async () => {
fetchData();
});
</script>
<template>
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input outlined dense v-model="filterKeyword" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="store.row"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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">
<div>
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
dialog = true;
relationship = props.row.name;
editId = props.row.id;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
<DialogForm
v-model:dialog="dialog"
v-model:data="relationship"
v-model:personalName="personalName"
v-model:dialogStatus="dialogStatus"
v-model:editId="editId"
:addData="addData"
:fetch-data="fetchData"
:edit-data="editData"
/>
</template>

View file

@ -0,0 +1,279 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRouter } from "vue-router";
import { usePersonalDataStore } from "@/modules/01_metadata/stores/personalStore";
import { useQuasar } from "quasar";
import DialogForm from "@/modules/01_metadata/components/personal/DialogForm.vue";
import http from "@/plugins/http";
import config from "@/app.config";
const store = usePersonalDataStore();
const mixin = useCounterMixin();
const { dialogRemove, messageError, showLoader, hideLoader, success } = mixin;
const 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: "rank",
align: "left",
label: "ลำดับ",
sortable: true,
field: "rank",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "left",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
] as const satisfies QTableProps["columns"];
const $q = useQuasar();
const editId = ref<string>("");
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const educationLevel = ref<string>("");
const educationRank = ref<number>();
const dialogStatus = ref<string>("");
const personalName = ref<string>("ระดับการศึกษา");
const visibleColumns = ref<string[]>([
"name",
"rank",
"createdAt",
"lastUpdatedAt",
"lastUpdateFullName",
]);
async function fetchData() {
showLoader();
await http
.get(config.API.orgEducationLevel)
.then(async (res) => {
store.save(res.data.result);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
await http
.post(config.API.orgEducationLevel, {
name: educationLevel.value,
rank: educationRank.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(id: string) {
await http
.put(config.API.orgEducationLevelId(id), {
name: educationLevel.value,
rank: educationRank.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.orgEducationLevelId(id))
.then(() => {
fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(async () => {
fetchData();
});
</script>
<template>
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
educationLevel = '';
educationRank = undefined;
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input outlined dense v-model="filterKeyword" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="store.row"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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">
<div>
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
dialog = true;
editId = props.row.id;
educationLevel = props.row.name;
educationRank = props.row.rank;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
<DialogForm
v-model:educationRank="educationRank"
v-model:dialog="dialog"
v-model:data="educationLevel"
v-model:personalName="personalName"
v-model:dialogStatus="dialogStatus"
v-model:editId="editId"
:addData="addData"
:fetch-data="fetchData"
:edit-data="editData"
/>
</template>

View file

@ -0,0 +1,271 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRouter } from "vue-router";
import { usePersonalDataStore } from "@/modules/01_metadata/stores/personalStore";
import { useQuasar } from "quasar";
import DialogForm from "@/modules/01_metadata/components/personal/DialogForm.vue";
import http from "@/plugins/http";
import config from "@/app.config";
const router = useRouter();
const store = usePersonalDataStore();
const mixin = useCounterMixin();
const { dialogRemove, messageError, showLoader, hideLoader, success } = mixin;
const 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: "createdAt",
align: "left",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
] as const satisfies QTableProps["columns"];
const $q = useQuasar();
const editId = ref<string>("");
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const province = ref<string>("");
const dialogStatus = ref<string>("");
const personalName = ref<string>("จังหวัด");
const visibleColumns = ref<string[]>([
"name",
"createdAt",
"lastUpdatedAt",
"lastUpdateFullName",
]);
async function fetchData() {
showLoader();
await http
.get(config.API.orgProvince)
.then(async (res) => {
store.save(res.data.result);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
await http
.post(config.API.orgProvince, {
name: province.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(id: string) {
await http
.put(config.API.orgProvince + `/${id}`, {
name: province.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.orgProvince + `/${id}`)
.then(() => {
fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function nextPage(id: string) {
router.push(`/master-data/personal/district/${id}`);
}
onMounted(async () => {
fetchData();
});
</script>
<template>
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
province = '';
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input outlined dense v-model="filterKeyword" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="store.row"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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"
@click="nextPage(props.row.id)"
>
<div>
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
dialog = true;
editId = props.row.id;
province = props.row.name;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
<DialogForm
v-model:dialog="dialog"
v-model:data="province"
v-model:personalName="personalName"
v-model:dialogStatus="dialogStatus"
v-model:editId="editId"
:addData="addData"
:fetch-data="fetchData"
:edit-data="editData"
/>
</template>

View file

@ -0,0 +1,148 @@
<script setup lang="ts">
import { ref } from "vue";
import { useCounterMixin } from "@/stores/mixin";
import dialogHeader from "@/components/DialogHeader.vue";
import { QInput, useQuasar } from "quasar";
const $q = useQuasar();
const mixin = useCounterMixin();
const { dialogConfirm } = mixin;
const dataRef = ref<QInput | null>(null);
const zipCodeRef = ref<QInput | null>(null);
const educationRankRef = ref<QInput | null>(null);
const data = defineModel<string>("data", {
required: true,
});
const personalName = defineModel<string>("personalName");
const dialogStatus = defineModel<string>("dialogStatus");
const editId = defineModel<string>("editId");
const dialog = defineModel<boolean>("dialog");
const educationRank = defineModel<number>("educationRank");
const zipCode = defineModel<string>("zipCode");
const props = defineProps({
fetchData: {
type: Function,
},
addData: {
type: Function,
default: () => {},
},
editData: {
type: Function,
default: () => {},
},
});
function closeDialog() {
dialog.value = false;
}
function validateForm() {
dataRef.value?.validate();
zipCodeRef.value?.validate();
educationRankRef.value?.validate();
onSubmit();
}
async function onSubmit() {
if (data.value.length > 0) {
dialogConfirm(
$q,
async () => {
dialogStatus.value === "create"
? props.addData()
: props.editData(editId.value);
closeDialog();
data.value = "";
educationRank.value = undefined;
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
}
</script>
<template>
<q-dialog v-model="dialog" class="dialog" persistent>
<q-card style="min-width: 350px">
<form @submit.prevent="validateForm">
<q-card-section class="flex justify-between" style="padding: 0">
<dialog-header
:tittle="dialogStatus == 'edit' ? 'แก้ไขข้อมูล' : 'เพิ่มข้อมูล'"
:close="closeDialog"
/>
</q-card-section>
<q-separator color="grey-4" />
<q-card-section class="q-pa-none">
<div class="col-12 q-ma-md">
<q-input
ref="dataRef"
outlined
v-model="data"
:label="personalName"
dense
class="inputgreen"
lazy-rules
borderless
:rules="[(val) => val.length > 0 || 'กรุณากรอก' + personalName]"
hide-bottom-space
/>
</div>
</q-card-section>
<q-card-section
class="q-pa-none"
v-if="personalName === 'ระดับการศึกษา'"
>
<div class="col-12 q-ma-md">
<q-input
ref="educationRankRef"
outlined
v-model="educationRank"
label="ลำดับ"
dense
type="number"
lazy-rules
borderless
min="1"
:rules="[(val) => val != undefined || 'กรุณากรอกลำดับ']"
hide-bottom-space
/>
</div>
</q-card-section>
<q-card-section class="q-pa-none" v-if="personalName === 'แขวง/ตำบล'">
<div class="col-12 q-ma-md">
<q-input
ref="zipCodeRef"
outlined
v-model="zipCode"
label="รหัสไปรษณีย์"
dense
class="inputgreen"
lazy-rules
borderless
:rules="[(val) => val.length > 0 || 'กรุณากรอกรหัสไปรษณีย์']"
hide-bottom-space
mask="#####"
/>
</div>
</q-card-section>
<q-separator color="grey-4" />
<q-card-actions align="right">
<q-btn
id="onSubmit"
type="submit"
dense
unelevated
label="บันทึก"
color="public"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</form>
</q-card>
</q-dialog>
</template>

View file

@ -0,0 +1,313 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRouter, useRoute } from "vue-router";
import { usePersonalDataStore } from "@/modules/01_metadata/stores/personalStore";
import { useQuasar } from "quasar";
import DialogForm from "@/modules/01_metadata/components/personal/DialogForm.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import type { FormDistrict } from "@/modules/01_metadata/interface/response/personal/personal";
const router = useRouter();
const route = useRoute();
const id = ref<string>(route.params.id as string);
const store = usePersonalDataStore();
const mixin = useCounterMixin();
const {
dialogRemove,
messageError,
showLoader,
hideLoader,
success,
date2Thai,
} = mixin;
const 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: "createdAt",
align: "left",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
] as const satisfies QTableProps["columns"];
const $q = useQuasar();
const editId = ref<string>("");
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const district = ref<string>("");
const rows = ref<FormDistrict[]>([]);
const dialogStatus = ref<string>("");
const personalName = ref<string>("เขต/อำเภอ");
const visibleColumns = ref<string[]>([
"name",
"createdAt",
"lastUpdatedAt",
"lastUpdateFullName",
]);
async function fetchData() {
showLoader();
await http
.get(config.API.orgProvince + `/${id.value}`)
.then(async (res) => {
const data = res.data.result.districts;
const list = data.map((e: any) => ({
...e,
createdAt: e.createdAt ? date2Thai(e.createdAt) : "",
lastUpdatedAt: e.lastUpdatedAt ? date2Thai(e.lastUpdatedAt) : "",
}));
rows.value = list;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
await http
.post(config.API.orgDistrict, {
name: district.value,
provinceId: id.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(id: string) {
await http
.put(config.API.orgDistrict + `/${id}`, {
name: district.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.orgDistrict + `/${id}`)
.then(() => {
fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function nextPage(idSub: string) {
router.push(`/master-data/personal/sub-district/${id.value}/${idSub}`);
}
onMounted(async () => {
fetchData();
});
</script>
<template>
<div class="toptitle text-dark col-12 row items-center">
<q-btn
icon="mdi-arrow-left"
unelevated
round
dense
flat
color="primary"
class="q-mr-sm"
@click="router.go(-1)"
/>
เขต/อำเภอ
</div>
<q-card flat bordered>
<div class="q-pa-md">
<q-toolbar class="q-pa-none">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
district = '';
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input
outlined
dense
v-model="filterKeyword"
label="ค้นหา"
></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="rows"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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"
@click="nextPage(props.row.id)"
>
<div>
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
dialog = true;
editId = props.row.id;
district = props.row.name;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
</div>
</q-card>
<DialogForm
v-model:dialog="dialog"
v-model:data="district"
v-model:personalName="personalName"
v-model:dialogStatus="dialogStatus"
v-model:editId="editId"
:addData="addData"
:fetch-data="fetchData"
:edit-data="editData"
/>
</template>

View file

@ -0,0 +1,324 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRouter, useRoute } from "vue-router";
import { usePersonalDataStore } from "@/modules/01_metadata/stores/personalStore";
import { useQuasar } from "quasar";
import DialogForm from "@/modules/01_metadata/components/personal/DialogForm.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import type { FormSubDistrict } from "@/modules/01_metadata/interface/response/personal/personal";
const rows = ref<FormSubDistrict[]>([]);
const router = useRouter();
const route = useRoute();
const id = ref<string>(route.params.id as string);
const idProvince = ref<string>(route.params.provinceId as string);
const store = usePersonalDataStore();
const mixin = useCounterMixin();
const {
dialogRemove,
messageError,
showLoader,
hideLoader,
success,
date2Thai,
} = mixin;
const 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: "zipCode",
align: "left",
label: "รหัสไปรษณีย์",
sortable: true,
field: "zipCode",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "left",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
] as const satisfies QTableProps["columns"];
const $q = useQuasar();
const editId = ref<string>("");
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const subDistrict = ref<string>("");
const zipCode = ref<string>("");
const dialogStatus = ref<string>("");
const personalName = ref<string>("แขวง/ตำบล");
const visibleColumns = ref<string[]>([
"name",
"zipCode",
"createdAt",
"lastUpdatedAt",
"lastUpdateFullName",
]);
async function fetchData() {
showLoader();
await http
.get(config.API.orgDistrict + `/${id.value}`)
.then(async (res) => {
const data = res.data.result.subDistricts;
const list = data.map((e: any) => ({
...e,
createdAt: e.createdAt ? date2Thai(e.createdAt) : "",
lastUpdatedAt: e.lastUpdatedAt ? date2Thai(e.lastUpdatedAt) : "",
}));
rows.value = list;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
await http
.post(config.API.orgSubDistrict, {
name: subDistrict.value,
districtId: id.value,
zipCode: zipCode.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(idRow: string) {
await http
.put(config.API.orgSubDistrict + `/${idRow}`, {
name: subDistrict.value,
districtId: id.value,
zipCode: zipCode.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.orgSubDistrict + `/${id}`)
.then(() => {
fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(async () => {
fetchData();
});
</script>
<template>
<div class="toptitle text-dark col-12 row items-center">
<q-btn
icon="mdi-arrow-left"
unelevated
round
dense
flat
color="primary"
class="q-mr-sm"
@click="router.go(-1)"
/>
แขวง/ตำบล
</div>
<q-card flat bordered>
<div class="q-pa-md">
<q-toolbar class="q-pa-none">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
subDistrict = '';
zipCode = '';
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input
outlined
dense
v-model="filterKeyword"
label="ค้นหา"
></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="rows"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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">
<div>
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
dialog = true;
editId = props.row.id;
subDistrict = props.row.name;
zipCode = props.row.zipCode;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
</div>
</q-card>
<DialogForm
v-model:dialog="dialog"
v-model:data="subDistrict"
v-model:zipCode="zipCode"
v-model:personalName="personalName"
v-model:dialogStatus="dialogStatus"
v-model:editId="editId"
:addData="addData"
:fetch-data="fetchData"
:edit-data="editData"
/>
</template>

View file

@ -0,0 +1,482 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
/** importType*/
import type { QTableProps } from "quasar";
import type {
ObjectPosRef,
FormQuery,
} from "@/modules/01_metadata/interface/index/positionEmployee";
import type {
DataOption,
RowDetailPositions,
} from "@/modules/01_metadata/interface/request/position/index";
import type {
ResGroup,
ResLevel,
ResPossition,
} from "@/modules/01_metadata/interface/response/positionEmployee/Main";
/**importComponets*/
import DialogHeader from "@/components/DialogHeader.vue";
/**importStore*/
import { useCounterMixin } from "@/stores/mixin";
/**use*/
const $q = useQuasar();
const mixin = useCounterMixin();
const {
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
dialogRemove,
} = mixin;
const rows = ref<ResPossition[]>([]);
const columns = ref<QTableProps["columns"]>([
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: false,
field: "no",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posDictName",
align: "left",
label: "ชื่อตำแหน่ง",
sortable: true,
field: "posDictName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posTypeName",
align: "left",
label: "กลุ่มงาน",
sortable: true,
field: "posTypeName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posLevelName",
align: "left",
label: "ระดับชั้นงาน",
sortable: true,
field: "posLevelName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
const visibleColumns = ref<string[]>([
"no",
"posDictName",
"posTypeName",
"posLevelName",
]);
const formQuery = reactive<FormQuery>({
type: "positionName",
keyword: "",
});
const optionFilter = ref<DataOption[]>([
{ id: "positionName", name: "ชื่อตำแหน่ง" },
{ id: "positionType", name: "กลุ่มงาน" },
{ id: "positionLevel", name: "ระดับชั้นงาน" },
]);
const modalDialog = ref<boolean>(false);
const isStatusEdit = ref<boolean>(false);
const posId = ref<string>("");
const formDataPos = reactive({
posName: "",
posTypeName: "",
posLevelName: "",
});
const posNameRef = ref<object | null>(null);
const posTypeNameRef = ref<object | null>(null);
const posLevelNameRef = ref<object | null>(null);
const objectRef: ObjectPosRef = {
posName: posNameRef,
posTypeName: posTypeNameRef,
posLevelName: posLevelNameRef,
};
const posTypeMain = ref<ResGroup[]>([]);
const posTypeOp = ref<DataOption[]>([]);
const posLevelOp = ref<DataOption[]>([]);
function deletePos(id: string) {
dialogRemove($q, () => {
showLoader();
http
.delete(config.API.orgEmployeePosById(id))
.then(() => {
success($q, "ลบข้อมูลสำเร็จ");
fetchData();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
async function onClickOpenDialog(typeEdit: boolean = false, data: any = []) {
modalDialog.value = true;
isStatusEdit.value = typeEdit;
await fetchType();
updatePosTypeName(data.posTypeId);
if (data) {
posId.value = data.id;
setTimeout(() => {
formDataPos.posName = data.posDictName;
formDataPos.posTypeName = data.posTypeId;
formDataPos.posLevelName = data.posLevelId;
}, 200);
}
}
async function fetchData(statusFetch: boolean = false) {
showLoader();
await http
.get(
config.API.orgEmployeePos +
`?keyword=${formQuery.keyword}&type=${
statusFetch ? "" : formQuery.type
}`
)
.then((res) => {
rows.value = res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function fetchType() {
if (posTypeMain.value.length === 0) {
showLoader();
await http
.get(config.API.orgEmployeeType)
.then((res) => {
const data = res.data.result;
posTypeMain.value = data;
posTypeOp.value = data.map((e: ResGroup) => ({
id: e.id,
name: e.posTypeName,
}));
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
}
async function onClickSubmit() {
const hasError = [];
for (const key in objectRef) {
if (Object.prototype.hasOwnProperty.call(objectRef, key)) {
const property = objectRef[key];
if (property.value && typeof property.value.validate === "function") {
const isValid = property.value.validate();
hasError.push(isValid);
}
}
}
if (hasError.every((result) => result === true)) {
dialogConfirm($q, () => {
submit();
});
}
}
async function submit() {
const body = {
posDictName: formDataPos.posName,
posTypeId: formDataPos.posTypeName,
posLevelId: formDataPos.posLevelName,
};
showLoader();
try {
const url = !isStatusEdit.value
? config.API.orgEmployeePos
: config.API.orgEmployeePosById(posId.value);
await http[!isStatusEdit.value ? "post" : "put"](url, body);
success($q, "บันทีกข้อมูลสำเร็จ");
fetchData();
} catch (err) {
messageError($q, err);
} finally {
hideLoader();
closeDialog();
}
}
function updatePosTypeName(id: string) {
const posLevel = posTypeMain.value.find((e: ResGroup) => e.id === id);
posLevelOp.value =
posLevel?.posLevels.map((e: ResLevel) => ({
id: e.id,
name: e.posLevelName.toString(),
})) ?? [];
formDataPos.posLevelName = "";
}
function closeDialog() {
modalDialog.value = false;
formDataPos.posName = "";
formDataPos.posTypeName = "";
formDataPos.posLevelName = "";
}
onMounted(() => {
fetchData(true);
});
</script>
<template>
<div class="row col-12 q-mb-sm">
<div class="col-md-2">
<q-btn
id="addComplaints"
for="addComplaints"
size="12px"
flat
round
color="primary"
icon="mdi-plus"
@click="onClickOpenDialog()"
><q-tooltip>เพมตำเเหน </q-tooltip></q-btn
>
</div>
<div class="row col-md-10 q-col-gutter-sm">
<div class="col-md-4">
<q-select
label="ค้นหาจาก"
v-model="formQuery.type"
:options="optionFilter"
emit-value
dense
map-options
outlined
option-label="name"
option-value="id"
/>
</div>
<div class="col-md-6">
<q-input
ref="searchRef"
v-model="formQuery.keyword"
outlined
dense
lazy-rules
label="คำค้น"
hide-bottom-space
@keydown.enter="fetchData()"
>
<template v-slot:append>
<q-icon
v-if="formQuery.keyword"
name="cancel"
@click="(formQuery.keyword = ''), fetchData()"
class="cursor-pointer"
></q-icon>
</template>
</q-input>
</div>
<div class="row col-md-2">
<q-btn
color="primary"
icon="search"
label="ค้นหา"
class="full-width"
@click="fetchData()"
/>
</div>
</div>
</div>
<div class="full-width q-mt-sm">
<d-table
ref="table"
:columns="columns"
:rows="rows"
row-key="id"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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"
style="color: #000000; font-weight: 500"
>
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
<q-th auto-width></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 == 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="blue-6"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="mdi-content-copy"
clickable
@click.stop="
() => {
onClickOpenDialog(false, props.row);
}
"
>
<q-tooltip>ดลอกขอม</q-tooltip>
</q-btn>
<q-btn
color="edit"
flat
dense
round
size="12px"
icon="mdi-pencil"
clickable
@click.stop="onClickOpenDialog(true, props.row)"
v-close-popup
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="deletePos(props.row.id)"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
</div>
<q-dialog v-model="modalDialog" class="dialog" persistent>
<q-card style="width: 350px">
<form @submit.prevent="onClickSubmit">
<DialogHeader
:tittle="`${
isStatusEdit
? 'แก้ไขข้อมูลตำแหน่งลูกจ้างประจำ'
: 'เพิ่มข้อมูลตำแหน่งลูกจ้างประจำ'
}`"
:close="closeDialog"
/>
<q-separator />
<q-card-section>
<div class="row q-col-gutter-sm col-12">
<div class="col-12">
<q-input
ref="posNameRef"
v-model="formDataPos.posName"
dense
outlined
for="#positionName"
label="ชื่อตำแหน่ง"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกชื่อตำแหน่ง'}`]"
/>
</div>
<div class="col-6">
<q-select
ref="posTypeNameRef"
label="กลุ่มงาน"
v-model="formDataPos.posTypeName"
:options="posTypeOp"
emit-value
dense
map-options
outlined
option-label="name"
option-value="id"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณาเลือกกลุ่มงาน'}`]"
@update:model-value="updatePosTypeName"
/>
</div>
<div class="col-6">
<q-select
ref="posLevelNameRef"
label="ระดับชั้นงาน"
v-model="formDataPos.posLevelName"
:disable="formDataPos.posTypeName === ''"
:options="posLevelOp"
emit-value
dense
map-options
outlined
option-label="name"
option-value="id"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณาเลือกระดับชั้นงาน'}`]"
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn type="submit" :label="`บันทึก`" color="public" />
</q-card-actions>
</form>
</q-card>
</q-dialog>
</template>

View file

@ -0,0 +1,373 @@
<script setup lang="ts">
import { ref, onMounted, reactive } from "vue";
import { useRouter } from "vue-router";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
/** importType*/
import type { QTableProps } from "quasar";
import type { ObjectGroupRef } from "@/modules/01_metadata/interface/index/positionEmployee";
import type { ResGroup } from "@/modules/01_metadata/interface/response/positionEmployee/Main";
import type { FrmDataGroup } from "@/modules/01_metadata/interface/request/positionEmployee";
/** importComponents*/
import dialogHeader from "@/components/DialogHeader.vue";
/** importStore*/
import { usePositionEmployeeDataStore } from "@/modules/01_metadata/stores/positionEmployeeStore";
import { useCounterMixin } from "@/stores/mixin";
/** use*/
const $q = useQuasar();
const store = usePositionEmployeeDataStore();
const router = useRouter();
const mixin = useCounterMixin();
const {
dialogRemove,
dialogConfirm,
success,
messageError,
showLoader,
hideLoader,
} = mixin;
const columns = ref<QTableProps["columns"]>([
{
name: "posTypeName",
align: "left",
label: "กลุ่มงาน",
sortable: true,
field: "posTypeName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posTypeShortName",
align: "left",
label: "อักษรย่อกลุ่มงาน",
sortable: true,
field: "posTypeShortName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posTypeRank",
align: "left",
label: "ระดับกลุ่มงาน",
sortable: true,
field: "posTypeRank",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
const visibleColumns = ref<string[]>([
"posTypeName",
"posTypeShortName",
"posTypeRank",
]);
/** from เพิ่มข้อมูลกลุ่มงาน */
const formDataGroup = reactive<FrmDataGroup>({
posTypeName: "",
posTypeShortName: "",
posTypeRank: null,
});
/** formRef*/
const posTypeNameRef = ref<Object | null>(null);
const posTypeShortNameRef = ref<Object | null>(null);
const posTypeRankRef = ref<Object | null>(null);
const objectGroupRef: ObjectGroupRef = {
posTypeName: posTypeNameRef,
posTypeShortName: posTypeShortNameRef,
posTypeRank: posTypeRankRef,
};
const editId = ref<string>("");
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const dialogStatus = ref<string>("");
const rows = ref<ResGroup[]>([]);
async function fetchData() {
showLoader();
await http
.get(config.API.orgEmployeeType)
.then(async (res) => {
rows.value = res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function onClickSubmit() {
const hasError = [];
for (const key in objectGroupRef) {
if (Object.prototype.hasOwnProperty.call(objectGroupRef, key)) {
const property = objectGroupRef[key];
if (property.value && typeof property.value.validate === "function") {
const isValid = property.value.validate();
hasError.push(isValid);
}
}
}
if (hasError.every((result) => result === true)) {
dialogConfirm($q, () => {
submit();
});
}
}
async function submit() {
const body: FrmDataGroup = {
posTypeName: formDataGroup.posTypeName,
posTypeShortName: formDataGroup.posTypeShortName,
posTypeRank: Number(formDataGroup.posTypeRank),
};
showLoader();
try {
const url =
dialogStatus.value === "create"
? config.API.orgEmployeeType
: config.API.orgEmployeeTypeById(editId.value);
await http[dialogStatus.value === "create" ? "post" : "put"](url, body);
success($q, "บันทีกข้อมูลสำเร็จ");
fetchData();
} catch (err) {
messageError($q, err);
} finally {
hideLoader();
closeDialog();
}
}
function onClickDelete(id: string) {
dialogRemove($q, () => {
showLoader();
http
.delete(config.API.orgEmployeeTypeById(id))
.then(() => {
fetchData();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
function onClickOpenDialogEdit(data: ResGroup) {
dialogStatus.value = "edit";
dialog.value = true;
editId.value = data.id;
formDataGroup.posTypeName = data.posTypeName;
formDataGroup.posTypeShortName = data.posTypeShortName;
formDataGroup.posTypeRank = data.posTypeRank;
}
function onClickDetail(id: string) {
router.push(`/master-data/position-employee/level/${id}`);
}
function closeDialog() {
dialog.value = false;
clearFormData();
}
function clearFormData() {
formDataGroup.posTypeName = "";
formDataGroup.posTypeShortName = "";
formDataGroup.posTypeRank = null;
}
onMounted(() => {
fetchData();
});
</script>
<template>
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input outlined dense v-model="filterKeyword" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="rows"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
:rows-per-page-options="[10, 20, 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"
@click.stop="onClickDetail(props.row.id)"
>
<q-td v-for="col in props.cols" :key="col.id">
{{ col.value ? col.value : "-" }}
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
@click.stop="onClickOpenDialogEdit(props.row)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="onClickDelete(props.row.id)"
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
<q-dialog v-model="dialog" class="dialog" persistent>
<q-card style="width: 350px">
<form @submit.prevent="onClickSubmit">
<q-card-section class="flex justify-between" style="padding: 0">
<dialog-header
:tittle="
dialogStatus === 'edit'
? 'แก้ไขข้อมูลกลุ่มงาน'
: 'เพิ่มข้อมูลกลุ่มงาน'
"
:close="closeDialog"
/>
</q-card-section>
<q-separator color="grey-4" />
<q-card-section class="row q-gutter-y-md">
<div class="col-12">
<q-input
ref="posTypeNameRef"
outlined
v-model="formDataGroup.posTypeName"
label="ชื่อกลุ่มงาน"
dense
lazy-rules
borderless
bg-color="white"
:rules="[(val) => val.length > 0 || 'กรุณากรอกชื่อกลุ่มงาน']"
hide-bottom-space
/>
</div>
<div class="col-12">
<q-input
ref="posTypeShortNameRef"
v-model="formDataGroup.posTypeShortName"
dense
outlined
for="#positionShortName"
label="อักษรย่อกลุ่มงาน"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกอักษรย่อกลุ่มงาน'}`]"
/>
</div>
<div class="col-12">
<q-input
ref="posTypeRankRef"
outlined
v-model="formDataGroup.posTypeRank"
label="ระดับกลุ่มงาน"
dense
lazy-rules
borderless
min="1"
bg-color="white"
:rules="[(val) => val != undefined || 'กรุณากรอกระดับกลุ่มงาน']"
hide-bottom-space
mask="############"
/>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
id="onSubmit"
type="submit"
dense
unelevated
label="บันทึก"
color="public"
class="q-px-md"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</form>
</q-card>
</q-dialog>
</template>
<style scoped lang="scss"></style>

View file

@ -0,0 +1,408 @@
<script setup lang="ts">
import { ref, onMounted, reactive } from "vue";
import { useQuasar } from "quasar";
import { useRouter, useRoute } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
/** importType*/
import type { QTableProps } from "quasar";
import type {
ResGroup,
ResLevel,
} from "@/modules/01_metadata/interface/response/positionEmployee/Main";
import type { ObjectLevelRef } from "@/modules/01_metadata/interface/index/positionEmployee";
import type { FormDataLevel } from "@/modules/01_metadata/interface/request/positionEmployee";
/** importComponts*/
import DialogHeader from "@/components/DialogHeader.vue";
/** importStore*/
import { useMainOptionDataStore } from "@/modules/01_metadata/stores/main";
import { useCounterMixin } from "@/stores/mixin";
/**use*/
const $q = useQuasar();
const route = useRoute();
const router = useRouter();
const posName = ref<string>("");
const posTypeId = ref<string>(route.params.id.toString());
const {
dialogRemove,
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
} = useCounterMixin();
const storeOption = useMainOptionDataStore();
const columns = ref<QTableProps["columns"]>([
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: false,
field: "no",
headerStyle: "font-size: 14px; width:0px",
style: "font-size: 14px",
},
{
name: "posLevelName",
align: "left",
label: "ระดับชั้นงาน",
sortable: true,
field: "posLevelName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posTypeName",
align: "left",
label: "กลุ่มงาน",
sortable: true,
field: "posTypeName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posLevelAuthority",
align: "left",
label: "ผู้มีอำนาจสั่งบรรจุ",
sortable: true,
field: "posLevelAuthority",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
const rows = ref<any>([]);
const visibleColumns = ref<string[]>([
"no",
"posLevelName",
"posTypeName",
"posLevelAuthority",
]);
const filter = ref<string>("");
const levelId = ref<string>("");
const titleName = ref<string | null>("");
const formDataLevel = reactive<FormDataLevel>({
posLevelName: null,
posTypeName: "",
posLevelAuthority: "",
});
/** formRef*/
const posLevelNameRef = ref<Object | null>(null);
const commanderRef = ref<Object | null>(null);
const objectLevelRef: ObjectLevelRef = {
posLevelName: posLevelNameRef,
posLevelAuthority: commanderRef,
};
const id = ref<string>(route.params.id.toString());
function fetchData() {
showLoader();
http
.get(config.API.orgEmployeeTypeById(id.value))
.then((res) => {
titleName.value = res.data.result.posTypeName ?? null;
formDataLevel.posTypeName = res.data.result.posTypeName;
rows.value = res.data.result.posLevels.map((x: any) => ({
...x,
posTypeName: res.data.result.posTypeName,
}));
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
const isStatusEdit = ref<boolean>(false);
const modalDialog = ref<boolean>(false);
function onClickOpenDialog(statusEdit: boolean = false, data: any = []) {
isStatusEdit.value = statusEdit;
modalDialog.value = true;
if (statusEdit) {
levelId.value = data.id;
formDataLevel.posLevelName = data.posLevelName;
formDataLevel.posTypeName = titleName.value;
formDataLevel.posLevelAuthority = data.posLevelAuthority;
} else {
formDataLevel.posTypeName = titleName.value ? titleName.value : "";
}
}
function onClickCloseDialog() {
modalDialog.value = false;
formDataLevel.posLevelName = null;
formDataLevel.posTypeName = "";
formDataLevel.posLevelAuthority = "";
}
function onClickSubmit() {
const hasError = [];
for (const key in objectLevelRef) {
if (Object.prototype.hasOwnProperty.call(objectLevelRef, key)) {
const property = objectLevelRef[key];
if (property.value && typeof property.value.validate === "function") {
const isValid = property.value.validate();
hasError.push(isValid);
}
}
}
if (hasError.every((result) => result === true)) {
dialogConfirm($q, () => {
submit();
});
}
}
async function submit() {
const body = {
posLevelName: Number(formDataLevel.posLevelName),
posTypeId: posTypeId.value,
posLevelRank: Number(formDataLevel.posLevelName),
posLevelAuthority: formDataLevel.posLevelAuthority,
};
showLoader();
try {
const url = !isStatusEdit.value
? config.API.orgEmployeelevel
: config.API.orgEmployeelevelById(levelId.value);
await http[!isStatusEdit.value ? "post" : "put"](url, body);
success($q, "บันทีกข้อมูลสำเร็จ");
fetchData();
onClickCloseDialog();
} catch (err) {
messageError($q, err);
} finally {
hideLoader();
}
}
function onClickDelete(id: string) {
dialogRemove($q, () => {
http
.delete(config.API.orgEmployeelevelById(id))
.then(() => {
success($q, "ลบข้อมูลสำเร็จ");
fetchData();
})
.catch((err) => {
messageError($q, err);
});
});
}
function convertPosLevelAuthority(val: string) {
const result = storeOption.posLevelAuthorityOption.find((e) => e.id === val);
return result?.label;
}
onMounted(() => {
posTypeId.value && fetchData();
});
</script>
<template>
<div class="toptitle text-dark col-12 row items-center">
<q-btn
icon="mdi-arrow-left"
unelevated
round
dense
flat
color="primary"
class="q-mr-sm"
@click="router.go(-1)"
/>
รายการระดบชนงาน{{ titleName }}
</div>
<q-card flat bordered>
<div class="q-pa-md">
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop.pervent="onClickOpenDialog()"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input outlined dense v-model="filter" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="rows"
:filter="filter"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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-bold">{{ 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, index) in props.cols" :key="col.id">
<div v-if="col.name == 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-else-if="col.name === 'posLevelAuthority'">
{{ col.value ? convertPosLevelAuthority(col.value) : "-" }}
</div>
<div v-else>
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
@click.stop.pervent="onClickOpenDialog(true, props.row)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
@click="onClickDelete(props.row.id)"
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
</div>
</q-card>
<q-dialog v-model="modalDialog" class="dialog" persistent>
<q-card style="width: 350px">
<form @submit.prevent="onClickSubmit">
<q-card-section class="flex justify-between" style="padding: 0">
<DialogHeader
:tittle="isStatusEdit ? 'แก้ไขข้อมูล' : 'เพิ่มข้อมูล'"
:close="onClickCloseDialog"
/>
</q-card-section>
<q-separator color="grey-4" />
<q-card-section class="row q-gutter-y-md">
<div class="col-12">
<q-input
outlined
ref="posLevelNameRef"
v-model="formDataLevel.posLevelName"
label="ระดับชั้นงาน"
dense
lazy-rules
borderless
bg-color="white"
hide-bottom-space
mask="#######################################"
:rules="[(val) => !!val || 'กรุณากรอกระดับชั้นงาน']"
/>
</div>
<div class="col-12">
<q-select
ref="commanderRef"
outlined
v-model="formDataLevel.posLevelAuthority"
emit-value
map-options
:options="storeOption.posLevelAuthorityOption"
option-value="id"
label="ผู้มีอำนาจสั่งบรรจุ"
dense
lazy-rules
borderless
bg-color="white"
hide-bottom-space
:rules="[(val) => !!val || 'กรุณาเลือกผู้มีอำนาจสั่งบรรจุ']"
/>
</div>
<div class="col-12">
<q-select
ref="posTypeIdRef"
v-model="formDataLevel.posTypeName"
label="กลุ่มงาน"
outlined
dense
bg-color="white"
options-cover
hide-bottom-space
readonly
/>
</div>
</q-card-section>
<q-card-actions align="right">
<q-btn
id="onSubmit"
type="submit"
dense
unelevated
label="บันทึก"
color="public"
class="q-px-md"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</form>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -0,0 +1,447 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { useCounterMixin } from "@/stores/mixin";
import http from "@/plugins/http";
import config from "@/app.config";
import { useQuasar } from "quasar";
import type {
DataOption,
FormPositionSelect,
ListMenu,
RowDetailPositions,
} from "@/modules/01_metadata/interface/request/position/index";
import type { QTableProps } from "quasar";
import DialogAddPosition from "@/modules/01_metadata/components/position/DialogAddPosition.vue";
const editPosition = ref<boolean>(false);
const modalAddPosition = ref<boolean>(false);
const levelOpsMain = ref<DataOption[]>([]);
const dataLevel = ref<any>();
const levelOps = ref<DataOption[]>([]);
const isSpecial = ref<boolean>(false);
const formPositionSelect = reactive<FormPositionSelect>({
positionId: "",
posTypeId: "",
positionName: "",
positionField: "",
positionType: "",
positionLevel: "",
positionExecutive: "",
positionExecutiveField: "",
positionArea: "",
isSpecial: false,
});
const rows = ref<RowDetailPositions[]>([]);
const listMenu = ref<ListMenu[]>([
{
label: "คัดลอก",
icon: "mdi-content-copy",
type: "copy",
color: "blue-6",
},
{
label: "ลบ",
icon: "delete",
type: "remove",
color: "red",
},
]);
const columns = ref<QTableProps["columns"]>([
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: false,
field: "no",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionName",
align: "left",
label: "ตำแหน่งในสายงาน",
sortable: true,
field: "positionName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionField",
align: "left",
label: "สายงาน",
sortable: true,
field: "positionField",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posTypeName",
align: "left",
label: "ประเภทตำเเหน่ง",
sortable: true,
field: "posTypeName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posLevelName",
align: "left",
label: "ระดับตำแหน่ง",
sortable: true,
field: "posLevelName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posExecutiveName",
align: "left",
label: "ตำแหน่งทางการบริหาร",
sortable: true,
field: "posExecutiveName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionExecutiveField",
align: "left",
label: "ด้านทางการบริหาร",
sortable: true,
field: "positionExecutiveField",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionArea",
align: "left",
label: "ด้าน/สาขา",
sortable: true,
field: "positionArea",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
const visibleColumns = ref<string[]>([
"no",
"positionName",
"positionField",
"posTypeName",
"posLevelName",
"posExecutiveName",
"positionExecutiveField",
"positionArea",
]);
const $q = useQuasar();
const rowsPositionSelect = ref<RowDetailPositions[]>([]);
const mixin = useCounterMixin();
const {
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
dialogRemove,
} = mixin;
/** input ค้นหา */
const searchRef = ref<any>(null);
const search = ref<string>("");
const type = ref<string>("positionName");
const isReadonly = ref<boolean>(false); //
const optionFilter = ref<DataOption[]>([
{ id: "positionName", name: "ตำแหน่งในสายงาน" },
{ id: "positionField", name: "สายงาน" },
{ id: "positionType", name: "ประเภทตำแหน่ง" },
{ id: "positionLevel", name: "ระดับตำแหน่ง" },
{ id: "positionExecutive", name: "ตำแหน่งทางการบริหาร" },
{ id: "positionExecutiveField", name: "ด้านทางการบริหาร" },
{ id: "positionArea", name: "ด้าน/สาขา" },
]);
/**
* ดลอกขอม
* @param data อมลตำแหน
*/
function copyDetiail(data: RowDetailPositions) {
formPositionSelect.positionId = data.id;
formPositionSelect.posTypeId = data.posTypeId;
formPositionSelect.positionName = data.positionName;
formPositionSelect.positionField = data.positionField;
formPositionSelect.positionType = data.posTypeId;
formPositionSelect.positionLevel = data.posLevelId;
formPositionSelect.positionExecutive = data.posExecutiveId;
formPositionSelect.positionExecutiveField = data.positionExecutiveField;
formPositionSelect.positionArea = data.positionArea;
modalAddPosition.value = true;
}
/**
* แกไขขอม
* @param data อมลตำแหน
*/
function editDetiail(data: RowDetailPositions) {
console.log(data);
formPositionSelect.positionId = data.id;
formPositionSelect.posTypeId = data.posTypeId;
formPositionSelect.positionName = data.positionName;
formPositionSelect.positionField = data.positionField;
formPositionSelect.positionType = data.posTypeId;
formPositionSelect.positionLevel = data.posLevelId;
formPositionSelect.positionExecutive = data.posExecutiveId;
formPositionSelect.positionExecutiveField = data.positionExecutiveField;
formPositionSelect.positionArea = data.positionArea;
formPositionSelect.isSpecial = data.isSpecial;
modalAddPosition.value = true;
editPosition.value = true;
}
/**
* งค css ออกไปตามเงอนไข
* @param val true/false
*/
function inputEdit(val: boolean) {
return {
"full-width cursor-pointer inputgreen ": val,
"full-width cursor-pointer inputgreen": !val,
};
}
async function searchInput() {
showLoader();
await http
.get(
config.API.orgPosPosition + `?keyword=${search.value}&type=${type.value}`
)
.then((res) => {
rowsPositionSelect.value = res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function addPosition(data: RowDetailPositions) {
const isIdExist = rows.value.some((item: any) => item.id === data.id);
if (!isIdExist) {
rows.value = [data, ...rows.value];
}
}
function deletePos(id: string) {
dialogRemove($q, () => {
showLoader();
http
.delete(config.API.orgPosPositionById(id))
.then(() => {
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
searchInput();
hideLoader();
});
});
}
function popupAdd() {
modalAddPosition.value = true;
}
async function fetchData() {
search.value = "";
showLoader();
await http
.get(config.API.orgPosPosition + `?keyword=&type=ALL`)
.then((res) => {
rowsPositionSelect.value = res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(() => {
fetchData();
});
</script>
<template>
<div class="row col-12 q-mb-sm">
<div class="col-md-2">
<q-btn
id="addComplaints"
for="addComplaints"
size="12px"
flat
round
color="primary"
icon="mdi-plus"
@click="popupAdd()"
><q-tooltip>เพมตำเเหน </q-tooltip></q-btn
>
</div>
<div class="row col-md-10 q-col-gutter-sm">
<div class="col-md-4">
<q-select
label="ค้นหาจาก"
v-model="type"
:options="optionFilter"
emit-value
dense
map-options
outlined
option-label="name"
option-value="id"
/>
</div>
<div class="col-md-6">
<q-input
ref="searchRef"
:class="inputEdit(isReadonly)"
v-model="search"
outlined
dense
lazy-rules
label="คำค้น"
hide-bottom-space
>
<template v-slot:append>
<q-icon
v-if="search"
name="cancel"
@click="fetchData()"
class="cursor-pointer"
></q-icon>
</template>
</q-input>
</div>
<div class="row col-md-2">
<q-btn
color="primary"
icon="search"
label="ค้นหา"
class="full-width"
@click="searchInput()"
/>
</div>
</div>
</div>
<div class="full-width q-mt-sm">
<d-table
ref="table"
:columns="columns"
:rows="rowsPositionSelect"
row-key="id"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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"
style="color: #000000; font-weight: 500"
>
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
<q-th auto-width></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"
@click="addPosition(props.row)"
>
<div v-if="col.name == 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-else-if="col.name === 'posLevelName'">
{{
props.row.posLevelName
? props.row.isSpecial == true
? `${props.row.posLevelName} (ฉ)`
: props.row.posLevelName
: "-"
}}
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="blue-6"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="mdi-content-copy"
clickable
@click.stop="
() => {
copyDetiail(props.row);
}
"
>
<q-tooltip>ดลอกขอม</q-tooltip>
</q-btn>
<q-btn
color="edit"
flat
dense
round
size="12px"
icon="mdi-pencil"
clickable
@click.stop="editDetiail(props.row)"
v-close-popup
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="deletePos(props.row.id)"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
</div>
<DialogAddPosition
v-model:add-position="modalAddPosition"
v-model:form-data="formPositionSelect"
v-model:edit-check="editPosition"
:get-data="searchInput"
/>
</template>

View file

@ -0,0 +1,430 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type { QInput, QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRouter } from "vue-router";
import { usePositionTypeDataStore } from "@/modules/01_metadata/stores/positionTypeStore";
import dialogHeader from "@/components/DialogHeader.vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
const store = usePositionTypeDataStore();
const router = useRouter();
const mixin = useCounterMixin();
const {
dialogRemove,
dialogConfirm,
success,
messageError,
showLoader,
hideLoader,
} = mixin;
const columns = [
{
name: "posTypeName",
align: "left",
label: "ประเภทตำแหน่ง",
sortable: true,
field: "posTypeName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posTypeRank",
align: "left",
label: "ระดับตำแหน่ง",
sortable: true,
field: "posTypeRank",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "left",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
] as const satisfies QTableProps["columns"];
const $q = useQuasar();
const editId = ref<string>("");
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const posTypeName = ref<string>("");
const posTypeNameRef = ref<QInput | null>(null);
const posTypeRank = ref<number | null>(null);
const posTypeRankRef = ref<QInput | null>(null);
const dialogStatus = ref<string>("");
const visibleColumns = ref<string[]>(["posTypeName", "posTypeRank"]);
async function fetchData() {
showLoader();
await http
.get(config.API.orgPosType)
.then(async (res) => {
store.save(res.data.result);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
await http
.post(config.API.orgPosType, {
posTypeName: posTypeName.value,
posTypeRank: posTypeRank.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(id: string) {
await http
.put(config.API.orgPosTypeId(id), {
posTypeName: posTypeName.value,
posTypeRank: posTypeRank.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.orgPosTypeId(id))
.then(() => {
fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(async () => {
fetchData();
});
function closeDialog() {
dialog.value = false;
}
function onclickDetail(id: string) {
router.push(`/master-data/position/level/${id}`);
}
function validateForm() {
posTypeNameRef.value?.validate();
posTypeRankRef.value?.validate();
onSubmit();
}
async function onSubmit() {
if (posTypeName.value.length > 0 && posTypeRank.value !== null) {
dialogConfirm(
$q,
async () => {
dialogStatus.value === "create" ? addData() : editData(editId.value);
closeDialog();
posTypeName.value = "";
posTypeRank.value = null;
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
}
</script>
<template>
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input outlined dense v-model="filterKeyword" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="store.row"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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"
@click.stop="onclickDetail(props.row.id)"
>
<q-td v-for="col in props.cols" :key="col.id">
{{ col.value }}
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
dialog = true;
editId = props.row.id;
posTypeName = props.row.posTypeName;
posTypeRank = props.row.posTypeRank;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
<q-dialog v-model="dialog" class="dialog" persistent>
<q-card style="min-width: 350px">
<form @submit.prevent="validateForm">
<q-card-section class="flex justify-between" style="padding: 0">
<dialog-header
:tittle="dialogStatus === 'edit' ? 'แก้ไขข้อมูล' : 'เพิ่มข้อมูล'"
:close="closeDialog"
/>
</q-card-section>
<q-separator color="grey-4" />
<q-card-section class="q-pa-none">
<div class="col-12 q-ma-md">
<q-input
ref="posTypeNameRef"
outlined
v-model="posTypeName"
label="ประเภทตำแหน่ง"
dense
lazy-rules
borderless
:rules="[(val) => val.length > 0 || 'กรุณากรอกประเภทตำแหน่ง']"
hide-bottom-space
/>
</div>
<div class="col-12 q-ma-md">
<q-input
ref="posTypeRankRef"
outlined
v-model="posTypeRank"
label="ระดับตำแหน่ง"
dense
lazy-rules
borderless
min="1"
:rules="[(val) => val != undefined || 'กรุณากรอกระดับตำแหน่ง']"
hide-bottom-space
mask="############"
/>
</div>
</q-card-section>
<q-card-actions align="right">
<q-btn
id="onSubmit"
type="submit"
dense
unelevated
label="บันทึก"
color="public"
class="q-px-md"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</form>
</q-card>
</q-dialog>
</template>
<style scoped lang="scss">
.border_custom {
border-radius: 6px !important;
border: 1px solid #e1e1e1;
}
$toggle-background-color-on: #06884d;
$toggle-background-color-off: darkgray;
$toggle-control-color: white;
$toggle-width: 40px;
$toggle-height: 25px;
$toggle-gutter: 3px;
$toggle-radius: 50%;
$toggle-control-speed: 0.15s;
$toggle-control-ease: ease-in;
// These are our computed variables
// change at your own risk.
$toggle-radius: $toggle-height / 2;
$toggle-control-size: $toggle-height - ($toggle-gutter * 2);
.toggle-control {
display: block;
position: relative;
padding-left: $toggle-width;
margin-bottom: 12px;
cursor: pointer;
font-size: 22px;
user-select: none;
input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
input:checked ~ .control {
background-color: $toggle-background-color-on;
&:after {
left: $toggle-width - $toggle-control-size - $toggle-gutter;
}
}
.control {
position: absolute;
top: -7px;
left: -15px;
height: $toggle-height;
width: $toggle-width;
border-radius: $toggle-radius;
background-color: $toggle-background-color-off;
transition: background-color $toggle-control-speed $toggle-control-ease;
&:after {
content: "";
position: absolute;
left: $toggle-gutter;
top: $toggle-gutter;
width: $toggle-control-size;
height: $toggle-control-size;
border-radius: $toggle-radius;
background: $toggle-control-color;
transition: left $toggle-control-speed $toggle-control-ease;
}
}
}
</style>

View file

@ -0,0 +1,30 @@
<script setup lang="ts">
import { ref } from "vue";
import { useRouter } from "vue-router";
import ListLevelDetail from "@/modules/01_metadata/components/position/05ListLevelDetail.vue";
const router = useRouter();
const posName = ref<string>("");
</script>
<template>
<div class="toptitle text-dark col-12 row items-center">
<q-btn
icon="mdi-arrow-left"
unelevated
round
dense
flat
color="primary"
class="q-mr-sm"
@click="router.go(-1)"
/>
รายการระดบตำแหนงของ {{ posName }}
</div>
<q-card flat bordered>
<ListLevelDetail v-model:posName="posName" />
</q-card>
</template>
<style scoped></style>

View file

@ -0,0 +1,242 @@
<script setup lang="ts">
import { onMounted, ref } from "vue";
import type { QTableProps } from "quasar";
import { useQuasar } from "quasar";
import type {
RowListForm,
ListMenu,
} from "@/modules/01_metadata/interface/request/position";
import http from "@/plugins/http";
import config from "@/app.config";
import DialogAdd from "@/modules/01_metadata/components/position/DialogAddExecutive.vue";
import { useCounterMixin } from "@/stores/mixin";
const $q = useQuasar();
const mixin = useCounterMixin();
const {
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
dialogRemove,
} = mixin;
const dataEdit = ref<any>();
const isEdit = ref<boolean>(false);
const modalAdd = ref<boolean>(false);
const filterKeyword = ref<string>("");
const rows = ref<RowListForm[]>([]);
const listMenu = ref<ListMenu[]>([
{
label: "แก้ไข",
icon: "mdi-pencil",
type: "copy",
color: "teal-6",
},
{
label: "ลบ",
icon: "delete",
type: "remove",
color: "red",
},
]);
const visibleColumns = ref<string[]>([
"no",
"posExecutiveName",
"posExecutivePriority",
]);
const columns = ref<QTableProps["columns"]>([
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: false,
field: "no",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posExecutiveName",
align: "left",
label: "ชื่อตำแหน่งทางการบริหาร",
sortable: true,
field: "posExecutiveName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posExecutivePriority",
align: "left",
label: "ลำดับความสำคัญ",
sortable: true,
field: "posExecutivePriority",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
function getData() {
showLoader();
http
.get(config.API.orgPosExecutive)
.then((res) => {
const dataList = res.data.result;
rows.value = dataList;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
function popUpAdd() {
modalAdd.value = true;
}
function editPopUp(data: RowListForm[]) {
dataEdit.value = data;
modalAdd.value = true;
isEdit.value = true;
}
function deletePos(id: string) {
dialogRemove($q, () => {
showLoader();
http
.delete(config.API.orgPosExecutiveById(id))
.then(() => {
success($q, "ลบข้อมูลสำเร็จ");
getData();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
onMounted(() => {
getData();
});
</script>
<template>
<div class="row col-12 q-col-gutter-x-sm">
<div class="col-2">
<q-btn
size="12px"
flat
round
color="primary"
icon="mdi-plus"
@click="popUpAdd()"
>
<q-tooltip>เพมตำแหนงทางการบรหาร</q-tooltip>
</q-btn>
</div>
<q-space />
<q-input outlined dense v-model="filterKeyword" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
<div class="col-12 q-mt-sm">
<d-table
ref="table"
:columns="columns"
:rows="rows"
row-key="id"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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"
style="color: #000000; font-weight: 500"
>
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
<q-th auto-width></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 == 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-else>
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
editPopUp(props.row);
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="deletePos(props.row.id)"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
</div>
<DialogAdd
v-model:executive="modalAdd"
v-model:form-data="dataEdit"
v-model:edit="isEdit"
:get-data="getData"
/>
</template>

View file

@ -0,0 +1,543 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import type { QInput, QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRoute } from "vue-router";
import { usePositionDataStore } from "@/modules/01_metadata/stores/positionListStore";
import { useMainOptionDataStore } from "@/modules/01_metadata/stores/main";
import { usePositionTypeDataStore } from "@/modules/01_metadata/stores/positionTypeStore";
import dialogHeader from "@/components/DialogHeader.vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
const store = usePositionDataStore();
const storeOption = useMainOptionDataStore();
const storeName = usePositionTypeDataStore();
const mixin = useCounterMixin();
const posName = defineModel<string>("posName", {
required: true,
});
const {
dialogRemove,
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
} = mixin;
const $q = useQuasar();
const columns = [
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: false,
field: "no",
headerStyle: "font-size: 14px; width:0px",
style: "font-size: 14px",
},
{
name: "posLevelName",
align: "left",
label: "ชื่อระดับตำแหน่ง",
sortable: true,
field: "posLevelName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posTypeName",
align: "left",
label: "ประเภทตำแหน่ง",
sortable: true,
field: "posTypeName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posLevelRank",
align: "left",
label: "ระดับของระดับตำแหน่ง",
sortable: true,
field: "posLevelRank",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posLevelAuthority",
align: "left",
label: "ผู้มีอำนาจสั่งบรรจุ",
sortable: true,
field: "posLevelAuthority",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "center",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "center",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
] as const satisfies QTableProps["columns"];
const route = useRoute();
const id = ref<string>(route.params.id.toString());
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const dialogStatus = ref<string>("");
const editId = ref<string>("");
const posLevelName = ref<string>("");
const posLevelRank = ref<number>();
const posLevelAuthority = ref<string>("");
const posLevelNameRef = ref<QInput | null>(null);
const posLevelRankRef = ref<QInput | null>(null);
const posTypeIdRef = ref<QInput | null>(null);
const posLevelAuthorityRef = ref<QInput | null>(null);
const visibleColumns = ref<string[]>([
"no",
"posTypeName",
"posLevelName",
"posLevelRank",
"posLevelAuthority",
]);
async function fetchData() {
showLoader();
await http
.get(config.API.orgPosTypeId(id.value))
.then(async (res) => {
posName.value = res.data.result.posTypeName;
store.save(res.data.result.posLevels);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
await http
.post(config.API.orgPosLevel, {
posLevelName: posLevelName.value,
posLevelRank: posLevelRank.value,
posLevelAuthority:
posLevelAuthority.value == "" ? "" : posLevelAuthority.value,
posTypeId: id.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(editId: string) {
await http
.put(config.API.orgPosLevelId(editId), {
posLevelName: posLevelName.value,
posLevelRank: posLevelRank.value,
posLevelAuthority:
posLevelAuthority.value == "" ? "" : posLevelAuthority.value,
posTypeId: id.value,
})
.then(() => {
fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(id: string) {
await http
.delete(config.API.orgPosLevelId(id))
.then(() => {
fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function closeDialog() {
dialog.value = false;
}
function validateForm() {
posLevelNameRef.value?.validate();
posLevelRankRef.value?.validate();
posTypeIdRef.value?.validate();
posLevelAuthorityRef.value?.validate();
onSubmit();
}
async function onSubmit() {
if (
posLevelName.value.length > 0 &&
posLevelAuthority.value.length > 0 &&
posLevelRank.value !== undefined &&
posLevelRank.value > 0
) {
dialogConfirm(
$q,
async () => {
dialogStatus.value === "create" ? addData() : editData(editId.value);
closeDialog();
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
}
onMounted(async () => {
fetchData();
});
</script>
<template>
<div class="q-pa-md">
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
posLevelName = '';
posLevelRank = undefined;
posLevelAuthority = '';
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input outlined dense v-model="filterKeyword" label="ค้นหา"></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="store.row"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
: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-bold">{{ 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, index) in props.cols" :key="col.id">
<div v-if="col.name === 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-if="col.name === 'posTypeName'">
{{ posName }}
</div>
<div v-else>
{{ col.value }}
</div>
</q-td>
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
size="12px"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
editId = props.row.id;
posLevelName = props.row.posLevelName;
posLevelRank = props.row.posLevelRank;
if (props.row.posLevelAuthority === 'หัวหน้าหน่วยงาน') {
posLevelAuthority = 'HEAD';
} else if (props.row.posLevelAuthority === 'ปลัด') {
posLevelAuthority = 'DEPUTY';
} else {
posLevelAuthority = 'GOVERNOR';
}
dialog = true;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
size="12px"
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
</d-table>
<q-dialog v-model="dialog" class="dialog" persistent>
<q-card style="width: 350px">
<form @submit.prevent="validateForm">
<q-card-section class="flex justify-between" style="padding: 0">
<dialog-header
:tittle="dialogStatus == 'edit' ? 'แก้ไขข้อมูล' : 'เพิ่มข้อมูล'"
:close="closeDialog"
/>
</q-card-section>
<q-separator color="grey-4" />
<q-card-section class="row q-gutter-y-md">
<div class="col-12">
<q-input
outlined
ref="posLevelNameRef"
v-model="posLevelName"
label="ชื่อระดับตำแหน่ง"
dense
lazy-rules
borderless
bg-color="white"
hide-bottom-space
:rules="[(val) => val.length > 0 || 'กรุณากรอกระดับตำแหน่ง']"
/>
</div>
<div class="col-12">
<q-input
ref="posLevelRankRef"
outlined
v-model="posLevelRank"
label="ระดับ"
dense
lazy-rules
borderless
min="1"
bg-color="white"
:rules="[(val) => val > 0 || 'กรุณากรอกระดับ']"
hide-bottom-space
mask="############"
/>
</div>
<div class="col-12">
<q-select
ref="posLevelAuthorityRef"
outlined
v-model="posLevelAuthority"
emit-value
map-options
:options="storeOption.posLevelAuthorityOption"
option-value="id"
label="ผู้มีอำนาจสั่งบรรจุ"
dense
lazy-rules
:rules="[(val) => !!val || 'กรุณาเลือกผู้มีอำนาจสั่งบรรจุ']"
borderless
bg-color="white"
hide-bottom-space
/>
</div>
<div class="col-12">
<q-select
ref="posTypeIdRef"
v-model="posName"
label="ประเภทตำแหน่ง"
outlined
dense
bg-color="white"
options-cover
hide-bottom-space
readonly
/>
</div>
</q-card-section>
<q-card-actions align="right">
<q-btn
id="onSubmit"
type="submit"
dense
unelevated
label="บันทึก"
color="public"
class="q-px-md"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</form>
</q-card>
</q-dialog>
</div>
</template>
<style scoped lang="scss">
.border_custom {
border-radius: 6px !important;
border: 1px solid #e1e1e1;
}
$toggle-background-color-on: #06884d;
$toggle-background-color-off: darkgray;
$toggle-control-color: white;
$toggle-width: 40px;
$toggle-height: 25px;
$toggle-gutter: 3px;
$toggle-radius: 50%;
$toggle-control-speed: 0.15s;
$toggle-control-ease: ease-in;
// These are our computed variables
// change at your own risk.
$toggle-radius: $toggle-height / 2;
$toggle-control-size: $toggle-height - ($toggle-gutter * 2);
.toggle-control {
display: block;
position: relative;
padding-left: $toggle-width;
margin-bottom: 12px;
cursor: pointer;
font-size: 22px;
user-select: none;
input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
input:checked ~ .control {
background-color: $toggle-background-color-on;
&:after {
left: $toggle-width - $toggle-control-size - $toggle-gutter;
}
}
.control {
position: absolute;
top: -7px;
left: -15px;
height: $toggle-height;
width: $toggle-width;
border-radius: $toggle-radius;
background-color: $toggle-background-color-off;
transition: background-color $toggle-control-speed $toggle-control-ease;
&:after {
content: "";
position: absolute;
left: $toggle-gutter;
top: $toggle-gutter;
width: $toggle-control-size;
height: $toggle-control-size;
border-radius: $toggle-radius;
background: $toggle-control-color;
transition: left $toggle-control-speed $toggle-control-ease;
}
}
}
</style>

View file

@ -0,0 +1,214 @@
<script setup lang="ts">
import { ref, reactive, watch } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps } from "quasar";
import DialogHeader from "@/components/DialogHeader.vue";
import type {
FormExecutiveRef,
RowListForm,
} from "@/modules/01_metadata/interface/request/position/index";
import { useCounterMixin } from "@/stores/mixin";
// import { bo } from "@fullcalendar/core/internal-common";
const { dialogConfirm, showLoader, success, hideLoader, messageError } =
useCounterMixin();
const formExecutive = reactive<RowListForm>({
id: "",
posExecutiveName: "",
posExecutivePriority: null,
});
const $q = useQuasar();
const isReadonly = ref<boolean>(false); //
const modal = defineModel<boolean>("executive", { required: true });
const formData = defineModel<any>("formData", { required: true });
const isEdit = defineModel<any>("edit", { required: true });
const posExecutiveNameRef = ref<Object | null>(null);
const posExecutivePriorityRef = ref<Object | null>(null);
const objectExecutiveRef: FormExecutiveRef = {
posExecutiveName: posExecutiveNameRef,
posExecutivePriority: posExecutivePriorityRef,
};
const props = defineProps({
getData: Function,
});
/** ฟังก์ชั่นตรวจสอบความถูกต้องของข้อมูลในฟอร์ม */
function validateFormExecutive() {
const hasError = [];
for (const key in objectExecutiveRef) {
if (Object.prototype.hasOwnProperty.call(objectExecutiveRef, key)) {
const property = objectExecutiveRef[key];
if (property.value && typeof property.value.validate === "function") {
const isValid = property.value.validate();
hasError.push(isValid);
}
}
}
if (hasError.every((result) => result === true)) {
if (isEdit.value == false) {
onSubmit();
} else {
putSubmit();
}
}
}
/** ฟังชั่น บันทึก */
function onSubmit() {
dialogConfirm(
$q,
async () => {
showLoader();
const body = {
posExecutiveName: formExecutive.posExecutiveName,
posExecutivePriority: formExecutive.posExecutivePriority, //
};
await http
.post(config.API.orgPosExecutive, body)
.then(() => {
success($q, "เพิ่มข้อมูลสำเร็จ");
clearForm();
modal.value = false;
props.getData?.();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
},
"ยืนยันการเพิ่มตำแหน่ง",
"ต้องการยืนยันการเพิ่มตำแหน่งนี้ใช่หรือไม่?"
);
}
/** ฟังชั่น บันทึก */
function putSubmit() {
dialogConfirm(
$q,
async () => {
showLoader();
const body = {
posExecutiveName: formExecutive.posExecutiveName,
posExecutivePriority: formExecutive.posExecutivePriority, //
};
await http
.put(config.API.orgPosExecutive + `/${formExecutive.id}`, body)
.then(() => {
success($q, "เพิ่มข้อมูลสำเร็จ");
clearForm();
modal.value = false;
props.getData?.();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
},
"ยืนยันการเพิ่มตำแหน่ง",
"ต้องการยืนยันการเพิ่มตำแหน่งนี้ใช่หรือไม่?"
);
}
async function clearForm() {
formExecutive.id = "";
formExecutive.posExecutiveName = "";
formExecutive.posExecutivePriority = null;
isEdit.value = false;
}
/**
* งค css ออกไปตามเงอนไข
* @param val true/false
*/
function inputEdit(val: boolean) {
return {
"full-width cursor-pointer inputgreen ": val,
"full-width cursor-pointer inputgreen": !val,
};
}
function close() {
clearForm();
modal.value = false;
}
watch(
() => modal.value,
() => {
if (modal.value == true) {
const dataList = formData.value;
formExecutive.id = "";
formExecutive.posExecutiveName = "";
formExecutive.posExecutivePriority = null;
if (isEdit.value == true) {
formExecutive.id = dataList.id;
formExecutive.posExecutiveName = dataList.posExecutiveName;
formExecutive.posExecutivePriority = dataList.posExecutivePriority;
}
}
}
);
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="min-width: 50vw">
<form @submit.prevent="validateFormExecutive">
<DialogHeader
:tittle="
isEdit ? `แก้ไขตำแหน่งทางการบริหาร` : 'เพิ่มตำแหน่งทางการบริหาร'
"
:close="close"
/>
<q-separator />
<q-card-section>
<div class="row q-col-gutter-sm col-12">
<div class="col-6">
<q-input
ref="posExecutiveNameRef"
v-model="formExecutive.posExecutiveName"
:class="inputEdit(isReadonly)"
dense
outlined
for="#posExecutiveName"
label="ชื่อตำแหน่งทางการบริหาร"
lazy-rules
hide-bottom-space
:rules="[
(val) => !!val || `${'กรุณากรอกชื่อตำแหน่งทางการบริหาร'}`,
]"
/>
</div>
<div class="col-6">
<q-input
ref="posExecutivePriorityRef"
v-model="formExecutive.posExecutivePriority"
:class="inputEdit(isReadonly)"
dense
outlined
type="number"
for="#posExecutivePriority"
label="ลำดับความสำคัญ"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกลำดับความสำคัญ'}`]"
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn type="submit" :label="`บันทึก`" color="public" />
</q-card-actions>
</form>
</q-card>
</q-dialog>
</template>

View file

@ -0,0 +1,404 @@
<script setup lang="ts">
import { ref, reactive, watch, defineProps } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps } from "quasar";
import DialogHeader from "@/components/DialogHeader.vue";
import type {
DataOption,
FormPositionSelectDialog,
FormPositionSelectRef,
OptionType,
OptionLevel,
OptionExecutive,
} from "@/modules/01_metadata/interface/request/position/index";
import { useCounterMixin } from "@/stores/mixin";
const executiveOpsMain = ref<DataOption[]>([]);
const levelOps = ref<DataOption[]>([]);
const levelOpsMain = ref<DataOption[]>([]);
const typeOps = ref<DataOption[]>([]);
const typeOpsMain = ref<DataOption[]>([]);
const dataLevel = ref<any>();
const executiveOps = ref<DataOption[]>([]);
const formPositionSelect = reactive<FormPositionSelectDialog>({
positionId: "",
positionName: "",
positionField: "",
positionType: "",
positionLevel: "",
positionExecutive: "",
positionExecutiveField: "",
positionArea: "",
});
const $q = useQuasar();
const isSpecial = ref<boolean>(false);
const props = defineProps({
getData: Function,
});
const { dialogConfirm, showLoader, success, hideLoader, messageError } =
useCounterMixin();
const isReadonly = ref<boolean>(false); //
const modal = defineModel<boolean>("addPosition", { required: true });
const formData = defineModel<any>("formData", { required: true });
const editCheck = defineModel<boolean>("editCheck", { required: true });
const positionNameRef = ref<Object | null>(null);
const positionFieldRef = ref<Object | null>(null);
const positionTypeRef = ref<Object | null>(null);
const positionLevelRef = ref<Object | null>(null);
const positionExecutiveRef = ref<Object | null>(null);
const positionExecutiveFieldRef = ref<Object | null>(null);
const positionAreaRef = ref<Object | null>(null);
const objectPositionSelectRef: FormPositionSelectRef = {
positionName: positionNameRef,
positionField: positionFieldRef,
positionType: positionTypeRef,
positionLevel: positionLevelRef,
positionExecutive: positionExecutiveRef,
positionExecutiveField: positionExecutiveFieldRef,
positionArea: positionAreaRef,
};
/** ฟังก์ชั่นตรวจสอบความถูกต้องของข้อมูลในฟอร์ม */
function validateFormPositionEdit() {
const hasError = [];
for (const key in objectPositionSelectRef) {
if (Object.prototype.hasOwnProperty.call(objectPositionSelectRef, key)) {
const property = objectPositionSelectRef[key];
if (property.value && typeof property.value.validate === "function") {
const isValid = property.value.validate();
hasError.push(isValid);
}
}
}
if (hasError.every((result) => result === true)) {
if (editCheck.value == true) {
saveSelectEdit();
} else {
onSubmitSelectEdit();
}
}
}
/** ฟังชั่น บันทึก */
function saveSelectEdit() {
console.log(formPositionSelect.positionExecutive);
console.log(formPositionSelect.positionArea);
dialogConfirm(
$q,
async () => {
showLoader();
const body = {
posDictName: formPositionSelect.positionName,
posDictField: formPositionSelect.positionField, //
posTypeId: formPositionSelect.positionType, //*
posLevelId: formPositionSelect.positionLevel, //*
posExecutiveId: formPositionSelect.positionExecutive, //
posDictExecutiveField: formPositionSelect.positionExecutiveField, //
posDictArea: formPositionSelect.positionArea, ///
isSpecial: isSpecial.value,
};
await http
.put(config.API.orgPosPositionById(formPositionSelect.positionId), body)
.then(() => {
success($q, "เพิ่มข้อมูลสำเร็จ");
clearFormPositionSelect();
modal.value = false;
editCheck.value = false;
props.getData?.();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
},
"ยืนยันการเพิ่มตำแหน่ง",
"ต้องการยืนยันการเพิ่มตำแหน่งนี้ใช่หรือไม่?"
);
}
/** ฟังชั่น บันทึก */
function onSubmitSelectEdit() {
dialogConfirm(
$q,
async () => {
showLoader();
const body = {
posDictName: formPositionSelect.positionName,
posDictField: formPositionSelect.positionField, //
posTypeId: formPositionSelect.positionType, //*
posLevelId: formPositionSelect.positionLevel, //*
posExecutiveId: formPositionSelect.positionExecutive,
posDictExecutiveField: formPositionSelect.positionExecutiveField, //
posDictArea: formPositionSelect.positionArea, ///
isSpecial: isSpecial.value,
};
await http
.post(config.API.orgPosPosition, body)
.then(() => {
success($q, "เพิ่มข้อมูลสำเร็จ");
clearFormPositionSelect();
modal.value = false;
editCheck.value = false;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
},
"ยืนยันการเพิ่มตำแหน่ง",
"ต้องการยืนยันการเพิ่มตำแหน่งนี้ใช่หรือไม่?"
);
}
async function clearFormPositionSelect() {
formPositionSelect.positionId = "";
formPositionSelect.positionName = "";
formPositionSelect.positionField = "";
formPositionSelect.positionType = "";
formPositionSelect.positionLevel = "";
formPositionSelect.positionExecutive = "";
formPositionSelect.positionExecutiveField = "";
formPositionSelect.positionArea = "";
}
/**
* งค css ออกไปตามเงอนไข
* @param val true/false
*/
function inputEdit(val: boolean) {
return {
"full-width cursor-pointer inputgreen ": val,
"full-width cursor-pointer inputgreen": !val,
};
}
/** function เรียกรายการประเภทตำแหน่ง */
async function fetchType() {
showLoader();
await http
.get(config.API.orgPosType)
.then((res) => {
const data = res.data.result;
dataLevel.value = data;
typeOpsMain.value = data.map((e: OptionType) => ({
id: e.id,
name: e.posTypeName,
}));
typeOps.value = typeOpsMain.value;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** function เรียกรายการตำแหน่งทางการบริหาร */
async function fetchExecutive() {
showLoader();
await http
.get(config.API.orgPosExecutive)
.then((res) => {
executiveOpsMain.value = res.data.result.map((e: OptionExecutive) => ({
id: e.id,
name: e.posExecutiveName,
}));
executiveOps.value = executiveOpsMain.value;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
watch(
() => modal.value,
() => {
if (modal.value == true) {
fetchType();
fetchExecutive();
if (formData.value) {
const dataList = formData.value;
setTimeout(() => {
updateSelectType(dataList.posTypeId);
}, 1000);
formPositionSelect.positionId = dataList.positionId;
formPositionSelect.positionName = dataList.positionName;
formPositionSelect.positionField = dataList.positionField;
formPositionSelect.positionType = dataList.positionType;
formPositionSelect.positionLevel = dataList.positionLevel;
formPositionSelect.positionExecutive = dataList.positionExecutive;
formPositionSelect.positionExecutiveField =
dataList.positionExecutiveField;
formPositionSelect.positionArea = dataList.positionArea;
isSpecial.value = dataList.isSpecial;
}
}
}
);
function updateSelectType(val: string) {
const listLevel = dataLevel.value.find((e: any) => e.id === val);
levelOpsMain.value = listLevel.posLevels.map((e: OptionLevel) => ({
id: e.id,
name: e.posLevelName,
}));
levelOps.value = levelOpsMain.value;
}
function close() {
modal.value = false;
editCheck.value = false;
}
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="min-width: 50vw">
<form @submit.prevent="validateFormPositionEdit">
<DialogHeader
:tittle="`${editCheck ? 'แก้ไขข้อมูลตำแหน่ง' : 'เพิ่มข้อมูลตำแหน่ง'}`"
:close="close"
/>
<q-separator />
<q-card-section>
<div class="row q-col-gutter-sm col-12">
<div class="col-12">
<q-input
ref="positionNameRef"
v-model="formPositionSelect.positionName"
:class="inputEdit(isReadonly)"
dense
outlined
for="#positionName"
label="ตำแหน่งในสายงาน"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกตำแหน่งในสายงาน'}`]"
/>
</div>
<div class="col-6">
<q-input
ref="positionAreaRef"
v-model="formPositionSelect.positionArea"
:class="inputEdit(isReadonly)"
dense
outlined
for="#positionArea"
label="ด้าน/สาขา"
lazy-rules
hide-bottom-space
/>
</div>
<div class="col-6">
<q-input
ref="positionFieldRef"
v-model="formPositionSelect.positionField"
:class="inputEdit(isReadonly)"
dense
outlined
for="#positionField"
label="สายงาน"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกสายงาน'}`]"
/>
</div>
<div class="col-6">
<q-select
ref="positionTypeRef"
:class="inputEdit(isReadonly)"
label="ประเภทตำแหน่ง"
v-model="formPositionSelect.positionType"
:options="typeOps"
emit-value
dense
@update:model-value="updateSelectType"
map-options
outlined
option-label="name"
option-value="id"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณาเลือกประเภทตำแหน่ง'}`]"
/>
</div>
<div class="col-6">
<q-select
ref="positionLevelRef"
:class="inputEdit(isReadonly)"
label="ระดับตำแหน่ง"
v-model="formPositionSelect.positionLevel"
:disable="formPositionSelect.positionType === ''"
:options="levelOps"
emit-value
dense
map-options
outlined
option-label="name"
option-value="id"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณาเลือกระดับตำแหน่ง'}`]"
/>
</div>
<div class="col-6">
<q-select
ref="positionExecutiveRef"
:class="inputEdit(isReadonly)"
label="ตำแหน่งทางการบริหาร"
v-model="formPositionSelect.positionExecutive"
:options="executiveOps"
emit-value
dense
map-options
outlined
option-label="name"
option-value="id"
lazy-rules
hide-bottom-space
clearable
/>
</div>
<div class="col-6">
<q-input
ref="positionExecutiveFieldRef"
v-model="formPositionSelect.positionExecutiveField"
:class="inputEdit(isReadonly)"
dense
outlined
for="#positionExecutiveField"
label="ด้านทางการบริหาร"
lazy-rules
hide-bottom-space
/>
</div>
<div class="col-6 self-center">
<q-checkbox
size="md"
v-model="isSpecial"
label="เฉพาะสายงานที่กำหนด"
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" class="bg-white text-teal">
<q-btn type="submit" :label="`บันทึก`" color="public" />
</q-card-actions>
</form>
</q-card>
</q-dialog>
</template>

View file

@ -0,0 +1,68 @@
interface DataSumCalendarObject {
id: number;
monthFull: String;
count: number;
color: String;
}
interface DataDateMonthObject {
month: number;
year: number;
}
interface DataDateAddObject {
year: number;
holidayDate: Date | string;
name: string;
isSpecial: boolean;
}
//ข้อมูล
interface RequestItemsObject {
createdAt?: Date;
createdFullName: string;
createdUserId: string;
holidayDate: Date | string;
id: string;
isSpecial: boolean;
lastUpdateFullName: string;
lastUpdateUserId: string;
lastUpdatedAt?: Date;
name: string;
originalDate: Date;
}
interface DataDateRowObject {
holidayDate: Date;
name: string;
isSpecial: boolean;
id: string;
}
interface DataDateAddObject {
year: number;
holidayDate: Date | string;
name: string;
isSpecial: boolean;
}
interface DataDateListsObject {
id: string;
dateRange: [Date, Date];
dataRangeRow: DataDateRowObject[];
detail: string;
isSpecial: boolean;
}
interface TabsObject {
label: string;
value: string;
}
export type {
DataSumCalendarObject,
DataDateMonthObject,
DataDateAddObject,
RequestItemsObject,
DataDateRowObject,
DataDateListsObject,
TabsObject,
};

View file

@ -0,0 +1,37 @@
interface Pagination {
rowsPerPage: number;
}
interface DataOption {
id: string;
name: string;
}
interface NewPagination {
descending: boolean;
page: number;
rowsPerPage: number;
sortBy: string;
}
interface ItemsMenu {
label: string;
value: string;
icon: string;
color: string;
}
interface DataAssignment {
createdAt: string;
id: string;
including: string;
includingName: string;
period: string;
year: string;
}
export type {
Pagination,
DataOption,
NewPagination,
ItemsMenu,
DataAssignment,
};

View file

@ -0,0 +1,28 @@
interface ObjectGroupRef {
posTypeName: object | null;
posTypeShortName: object | null;
posTypeRank: object | null;
[key: string]: any;
}
interface ObjectLevelRef {
posLevelName: object | null;
posLevelAuthority: object | null;
[key: string]: any;
}
interface ObjectPosRef {
posName: object | null;
posTypeName: object | null;
posLevelName: object | null;
[key: string]: any;
}
interface FormQuery {
type: string;
keyword: string;
}
export type { ObjectGroupRef, ObjectLevelRef, ObjectPosRef, FormQuery };

View file

@ -0,0 +1,75 @@
interface DataSumCalendarObject {
id: number;
monthFull: String;
count: number;
color: String;
}
interface DataListsObject {
id: number;
count: number;
name: string;
}
interface FormListMainByRole {
page: number;
pageSize: number;
position: string;
round: string;
keyword: string;
year: number | null;
}
interface FormDataRole {
position: string;
year: number | null | string;
round: string;
org: string;
including: string;
includingName: string;
target: string;
unit: string;
weight: string;
meaning: string;
formula: string;
documentInfoEvidence: string;
node: number | null;
nodeId: string | null;
orgRevisionId: string | null;
date?: [null, null];
}
interface FormCompetency {
competencyType: string;
competencyName: string;
definition: string;
level_1: any;
level_2: string;
level_3: string;
level_4: string;
level_5: string;
evaluation: string;
}
interface FormQueryCapacity {
page: number;
pageSize: number;
keyword: string;
}
interface FormFilterAssignment {
keyword: string;
period: string;
year: number | string | null;
pageSize: number;
page: number;
}
export type {
DataSumCalendarObject,
DataListsObject,
FormListMainByRole,
FormDataRole,
FormCompetency,
FormQueryCapacity,
FormFilterAssignment,
};

View file

@ -0,0 +1,39 @@
//ข้อมูลประวัติแก้ไข
interface RequestItemsPublishHistoryObject {
id: string;
items: RequestItemsHistoryObject[];
publishedDate: string;
}
//ข้อมูล
interface RequestItemsHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: string;
shortName: String;
}
//columns
interface Columns {
[index: number]: {
name: String;
align: String;
label: String;
sortable: Boolean;
field: String;
headerStyle: String;
style: String;
};
}
export type {
RequestItemsHistoryObject,
RequestItemsPublishHistoryObject,
Columns,
};

View file

@ -0,0 +1,42 @@
//ข้อมูลประวัติแก้ไข
interface RequestItemsPublishHistoryObject {
id: string;
items: RequestItemsHistoryObject[];
publishedDate: string;
}
//ข้อมูล
interface RequestItemsHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
level: number;
name: string;
shortName: String;
insigniaType?: any;
note: string;
}
//columns
interface Columns {
[index: number]: {
name: String;
align: String;
label: String;
sortable: Boolean;
field: String;
headerStyle: String;
style: String;
};
}
export type {
RequestItemsHistoryObject,
RequestItemsPublishHistoryObject,
Columns,
};

View file

@ -0,0 +1,38 @@
//ข้อมูลประวัติแก้ไข
interface RequestItemsPublishHistoryObject {
id: string;
items: RequestItemsHistoryObject[];
publishedDate: string;
}
//ข้อมูล
interface RequestItemsHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: string;
}
//columns
interface Columns {
[index: number]: {
name: String;
align: String;
label: String;
sortable: Boolean;
field: String;
headerStyle: String;
style: String;
};
}
export type {
RequestItemsHistoryObject,
RequestItemsPublishHistoryObject,
Columns,
};

View file

@ -0,0 +1,122 @@
interface Pagination {
rowsPerPage: number;
}
interface DataOption {
id: string;
name: string;
}
interface FormPositionSelectDialog {
positionId: string;
positionName: string;
positionField: string;
positionType: string;
positionLevel: string;
positionExecutive: string | null;
positionExecutiveField: string;
positionArea: string;
}
interface FormPositionEmployeeSelectDialog {
positionId: string;
positionName: string;
positionType: string;
positionLevel: string;
}
interface FormPositionSelectRef {
positionName: object | null;
positionField: object | null;
positionType: object | null;
positionLevel: object | null;
positionExecutive: object | null;
positionExecutiveField: object | null;
positionArea: object | null;
[key: string]: any;
}
interface FormPositionEmployeeSelectRef {
positionName: object | null;
positionType: object | null;
positionLevel: object | null;
[key: string]: any;
}
interface FormExecutiveRef {
posExecutiveName: object | null;
posExecutivePriority: object | null;
[key: string]: any;
}
interface OptionType {
id: string;
posTypeName: string;
}
interface OptionLevel {
id: string;
posLevelName: string;
}
interface OptionExecutive {
id: string;
posExecutiveName: string;
}
interface FormPositionSelect {
positionId: string;
posTypeId: string;
positionName: string;
positionField: string;
positionType: string;
positionLevel: string;
positionExecutive: string;
positionExecutiveField: string;
positionArea: string;
isSpecial: boolean;
}
interface ListMenu {
label: string;
icon: string;
type: string;
color: string;
}
interface RowDetailPositions {
id: string;
positionId: string;
positionName: string;
positionField: string;
positionType: string;
positionLevel: string;
positionExecutive: string;
positionExecutiveField: string;
positionArea: string;
posTypeId: string;
posLevelId: string;
posExecutiveId: string;
isSpecial: boolean;
}
interface RowListForm {
id: string;
posExecutiveName: string;
posExecutivePriority: number | null;
}
export type {
Pagination,
DataOption,
FormPositionSelect,
FormPositionSelectRef,
OptionType,
OptionLevel,
OptionExecutive,
ListMenu,
RowDetailPositions,
RowListForm,
FormPositionSelectDialog,
FormExecutiveRef,
FormPositionEmployeeSelectDialog,
FormPositionEmployeeSelectRef,
};

View file

@ -0,0 +1,13 @@
interface FrmDataGroup {
posTypeName: string;
posTypeShortName: string;
posTypeRank: number | null;
}
interface FormDataLevel {
posLevelName: number | null;
posTypeName: string | null;
posLevelAuthority: string;
}
export type { FrmDataGroup, FormDataLevel };

View file

@ -0,0 +1,61 @@
interface ResRound {
createdAt: Date;
createdFullName: string;
createdUserId: Date;
durationKPI: string;
endDate: Date;
id: Date;
lastUpdateFullName: string;
lastUpdateUserId: Date;
lastUpdatedAt: Date;
startDate: Date;
}
interface ResDataCapacity {
description: string;
id: string;
name: string;
type: string;
capacityDetails: capacityDetails;
}
interface capacityDetails {
capacityId: string;
description: string;
id: string;
level: string;
}
interface ResEvaluator {
createdAt: string;
createdFullName: string;
createdUserId: string;
id: string;
kpiUserEvaluationId: string;
lastUpdateFullName: string;
lastUpdateUserId: string;
lastUpdatedAt: string;
reason: string;
topic: string;
type: string;
}
interface ResAssignment {
data: {
result: {
data: [
{
createdAt: string;
id: string;
including: string;
includingName: string;
period: string;
year: string;
}
];
total: number;
};
};
}
export type { ResRound, ResDataCapacity, ResEvaluator, ResAssignment };

View file

@ -0,0 +1,8 @@
interface DataStrategic {
id: string;
name: string;
level: number;
children: DataStrategic;
}
export type { DataStrategic };

View file

@ -0,0 +1,34 @@
interface DataResponse {
createdAt: Date;
id: string;
isActive: boolean;
lastUpdateFullName: String;
lastUpdatedAt: Date;
name: string;
level: Number;
}
interface DataRow {
createdAt: string | null;
id: string;
isActive: boolean;
lastUpdateFullName: String;
lastUpdatedAt: string | null;
name: string;
}
interface DetailResponse extends DataResponse {
note: string;
shortName: string;
insigniaType: string;
insigniaTypeId: string;
level: number;
}
interface DetailRow
extends Omit<DetailResponse, "createdAt" | "lastUpdatedAt"> {
createdAt: string | null;
lastUpdatedAt: string | null;
}
export type { DataResponse, DataRow, DetailResponse, DetailRow };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,15 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
note: string;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,17 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
agencyCode: String;
governmentCode: String;
name: String;
note: string;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,15 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
zipCode: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,52 @@
interface DataResponse {
createdAt: Date;
id: string;
lastUpdateFullName: String;
lastUpdatedAt: Date;
prefix?: string;
rank?: number;
bloodgroup?: string;
gender?: string;
religion?: string;
relationship?: string;
name?: string;
}
interface DataRow {
createdAt: string | null;
id: string;
lastUpdateFullName: String;
lastUpdatedAt: string | null;
prefix?: string;
rank?: number;
bloodgroup?: string;
gender?: string;
religion?: string;
relationship?: string;
name?: string;
}
interface FormDistrict {
id: string;
createdAt: string;
createdUserId: string;
lastUpdatedAt: string;
lastUpdateUserId: string;
createdFullName: string;
lastUpdateFullName: string;
name: string;
provinceId: string;
}
interface FormSubDistrict {
id: string;
createdAt: string;
createdUserId: string;
lastUpdatedAt: string;
lastUpdateUserId: string;
createdFullName: string;
lastUpdateFullName: string;
name: string;
provinceId: string;
}
export type { DataResponse, DataRow,FormDistrict,FormSubDistrict };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,15 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
note: string;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,16 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
level: number;
name: String;
shortName: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,30 @@
interface DataResponse {
createdAt: Date;
id: string;
lastUpdateFullName: String;
lastUpdatedAt: Date;
posTypes?: {
id: string;
posTypeName: string;
posTypeRank: number;
};
posTypeName?: string;
posLevelName?: string;
posLevelRank?: number;
posLevelAuthority?: string;
}
interface DataRow {
createdAt: string | null;
id: string;
lastUpdateFullName: String;
lastUpdatedAt: string | null;
posTypeName?: string;
posTypeRank?: number;
posTypeId?: string;
posLevelName?: string;
posLevelRank?: number;
posLevelAuthority?: string;
}
export type { DataResponse, DataRow };

View file

@ -0,0 +1,15 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
note: string;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,15 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
note: string;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,26 @@
interface ResGroup {
id: string;
posLevels: ResLevel[];
posTypeName: string;
posTypeRank: number;
posTypeShortName: string;
}
interface ResLevel {
id: string;
posLevelName: number;
posTypeName: string;
posTypeId: string;
posLevelAuthority: string;
}
interface ResPossition {
id: string;
posDictName: string;
posLevelId: string;
posLevelName: number;
posTypeId: string;
posTypeName: string;
}
export type { ResGroup, ResLevel, ResPossition };

View file

@ -0,0 +1,15 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
note: string;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,15 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
note: string;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,14 @@
//ข้อมูล
interface ResponseHistoryObject {
createdAt?: Date;
createdFullName: String;
createdUserId: String;
id: String;
isActive: Boolean;
lastUpdateFullName: String;
lastUpdateUserId: String;
lastUpdatedAt?: Date;
name: String;
}
export type { ResponseHistoryObject };

View file

@ -0,0 +1,269 @@
const calendarWorkPage = () =>
import("@/modules/01_metadata/views/calendar.vue");
const masterInsignia = () => import("@/modules/01_metadata/views/insignia.vue");
const detailInsignia = () =>
import("@/modules/01_metadata/components/insignia/InsigniaDetail.vue");
const personalPage = () => import("@/modules/01_metadata/views/personal.vue");
const personalDistrict = () =>
import("@/modules/01_metadata/components/personal/province/01_District.vue");
const personalSubDistrict = () =>
import(
"@/modules/01_metadata/components/personal/province/02_SubDistrict.vue"
);
const positionPage = () => import("@/modules/01_metadata/views/position.vue");
const positionLevelPage = () =>
import("@/modules/01_metadata/components/position/03ListLevel.vue");
const positionEmployeePage = () =>
import("@/modules/01_metadata/views/positionEmployee.vue");
const positionEmployeeLevelPage = () =>
import("@/modules/01_metadata/components/position-employee/03ListLevel.vue");
const IndicatorByPlan = () =>
import("@/modules/01_metadata/views/indicatorByPlan.vue");
const IndicatorByPlanDetail = () =>
import(
"@/modules/01_metadata/components/Indicators/indicatorByPlan/DetailView.vue"
);
const IndicatorByRole = () =>
import("@/modules/01_metadata/views/indicatorByRole.vue");
const IndicatorByRoleDetail = () =>
import(
"@/modules/01_metadata/components/Indicators/indicatorByRole/DetailView.vue"
);
const competencyPage = () =>
import("@/modules/01_metadata/views/competency.vue");
const competencyAddPage = () =>
import("@/modules/01_metadata/components/competency/AddPage.vue");
const StrategicView = () => import("@/modules/01_metadata/views/Strategic.vue");
const AssignmentView = () =>
import("@/modules/01_metadata/views/Assignment.vue");
const AssignmentDetailView = () =>
import(
"@/modules/01_metadata/components/Indicators/Assignment/DetailView.vue"
);
export default [
{
path: "/master-data/calendar",
name: "masterCalendarWork",
component: calendarWorkPage,
meta: {
Auth: true,
Key: [7],
Role: "metadata",
},
},
{
path: "/master-data/insignia",
name: "masterInsignia",
component: masterInsignia,
meta: {
Auth: true,
Key: [7],
Role: "metadata",
},
},
{
path: "/master-data/insignia/detail/:id",
name: "masterInsigniadetail",
component: detailInsignia,
meta: {
Auth: true,
Key: [7],
Role: "metadata",
},
},
{
path: "/master-data/personal",
name: "masterPersonal",
component: personalPage,
meta: {
Auth: true,
Key: [7],
Role: "metadata",
},
},
{
path: "/master-data/personal/district/:id",
name: "masterPersonalDistrict",
component: personalDistrict,
meta: {
Auth: true,
Key: [7],
Role: "metadata",
},
},
{
path: "/master-data/personal/sub-district/:provinceId/:id",
name: "masterPersonalSubDistrict",
component: personalSubDistrict,
meta: {
Auth: true,
Key: [7],
Role: "metadata",
},
},
{
path: "/master-data/position",
name: "masterPosition",
component: positionPage,
meta: {
Auth: true,
Key: [7],
Role: "metadata",
},
},
{
path: "/master-data/position/level/:id",
name: "masterPositionLevel",
component: positionLevelPage,
meta: {
Auth: true,
Key: [9],
Role: "metadata",
},
},
{
path: "/master-data/position-employee",
name: "masterPositionEmployee",
component: positionEmployeePage,
meta: {
Auth: true,
Key: [8],
Role: "metadata",
},
},
{
path: "/master-data/position-employee/level/:id",
name: "masterPositionEmployeeLevel",
component: positionEmployeeLevelPage,
meta: {
Auth: true,
Key: [9],
Role: "metadata",
},
},
{
path: "/KPI-indicator-plan",
name: "KPIIndicatorByPlan",
component: IndicatorByPlan,
meta: {
Auth: true,
Key: [1.1],
Role: "evaluateKPI",
},
},
{
path: "/KPI-indicator-plan/add",
name: "KPIIndicatorByPlan/Add",
component: IndicatorByPlanDetail,
meta: {
Auth: true,
Key: [1.1],
Role: "evaluateKPI",
},
},
{
path: "/KPI-indicator-plan/:id",
name: "KPIIndicatorByPlanByid",
component: IndicatorByPlanDetail,
meta: {
Auth: true,
Key: [1.1],
Role: "evaluateKPI",
},
},
{
path: "/KPI-indicator-role",
name: "KPIIndicatorByRole",
component: IndicatorByRole,
meta: {
Auth: true,
Key: [1.1],
Role: "evaluateKPI",
},
},
{
path: "/KPI-indicator-role/add",
name: "KPIIndicatorByRoleAdd",
component: IndicatorByRoleDetail,
meta: {
Auth: true,
Key: [1.1],
Role: "evaluateKPI",
},
},
{
path: "/KPI-indicator-role/:id",
name: "KPIIndicatorByRoleByid",
component: IndicatorByRoleDetail,
meta: {
Auth: true,
Key: [1.1],
Role: "evaluateKPI",
},
},
{
path: "/KPI-competency",
name: "KPICompetency",
component: competencyPage,
meta: {
Auth: true,
Key: [1.1],
Role: "evaluateKPI",
},
},
{
path: "/KPI-competency/add",
name: "KPICompetencyAdd",
component: competencyAddPage,
meta: {
Auth: true,
Key: [1.1],
Role: "evaluateKPI",
},
},
{
path: "/KPI-competency/:id",
name: "KPICompetencyByid",
component: competencyAddPage,
meta: {
Auth: true,
Key: [1.1],
Role: "evaluateKPI",
},
},
{
path: "/strategic",
name: "strategic",
component: StrategicView,
meta: {
Auth: true,
Key: [1.6],
Role: "development",
},
},
{
path: "/assignment",
name: "KPIAssignment",
component: AssignmentView,
meta: {
Auth: true,
Key: [1.6],
Role: "development",
},
},
{
path: "/assignment/:id",
name: "KPIAssignmentById",
component: AssignmentDetailView,
meta: {
Auth: true,
Key: [1.1],
Role: "evaluateKPI",
},
},
];

View file

View file

@ -0,0 +1,31 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import type {
DataResponse,
DataRow,
} from "../interface/response/insignia/Insignia";
import { useCounterMixin } from "@/stores/mixin";
const { date2Thai } = useCounterMixin();
export const useInsigniaDataStore = defineStore("insigniaData", () => {
const row = ref<DataRow[]>([]);
function fetchData(data: DataResponse[], insigniaType?: string) {
// data.forEach((row, index) => {
// row.level = index + 1;
// });
const list = data.map((e) => ({
...e,
insigniaType: insigniaType,
createdAt: e.createdAt ? date2Thai(e.createdAt) : "",
lastUpdatedAt: e.lastUpdatedAt ? date2Thai(e.lastUpdatedAt) : "",
}));
list;
row.value = list;
}
return {
fetchData,
row,
};
});

View file

@ -0,0 +1,194 @@
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

@ -0,0 +1,30 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import type { DataOption } from "@/interface/main";
export const useMainOptionDataStore = defineStore("MainOptionData", () => {
const posLevelAuthorityOption = ref<DataOption[]>([
{
id: "HEAD",
label: "หัวหน้าหน่วยงาน",
},
{
id: "DEPUTY",
label: "ปลัด",
},
{
id: "GOVERNOR",
label: "ผู้ว่าฯ",
},
]);
function posLevelAuthorityConvert(val: string) {
return posLevelAuthorityOption.value.find((x: DataOption) => x.id === val)
?.label;
}
return {
posLevelAuthorityOption,
posLevelAuthorityConvert,
};
});

Some files were not shown because too many files have changed in this diff Show more