This commit is contained in:
Warunee Tamkoo 2024-08-01 12:12:28 +07:00
parent 46533bbd62
commit 15d3ac574d
128 changed files with 347 additions and 322 deletions

View file

@ -0,0 +1,389 @@
<script setup lang="ts">
import { ref, onMounted, watch, reactive } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import type { DataOption } from "@/modules/04_registryPerson/interface/index/Main";
import type { DataType } from "@/modules/04_registryPerson/interface/response/Main";
import type {
FormAddPerson,
MyObjectRef,
} from "@/modules/04_registryPerson/interface/request/Main";
import { useProfileDataStore } from "@/modules/04_registryPerson/stores/profile";
import DialogHeader from "@/components/DialogHeader.vue";
/** importStore*/
import { useRegistryNewDataStore } from "@/modules/04_registryPerson/store";
import { useCounterMixin } from "@/stores/mixin";
const profileStore = useProfileDataStore();
const $q = useQuasar();
const store = useRegistryNewDataStore();
const {
dialogConfirm,
success,
messageError,
hideLoader,
dialogMessageNotify,
date2Thai,
} = useCounterMixin();
const { calculateAge } = profileStore;
const modal = defineModel<boolean>("modal", { required: true });
const empType = defineModel<string>("empType", { required: true });
const props = defineProps({
fetchData: { type: Function },
fetchType: { type: Function },
});
const prefixOps = ref<DataOption[]>([]);
const rankOps = ref<DataOption[]>([]);
const levelOps = ref<DataType[]>([]);
const age = ref<string | null>("");
const formData = reactive<FormAddPerson>({
prefix: "",
firstName: "",
lastName: "",
citizenId: "",
position: "",
posTypeId: "",
posLevelId: "",
rank: "",
birthDate: null,
});
function fetchPrefix() {
http
.get(config.API.orgPrefix)
.then((res) => {
prefixOps.value = res.data.result.map((v: any) => ({
id: v.name,
name: v.name,
}));
})
.catch((err) => {
messageError($q, err);
});
}
function fetchRank() {
http
.get(config.API.orgRank)
.then((res) => {
rankOps.value = res.data.result.map((v: any) => ({
id: v.name,
name: v.name,
}));
})
.catch((err) => {
messageError($q, err);
});
}
/**
* function ตรวจสอบเลขประจำตวประชาชน
* @param citizenId เลขประจำตวประชาชน
*/
function changeCardID(citizenId: string | number | null) {
if (citizenId != null && typeof citizenId == "string") {
if (citizenId.length == 13 && citizenId) {
http
.put(config.API.profileNewCitizenId(citizenId), {
citizenId: citizenId,
})
.then(() => {})
.catch((err) => {
if (err.response.data.status === 500) {
dialogMessageNotify($q, err.response.data.message);
} else {
messageError($q, err);
}
});
}
}
}
function fetchLevel(id: string) {
const listLevel = store.posTypeMain.find((e: DataType) => e.id === id);
levelOps.value = listLevel?.posLevels;
const checkLevel = levelOps.value.filter(
(e: DataType) => e.id === formData.posLevelId
);
if (checkLevel.length === 0) {
formData.posLevelId = "";
}
}
function closeDialog() {
modal.value = false;
clearFormData();
}
const calculateMaxDate = () => {
const today = new Date();
today.setFullYear(today.getFullYear() - 18);
return today;
};
function clearFormData() {
formData.prefix = "";
formData.firstName = "";
formData.lastName = "";
formData.citizenId = "";
formData.position = "";
formData.posTypeId = "";
formData.posLevelId = "";
formData.rank = "";
age.value = "";
formData.birthDate = null;
}
async function onSubmit() {
dialogConfirm($q, async () => {
const type = empType.value !== "officer" ? "-employee" : "";
await http
.post(config.API.registryNew(type), formData)
.then(() => {
success($q, "บันทึกข้อมูลสำเร็จ");
props.fetchData?.();
closeDialog();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
watch(
() => modal.value,
() => {
if (modal.value) {
fetchPrefix();
fetchRank();
props.fetchType?.();
}
}
);
watch(
() => formData.birthDate,
(v) => {
if (v) {
age.value = calculateAge(v);
}
}
);
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="min-width: 350px">
<q-form @submit.prevent @validation-success="onSubmit()" greedy>
<DialogHeader tittle="เพิ่มข้อมูล" :close="closeDialog" />
<q-separator />
<q-card-section class="q-pa-md q-col-gutter-md">
<div class="row q-gutter-sm">
<div class="col">
<q-select
bg-color="white"
v-model="formData.prefix"
label="คำนำหน้าชื่อ"
outlined
dense
lazy-rules
:options="prefixOps"
option-label="name"
option-value="id"
map-options
hide-bottom-space
:rules="[
(val) => {
return val.length > 0 || 'กรุณาเลือกคำนำหน้าชื่อ';
},
]"
emit-value
/>
</div>
<div class="col">
<q-select
bg-color="white"
v-model="formData.rank"
label="ยศ"
outlined
dense
:options="rankOps"
option-label="name"
option-value="id"
map-options
hide-bottom-space
emit-value
/>
</div>
</div>
<q-input
bg-color="white"
outlined
v-model="formData.firstName"
label="ชื่อ"
dense
lazy-rules
borderless
:rules="[(val) => val.length > 0 || 'กรุณากรอกชื่อ']"
hide-bottom-space
/>
<q-input
bg-color="white"
outlined
v-model="formData.lastName"
label="นามสกุล"
dense
lazy-rules
borderless
:rules="[(val) => val.length > 0 || 'กรุณากรอกนามสกุล']"
hide-bottom-space
/>
<q-input
bg-color="white"
outlined
v-model="formData.citizenId"
label="เลขประจำตัวประชาชน"
dense
lazy-rules
borderless
:rules="[
(val: string) => !!val || `${'กรุณากรอกเลขประจำตัวประชาชน'}`,
(val: string) =>
val.length >= 13 ||
`${'กรุณากรอกเลขประจำตัวประชาชนให้ครบ'}`,
]"
maxlength="13"
hide-bottom-space
mask="#############"
@update:model-value="changeCardID"
/>
<div class="row q-col-gutter-sm">
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
autoApply
borderless
week-start="0"
:max-date="calculateMaxDate()"
:enableTimePicker="false"
menu-class-name="modalfix"
v-model="formData.birthDate"
:locale="'th'"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
for="inputDatereceive"
outlined
dense
hide-bottom-space
class="inputgreen"
:model-value="
formData.birthDate != null
? date2Thai(formData.birthDate)
: null
"
label="วัน/เดือน/ปี เกิด"
:rules="[
(val) => !!val || `${'กรุณาเลือก วัน/เดือน/ปี เกิด'}`,
]"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
readonly
class="inputgreen"
v-model="age"
label="อายุ"
/>
</div>
</div>
<q-input
bg-color="white"
outlined
v-model="formData.position"
label="ตำแหน่งในสายงาน"
dense
lazy-rules
borderless
:rules="[(val) => val.length > 0 || 'กรุณากรอกตำแหน่ง']"
hide-bottom-space
/>
<q-select
bg-color="white"
v-model="formData.posTypeId"
label="ตำแหน่งประเภท"
outlined
:options="store.posTypeOps"
dense
options-cover
map-options
emit-value
option-label="name"
option-value="id"
hide-bottom-space
:rules="[(val) => !!val || 'กรุณาเลือกตำแหน่งประเภท']"
@update:model-value="fetchLevel"
/>
<q-select
bg-color="white"
v-model="formData.posLevelId"
label="ระดับ"
:options="levelOps"
outlined
dense
map-options
emit-value
option-label="posLevelName"
option-value="id"
options-cover
hide-bottom-space
:rules="[(val) => !!val || 'กรุณาเลือกระดับ']"
/>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
id="onSubmit"
type="submit"
label="บันทึก"
color="public"
class="q-px-md"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -0,0 +1,437 @@
<script setup lang="ts">
import { ref } from "vue";
import { useQuasar } from "quasar";
import { useRouter } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
/**
* importType*
*/
import type { QTableProps } from "quasar";
import type { QForm } from "quasar";
import type { DataOption } from "@/modules/04_registryPerson/interface/index/Main";
import type {
HistoryPos,
Position,
} from "@/modules/04_registryPerson/interface/response/History";
/**
* import components
*/
import DialogHeader from "@/components/DialogHeader.vue";
/**
* importStore
*/
import { useCounterMixin } from "@/stores/mixin";
/**
* use
*/
const myForm = ref<QForm>();
const router = useRouter();
const $q = useQuasar();
const { showLoader, hideLoader, messageError, date2Thai } = useCounterMixin();
/**
* props
*/
const modal = defineModel<boolean>("modal", { required: true });
/**
* วแปร
*/
const employeeClass = ref<string>("");
const typeKeyword = ref<string>("");
const Keyword = ref<string>("");
const positionKeyword = ref<string>("");
const employeeClassOps = ref<DataOption[]>([
{ id: "officer", name: "ข้าราชการ กทม.สามัญ" },
{ id: "perm", name: "ลูกจ้างประจำ" },
]);
const typeKeywordOps = ref<DataOption[]>([
{ id: "no", name: "ตำแหน่งเลขที่" },
{ id: "position", name: "ตำแหน่ง" },
]);
const positionOps = ref<DataOption[]>([]);
const options = ref<DataOption[]>([]);
/**
* Table
*/
const columns = ref<QTableProps["columns"]>([
{
name: "no",
label: "ลำดับ",
field: "no",
align: "left",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "citizenId",
align: "left",
label: "เลขประจำตัวประชาชน",
field: "citizenId",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "name",
align: "left",
label: "ชื่อ - นามสกุล",
field: "name",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posNo",
align: "left",
label: "ตำแหน่งเลขที่",
field: "posNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "position",
align: "left",
label: "ตำแหน่ง",
field: "position",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "date",
align: "left",
label: "วันที่ถือครอง",
field: "date",
format: (val, row) => `${date2Thai(val)}`,
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
const rows = ref<HistoryPos[]>([]);
/**
* function fetch อมลตำแหน าราชการ
*/
function fecthPositionOfficer() {
http
.get(config.API.listPositionPathHistory)
.then((res) => {
let data = res.data.result.items;
positionOps.value = data.map((e: Position) => ({
id: e.id,
name: e.name,
}));
options.value = positionOps.value;
})
.catch((err) => {
messageError($q, err);
});
}
/**
* function fetch อมลตำแหน กจางประจำ
*/
function fetchPositionPerm() {
http
.get(config.API.listPositionEmployeePositionHistory)
.then((res) => {
let data = res.data.result.items;
console.log(data);
positionOps.value = data.map((e: Position) => ({
id: e.id,
name: e.name,
}));
options.value = positionOps.value;
})
.catch((err) => {
messageError($q, err);
});
}
/**
* function เปลยนประเภท
*/
function changeEmployeeClass() {
typeKeyword.value = "";
Keyword.value = "";
positionKeyword.value = "";
rows.value = [];
}
/**
* function เลอกฟลดจะคนหา
* @param typeKeyword ประเภทฟลด
*/
function selectTypeKeyword(typeKeyword: string) {
positionOps.value = [];
positionKeyword.value = "";
Keyword.value = "";
if (typeKeyword == "position" && employeeClass.value === "officer") {
fecthPositionOfficer();
} else if (typeKeyword == "position" && employeeClass.value === "perm") {
fetchPositionPerm();
}
}
/**
* function นหาประวอครองตำแหน
* @param type ประเภทขาราชการ
*/
function clickSearch(type: string) {
myForm.value!.validate().then((result: boolean) => {
if (result) {
showLoader();
let body = {};
if (typeKeyword.value === "no") {
Object.assign(body, {
posNo: Keyword.value,
});
} else if (typeKeyword.value === "position") {
Object.assign(body, {
position: positionKeyword.value,
});
}
const empType = type === "officer" ? "" : "-employee";
http
.post(config.API.registryNew(empType) + `/search/history/oc`, body)
.then((res) => {
let data = res.data.result;
if (data.length !== 0) {
rows.value = data.map((e: HistoryPos) => ({
id: e.id,
citizenId: e.citizenId,
name: e.fullName,
posNo: e.posNo,
position: e.position,
date: e.date,
}));
} else {
rows.value = [];
}
})
.catch((err) => {
messageError($q, err);
rows.value = [];
})
.finally(() => {
hideLoader();
});
}
});
}
/**
* function นหาขอม Optiion
* @param val คำคนหา
* @param update function
*/
function filterFn(val: string, update: Function) {
if (val === "") {
update(() => {
options.value = positionOps.value;
});
return;
} else {
update(() => {
options.value = positionOps.value.filter(
(e) => e.name.search(val) !== -1
);
});
}
}
/**
* function redirect ไปทะเบยนประว
* @param id
*/
function clickRedirect(id: string) {
const url =
employeeClass.value === "officer" ? "registry-person" : "registry-employee";
router.push(`${url}/${id}`);
}
/**
* function popup
*/
function closeDialog() {
modal.value = false;
employeeClass.value = "";
typeKeyword.value = "";
Keyword.value = "";
positionKeyword.value = "";
rows.value = [];
}
</script>
<template>
<q-dialog v-model="modal">
<q-card style="width: 850px; max-width: 80vw">
<DialogHeader :tittle="'ประวัติถือครองตำแหน่ง'" :close="closeDialog" />
<q-separator />
<q-card-section class="q-pa-sm">
<q-form ref="myForm">
<div class="col-12 bg-grey-2 q-pa-sm">
<div class="col-12 row q-pb-sm q-col-gutter-sm items-center"></div>
<div class="q-col-gutter-xs row no-wrap">
<div class="col-4">
<q-select
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณาเลือก ประเภท'}`]"
outlined
dense
lazy-rules
v-model="employeeClass"
emit-value
map-options
:options="employeeClassOps"
option-label="name"
option-value="id"
:label="`${'ประเภท'}`"
use-input
input-debounce="0"
@update:model-value="changeEmployeeClass"
/>
</div>
<div class="col-3">
<q-select
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณาเลือก ฟิลด์ที่จะค้น'}`]"
outlined
dense
lazy-rules
v-model="typeKeyword"
emit-value
map-options
:options="typeKeywordOps"
option-label="name"
option-value="id"
:label="`${' เลือกฟิลด์ที่จะค้น'}`"
use-input
input-debounce="0"
@update:model-value="selectTypeKeyword(typeKeyword)"
/>
</div>
<div class="col" v-if="typeKeyword === 'no'">
<q-input
borderless
outlined
dense
debounce="300"
v-model="Keyword"
placeholder="ตำแหน่งเลขที่"
:rules="[(val:string) => !!val || `${'กรุณากรอก ตำแหน่งเลขที่'}`]"
hide-bottom-space
/>
</div>
<div class="col" v-if="typeKeyword === 'position'">
<q-select
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณาเลือก ตำแหน่ง'}`]"
outlined
dense
v-model="positionKeyword"
emit-value
map-options
:options="options"
option-label="name"
option-value="id"
:label="`${' เลือกตำแหน่ง'}`"
use-input
input-debounce="0"
@filter="filterFn"
behavior="menu"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
ไมอม
</q-item-section>
</q-item>
</template></q-select
>
</div>
<q-space />
<q-btn
color="primary"
icon="mdi-magnify"
label="ค้นหา"
@click="clickSearch(employeeClass)"
/>
</div>
</div>
<div class="col-12 q-mt-sm">
<d-table
flat
dense
bordered
:rows="rows"
:columns="columns"
row-key="order"
no-data-label="ไม่มีข้อมูล"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
>
<div class="text-grey-7 text-weight-medium">
<span class="row">{{ col.label }}</span>
</div>
</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-if="col.name === 'citizenId'"
class="text-primary"
@click="clickRedirect(props.row.id)"
>
{{ props.row.citizenId ?? "-" }}
</div>
<div
v-else-if="col.name === 'name'"
class="text-primary"
@click="clickRedirect(props.row.id)"
>
{{ props.row.name ?? "-" }}
</div>
<div v-else class="table_ellipsis2">
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</div>
</q-form>
</q-card-section>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -0,0 +1,555 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import { useRouter } from "vue-router";
/** importType*/
import type { QTableProps } from "quasar";
import type { FormFilter } from "@/modules/04_registryPerson/interface/request/Main";
/** importComponent*/
import DialogAddData from "@/modules/04_registryPerson/components/DialogAddData.vue";
import DialogHistory from "@/modules/04_registryPerson/components/DialogHistory.vue";
/** importStore*/
import { useRegistryNewDataStore } from "@/modules/04_registryPerson/store";
const store = useRegistryNewDataStore();
const router = useRouter();
const formFilter = defineModel<FormFilter>("formFilter", { required: true });
const maxPage = defineModel<Number>("maxPage", { required: true });
const empType = defineModel<string>("empType", { required: true });
const props = defineProps({
rows: { type: Array },
fetchData: { type: Function },
fetchType: { type: Function },
total: { type: Number },
// empType: { type: String },
});
const columns = ref<QTableProps["columns"]>([
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: true,
field: "no",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "fullName",
align: "left",
label: "ชื่อ - นามสกุล",
sortable: true,
field: "fullName",
headerStyle: "font-size: 14px; min-width: 200px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posNo",
align: "left",
label: "ตำแหน่งเลขที่",
sortable: true,
field: "posNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "position",
align: "left",
label: "ตำแหน่งในสายงาน",
sortable: true,
field: "position",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posPath",
align: "left",
label: "ตำแหน่งประเภท",
sortable: true,
field: "posType",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posLevel",
align: "left",
label: "ระดับ",
sortable: true,
field: "posLevel",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format(val, row) {
return row.posTypeShortName
? row.posTypeShortName + " " + row.posLevel
: row.posLevel;
},
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "org",
align: "left",
label: "สังกัด",
sortable: true,
field: "org",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
// {
// name: "year",
// align: "left",
// label: "",
// sortable: true,
// field: "year",
// headerStyle: "font-size: 14px",
// style: "font-size: 14px",
// sort: (a: string, b: string) =>
// a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
// },
// {
// name: "salary",
// align: "left",
// label: "",
// sortable: true,
// field: "salary",
// 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[]>([
"no",
"fullName",
"posNo",
"position",
"posPath",
"posType",
"posLevel",
"org",
]);
function updatePagePagination() {
props.fetchData?.();
}
function updatePageSizePagination(newPagination: any) {
formFilter.value.page = 1;
formFilter.value.pageSize = newPagination.rowsPerPage;
}
const pagination = ref({
page: formFilter.value.page,
rowsPerPage: formFilter.value.pageSize,
});
const modalDialogAdd = ref<boolean>(false);
const modalHistory = ref<boolean>(false);
function onClickAddData() {
modalDialogAdd.value = !modalDialogAdd.value;
}
function onClickHistory() {
modalHistory.value = !modalHistory.value;
}
function onClickViewDetail(id: string) {
if (empType.value === "officer") {
router.push(`/registry-person/${id}`);
} else {
router.push(`/registry-employee/${id}`);
}
}
/**
* function redirect ไปหนารายการคำรองขอแกไขขอม
*/
function redirectToPagePetition() {
router.push(`/registry-person/request-edit`);
}
watch(
() => formFilter.value.pageSize,
() => {
props.fetchData?.();
}
);
</script>
<template>
<div class="col-12 row q-pb-sm q-col-gutter-sm items-center">
<div class="row q-gutter-sm">
<q-btn
v-if="empType === 'officer'"
color="primary"
label="รายการคำร้องขอแก้ไข"
@click="redirectToPagePetition()"
>
<q-tooltip>รายการคำรองขอแกไข</q-tooltip></q-btn
>
<q-btn
round
flat
dense
color="blue"
icon="mdi-history"
@click="onClickHistory"
>
<q-tooltip>ประวอครองตำแหน</q-tooltip></q-btn
>
</div>
<q-space />
<q-select
v-if="store.mode === 'table'"
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"
class="full-height"
hide-bottom-space
/>
<div>
<q-btn-toggle
v-model="store.mode"
dense
class="my-custom-toggle no-shadow"
toggle-color="grey-4"
:options="[
{ value: 'table', slot: 'table' },
{ value: 'card', slot: 'card' },
]"
>
<template v-slot:table>
<q-icon
name="format_list_bulleted"
class="q-px-sm q-py-xs"
size="22px"
:style="{
color: store.mode === 'table' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
<template v-slot:card>
<q-icon
name="mdi-view-grid-outline"
size="22px"
class="q-px-sm q-py-xs"
:style="{
color: store.mode === 'card' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
</q-btn-toggle>
</div>
</div>
<d-table
ref="table"
row-key="id"
flat
bordered
dense
:card-container-class="store.mode === 'card' ? 'q-col-gutter-md' : ''"
:columns="columns"
:rows="props.rows"
:grid="store.mode === 'card'"
:paging="true"
v-model:pagination="pagination"
:rows-per-page-options="[20, 50, 100]"
:visible-columns="visibleColumns"
@update:pagination="updatePageSizePagination"
>
>
<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" v-if="col.name === 'posLevel'">{{
empType === "officer" ? col.label : "ระดับชั้นงาน"
}}</span>
<span class="text-weight-medium" v-else-if="col.name === 'posPath'">{{
empType === "officer" ? col.label : "กลุ่มงาน"
}}</span>
<span
class="text-weight-medium"
v-else-if="col.name === 'position'"
>{{ empType === "officer" ? col.label : "ตำแหน่ง" }}</span
>
<span class="text-weight-medium" v-else>{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props" v-if="store.mode === 'table'">
<q-tr :props="props">
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name === 'no'">
{{
(formFilter.page - 1) * formFilter.pageSize + props.rowIndex + 1
}}
</div>
<div v-else-if="col.name == 'fullName'">
<div class="row col-12 wrap items-center">
<q-item>
<q-item-section avatar>
<q-avatar size="50px" class="bg-grey-2">
<q-img :src="props.row.avatar" />
</q-avatar>
</q-item-section>
<q-item-section>
<div
class="text-weight-medium text-primary cursor-pointer"
@click="onClickViewDetail(props.row.id)"
>
<q-tooltip>ทะเบยนประว</q-tooltip>
{{
`${props.row.prefix ? props.row.prefix : ""}${
props.row.firstName
} ${props.row.lastName}`
}}
</div>
<div class="text-weight-light">{{ props.row.citizenId }}</div>
</q-item-section>
</q-item>
</div>
</div>
<div v-else class="table_ellipsis">
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
<template v-slot:item="props" v-else>
<div class="col-xs-12 col-sm-4 col-md-3">
<q-card flat bordered class="">
<q-card-section class="text-center q-pb-none">
<q-avatar size="80px" class="bg-grey-2">
<q-img :src="props.row.avatar" />
</q-avatar>
<div class="text-weight-medium q-mt-md">
{{
`${props.row.prefix ? props.row.prefix : ""}${
props.row.firstName
} ${props.row.lastName}`
}}
</div>
<div class="text-weight-light full-width text-center">
{{ props.row.citizenId }}
</div>
</q-card-section>
<q-card-section class="q-pa-sm">
<q-card bordered class="bg-grey-13">
<q-card-section>
<div class="text-grey">ตำแหนงเลขท</div>
<div class="text-subtitle2 text-black q-ml-sm">
{{ props.row.posNo ? props.row.posNo : "-" }}
</div>
<div class="text-grey">
{{ empType === "officer" ? `ตำแหน่งในสายงาน` : `ตำแหน่ง` }}
</div>
<div class="text-subtitle2 text-black q-ml-sm">
{{ props.row.position ? props.row.position : "-" }}
</div>
<div class="text-grey">
{{ empType === "officer" ? "ตำแหน่งประเภท" : "กลุ่มงาน" }}
</div>
<div class="text-subtitle2 text-black q-ml-sm">
{{ props.row.posType ? props.row.posType : "-" }}
</div>
<div class="text-grey">
{{ empType === "officer" ? "ระดับ" : "ระดับชั้นงาน" }}
</div>
<div class="text-subtitle2 text-black q-ml-sm">
{{
props.row.posLevel
? props.row.posTypeShortName
? `${props.row.posTypeShortName} ${props.row.posLevel}`
: props.row.posLevel
: "-"
}}
</div>
</q-card-section>
<!-- <q-list dense class="q-py-sm"> -->
<!-- <q-item dense>
<q-item-section>
<q-item-label caption>ตำแหนงเลขท</q-item-label>
</q-item-section>
<q-item-section>
<q-item-label caption class="text-black">{{
props.row.posNo ? props.row.posNo : "-"
}}</q-item-label>
</q-item-section>
</q-item> -->
<!-- <q-item dense>
<q-item-section>
<q-item-label caption>{{
empType === "officer" ? `ตำแหน่งในสายงาน` : `ตำแหน่ง`
}}</q-item-label>
</q-item-section>
<q-item-section>
<q-item-label caption class="text-black">
{{
props.row.position ? props.row.position : "-"
}}</q-item-label
>
</q-item-section>
</q-item> -->
<!-- <q-item dense>
<q-item-section>
<q-item-label caption>
{{
empType === "officer" ? "ตำแหน่งประเภท" : "กลุ่มงาน"
}}</q-item-label
>
</q-item-section>
<q-item-section>
<q-item-label caption class="text-black">
{{
props.row.posType ? props.row.posType : "-"
}}</q-item-label
>
</q-item-section>
</q-item> -->
<!-- <q-item dense>
<q-item-section>
<q-item-label caption>
{{
empType === "officer" ? "ระดับ" : "ระดับชั้นงาน"
}}</q-item-label
>
</q-item-section>
<q-item-section>
<q-item-label caption class="text-black">
{{
props.row.posLevel
? props.row.posTypeShortName
? `${props.row.posTypeShortName} ${props.row.posLevel}`
: props.row.posLevel
: "-"
}}</q-item-label
>
</q-item-section>
</q-item> -->
<!-- </q-list> -->
</q-card>
</q-card-section>
<q-separator inset />
<q-btn
flat
color="black"
label="ดูเพิ่มเติม"
class="hover-button full-width q-pa-md"
@click="onClickViewDetail(props.row.id)"
/>
</q-card>
</div>
</template>
<template v-slot:pagination="scope">
งหมด {{ props.total }} รายการ
<q-pagination
v-model="formFilter.page"
active-color="primary"
color="dark"
:max="Number(maxPage)"
:max-pages="5"
size="sm"
boundary-links
direction-links
@update:model-value="updatePagePagination()"
></q-pagination>
</template>
<template v-slot:no-data="{ icon, message, filter }">
<div class="full-width row flex-center text-accent q-gutter-sm">
<span
><div
style="
height: 50vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
"
class="text-grey-5"
>
<q-icon name="search" size="4rem" />
<span>ไมพบขอม</span>
</div>
</span>
</div>
</template>
</d-table>
<DialogAddData
v-model:modal="modalDialogAdd"
:fetchData="props.fetchData"
:fetchType="props.fetchType"
:empType="empType"
/>
<DialogHistory v-model:modal="modalHistory" />
</template>
<style scoped>
.hover-button:hover {
background-color: #089cfc;
color: white !important;
}
.my-card {
width: 100%;
height: 100%;
}
.bg-grey-13 {
background: #f1f2f496 !important;
}
.my-custom-toggle {
border: 1px solid #d0d0d2;
}
.cardRO {
border-radius: 10px;
}
.cardRO:hover {
cursor: pointer;
background: #dcdcdc33 !important;
}
.cardRO:hover .textName {
color: #02a998;
}
</style>

View file

@ -0,0 +1,785 @@
<script setup lang="ts">
import { ref, reactive, watch, onMounted } from "vue";
import dialogHeader from "@/components/DialogHeader.vue";
import type { QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { QForm, useQuasar } from "quasar";
import { useRoute } from "vue-router";
import type { RequestItemsObject } from "@/modules/04_registryPerson/interface/request/ProfesLicense";
import type { ResponseObject } from "@/modules/04_registryPerson/interface/response/ProfesLicense";
import http from "@/plugins/http";
import config from "@/app.config";
const mixin = useCounterMixin();
const $q = useQuasar();
const {
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
date2Thai,
} = mixin;
const historyDialog = ref<boolean>(false);
const mode = ref<string>("table");
const dialog = ref<boolean>(false);
const route = useRoute();
const id = ref<string>(route.params.id.toString());
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const dialogStatus = ref<string>("create");
const editId = ref<string>("");
const keyword = ref<string>("");
const historyKeyword = ref<string>("");
const columns = ref<QTableProps["columns"]>([
{
name: "certificateType",
align: "left",
label: "ชื่อใบอนุญาต",
sortable: true,
field: "certificateType",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "issuer",
align: "left",
label: "หน่วยงานผู้ออกใบอนุญาต",
sortable: true,
field: "issuer",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "certificateNo",
align: "left",
label: "เลขที่ใบอนุญาต",
sortable: true,
field: "certificateNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "issueDate",
align: "left",
label: "วันที่ออกใบอนุญาต",
sortable: true,
field: "issueDate",
format: (v) => date2Thai(v),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "expireDate",
align: "left",
label: "วันที่หมดอายุ",
sortable: true,
format: (v) => date2Thai(v),
field: "expireDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const historyColumns = ref<QTableProps["columns"]>([
{
name: "certificateType",
align: "left",
label: "ชื่อใบอนุญาต",
sortable: true,
field: "certificateType",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "issuer",
align: "left",
label: "หน่วยงานผู้ออกใบอนุญาต",
sortable: true,
field: "issuer",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "certificateNo",
align: "left",
label: "เลขที่ใบอนุญาต",
sortable: true,
field: "certificateNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "issueDate",
align: "left",
label: "วันที่ออกใบอนุญาต",
sortable: true,
field: "issueDate",
format: (v) => date2Thai(v),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "expireDate",
align: "left",
label: "วันที่หมดอายุ",
sortable: true,
format: (v) => date2Thai(v),
field: "expireDate",
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: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
format: (v) => date2Thai(v),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const profesLicenseData = reactive<RequestItemsObject>({
certificateType: "",
issuer: "",
certificateNo: "",
issueDate: new Date(),
expireDate: null,
profileId: id.value,
});
const rows = ref<ResponseObject[]>([]);
const historyRows = ref<ResponseObject[]>([]);
const pagination = ref({
page: 1,
rowsPerPage: 10,
});
const historyPagination = ref({
page: 1,
rowsPerPage: 10,
});
const visibleColumns = ref<string[]>([
"certificateType",
"issuer",
"certificateNo",
"issueDate",
"expireDate",
]);
const historyVisibleColumns = ref<string[]>([
"certificateType",
"issuer",
"certificateNo",
"issueDate",
"expireDate",
"lastUpdateFullName",
"lastUpdatedAt",
]);
async function onSubmit() {
dialogConfirm(
$q,
async () => {
dialogStatus.value === "create" ? addData() : editData(editId.value);
closeDialog();
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
function closeDialog() {
dialog.value = false;
}
function closeHistoryDialog() {
historyDialog.value = false;
}
async function fetchData(id: string) {
showLoader();
await http
.get(config.API.profileNewCertificateByProfileId(id, empType.value))
.then(async (res) => {
rows.value = res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function clearForm() {
profesLicenseData.expireDate = null;
profesLicenseData.issueDate = new Date();
profesLicenseData.certificateNo = "";
profesLicenseData.certificateType = "";
profesLicenseData.issuer = "";
}
function editForm(row: any) {
dialogStatus.value = "edit";
editId.value = row.id;
profesLicenseData.certificateType = row.certificateType;
profesLicenseData.certificateNo = row.certificateNo;
profesLicenseData.issuer = row.issuer;
profesLicenseData.issueDate = row.issueDate;
profesLicenseData.expireDate = row.expireDate;
dialog.value = true;
}
async function addData() {
await http
.post(config.API.profileNewCertificate(empType.value), {
...profesLicenseData,
profileId: empType.value === "" ? id.value : undefined,
profileEmployeeId: empType.value !== "" ? id.value : undefined,
})
.then(() => {
fetchData(id.value);
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(idData: string) {
await http
.patch(
config.API.profileNewCertificateByCertificateId(idData, empType.value),
{
...profesLicenseData,
profileId: undefined,
}
)
.then(() => {
fetchData(id.value);
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
// function deleteData(idData: string) {
// dialogRemove($q, () =>
// http
// .delete(config.API.profileNewCertificateByCertificateId(idData))
// .then(() => {
// fetchData(id.value);
// })
// .catch((err) => {
// messageError($q, err);
// })
// .finally(() => {
// hideLoader();
// })
// );
// }
async function fetchHistoryData(id: string) {
showLoader();
await http
.get(config.API.profileNewCertificateHisByCertificateId(id, empType.value))
.then(async (res) => {
historyRows.value = res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(async () => {
await fetchData(id.value);
});
</script>
<template>
<div class="row items-center q-gutter-x-sm q-pb-sm">
<q-btn
round
flat
color="primary"
icon="add"
dense
@click="
dialogStatus = 'create';
clearForm();
dialog = true;
"
>
<q-tooltip>เพมขอม</q-tooltip></q-btn
>
<q-space />
<q-input dense outlined v-model="keyword" label="ค้นหา">
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
<q-select
v-if="mode === 'table'"
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-btn-toggle
v-model="mode"
dense
class="no-shadow toggle-borderd"
toggle-color="grey-4"
:options="[
{ value: 'table', slot: 'table' },
{ value: 'card', slot: 'card' },
]"
>
<template v-slot:table>
<q-icon
name="format_list_bulleted"
size="24px"
:style="{
color: mode === 'table' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
<template v-slot:card>
<q-icon
name="mdi-view-grid-outline"
size="24px"
:style="{
color: mode === 'card' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
</q-btn-toggle>
</div>
<d-table
:grid="mode === 'card'"
ref="table"
:columns="columns"
:rows="rows"
row-key="name"
flat
bordered
:paging="true"
dense
:filter="keyword"
v-model:pagination="pagination"
:rows-per-page-options="[20, 50, 100]"
class="custom-header-table"
:visible-columns="visibleColumns"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width />
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props" v-if="mode === 'table'">
<q-tr :props="props" class="cursor-pointer">
<q-td auto-width>
<q-btn
color="primary"
flat
dense
round
class="q-mr-xs"
size="14px"
icon="mdi-pencil-outline"
clickable
@click="
() => {
editForm(props.row);
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="
() => (fetchHistoryData(props.row.id), (historyDialog = true))
"
>
<q-tooltip>ประวแกไขใบอนญาตประกอบวชาช</q-tooltip>
</q-btn>
<!-- <q-btn
color="red"
flat
dense
round
size="14px"
icon="mdi-delete"
clickable
@click.stop="deleteData(props.row.id)"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn> -->
</q-td>
<q-td v-for="col in props.cols" :key="col.id">
<div>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
<template v-slot:item="props" v-else>
<div
class="q-pa-xs col-xs-12 col-sm-6 col-md-6 col-lg-6 grid-style-transition"
>
<q-card bordered>
<q-card-actions align="right" class="bg-grey-3">
<q-btn
flat
round
color="primary"
icon="edit"
@click="editForm(props.row)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
flat
round
color="info"
icon="mdi-history"
size="14px"
@click="
() => (fetchHistoryData(props.row.id), (historyDialog = true))
"
>
<q-tooltip>ประวแกไขใบอนญาตประกอบวชาช</q-tooltip>
</q-btn>
</q-card-actions>
<q-separator />
<q-list>
<div
:class="`row q-pa-sm`"
:style="`background-color: ${index % 2 !== 0 ? '#FAFAFA' : ''}`"
v-for="(col, index) in props.cols"
:key="col.name"
>
<div></div>
<div class="col label-color">
<div>{{ col.label }}</div>
</div>
<div class="col">
<div>{{ col.value }}</div>
</div>
</div>
</q-list>
</q-card>
</div>
</template>
</d-table>
<q-dialog v-model="dialog" class="dialog" persistent>
<q-card>
<q-form @submit.prevent greedy @validation-success="onSubmit()">
<dialog-header
:tittle="
dialogStatus == 'edit'
? 'แก้ไขข้อมูลใบอนุญาตประกอบวิชาชีพ'
: 'เพิ่มข้อมูลใบอนุญาตประกอบวิชาชีพ'
"
:close="closeDialog"
/>
<q-separator />
<q-card-section>
<div class="row q-col-gutter-sm q-pb-sm">
<div class="col-xs-12 col-md-6">
<q-input
outlined
v-model="profesLicenseData.certificateType"
label="ชื่อใบอนุญาต"
bg-color="white"
dense
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกชื่อใบอนุญาต'}`]"
hide-bottom-space
/>
</div>
<div class="col-xs-12 col-md-6">
<q-input
outlined
v-model="profesLicenseData.issuer"
label="หน่วยงานผู้ออกใบอนุญาต"
bg-color="white"
dense
class="inputgreen"
:rules="[
(val) => !!val || `${'กรุณากรอกหน่วยงานผู้ออกใบอนุญาต'}`,
]"
hide-bottom-space
/>
</div>
</div>
<div class="row q-col-gutter-sm q-pb-sm">
<div class="col-xs-12 col-md-6">
<q-input
outlined
v-model="profesLicenseData.certificateNo"
label="เลขที่ใบอนุญาต"
bg-color="white"
class="inputgreen"
dense
:rules="[(val) => !!val || `${'กรุณากรอกเลขที่ใบอนุญาต'}`]"
hide-bottom-space
/>
</div>
<div class="col-xs-12 col-md-6">
<datepicker
menu-class-name="modalfix"
v-model="profesLicenseData.issueDate"
:locale="'th'"
autoApply
class="col"
:enableTimePicker="false"
@update:modelValue="profesLicenseData.issueDate"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
class="inputgreen"
outlined
bg-color="white"
hide-bottom-space
:model-value="
profesLicenseData.issueDate
? date2Thai(profesLicenseData.issueDate)
: ''
"
:rules="[
(val) => !!val || `${'กรุณาเลือกวันที่ออกใบอนุญาต'}`,
]"
:label="`${'วันที่ออกใบอนุญาต'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</div>
<div class="row q-gutter-sm q-pb-sm">
<div class="col">
<datepicker
menu-class-name="modalfix"
v-model="profesLicenseData.expireDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
@update:modelValue="profesLicenseData.expireDate"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
outlined
bg-color="white"
class="inputgreen"
clearable
@clear="() => (profesLicenseData.expireDate = null)"
hide-bottom-space
:model-value="
profesLicenseData.expireDate
? date2Thai(profesLicenseData.expireDate)
: ''
"
:label="`${'วันที่หมดอายุ'}`"
@update:modelValue="profesLicenseData.expireDate = null"
>
<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-separator />
<q-card-actions align="right">
<q-btn label="บันทึก" id="onSubmit" type="submit" color="public">
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<q-dialog v-model="historyDialog" class="dialog" persistent>
<q-card style="min-width: 80%">
<dialog-header
tittle="ประวัติแก้ไขใบอนุญาตประกอบวิชาชีพ"
:close="closeHistoryDialog"
/>
<q-separator />
<q-card-section style="max-height: 60vh" class="scroll">
<div class="row q-gutter-sm q-mb-sm">
<q-space />
<q-input
dense
outlined
bg-color="white"
v-model="historyKeyword"
label="ค้นหา"
>
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
<q-select
v-model="historyVisibleColumns"
multiple
outlined
dense
bg-color="white"
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="historyColumns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
<d-table
ref="table"
:columns="historyColumns"
:rows="historyRows"
row-key="name"
flat
:filter="historyKeyword"
v-model:pagination="historyPagination"
bordered
:paging="true"
dense
:rows-per-page-options="[20, 50, 100]"
class="custom-header-table"
:visible-columns="historyVisibleColumns"
>
<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 ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card-section>
</q-card>
</q-dialog>
</template>
<style scoped>
.bg-color {
background-color: #fafafa;
}
.label-color {
color: #747474cc;
}
</style>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,835 @@
<script setup lang="ts">
import { ref, onMounted, reactive, watch } from "vue";
import { useRoute } from "vue-router";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps, QForm } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import DialogHeader from "@/components/DialogHeader.vue";
import type { RequestItemsObject } from "@/modules/04_registryPerson/interface/request/DeclarationHonor";
import type { ResponseObject } from "@/modules/04_registryPerson/interface/response/DeclarationHonor";
const $q = useQuasar();
const route = useRoute();
const mixin = useCounterMixin();
const {
date2Thai,
success,
messageError,
showLoader,
hideLoader,
dialogConfirm,
} = mixin;
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const id = ref<string>("");
const issueDateYear = ref<number>(0);
const declHonorForm = reactive<RequestItemsObject>({
isDate: "false",
issuer: "",
detail: "",
issueDate: null,
refCommandNo: "",
refCommandDate: null,
});
const isEdit = ref<boolean>(false);
const modal = ref<boolean>(false);
const modeView = ref<string>("table");
const filterSearch = ref("");
const filterHistory = ref<string>("");
const modalHistory = ref<boolean>(false);
const rowsHistory = ref<ResponseObject[]>([]);
const rows = ref<ResponseObject[]>([]);
const columns = ref<QTableProps["columns"]>([
{
name: "issueDate",
align: "left",
label: "วันที่ได้รับ",
sortable: true,
field: (v) =>
v.isDate
? date2Thai(v.issueDate)
: new Date(v.issueDate).getFullYear() + 543,
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "issuer",
align: "left",
label: "ผู้มีอำนาจลงนาม",
sortable: true,
field: "issuer",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "issueDate",
align: "left",
label: "วันที่ได้รับ",
sortable: true,
field: (v) =>
v.isDate
? date2Thai(v.issueDate)
: new Date(v.issueDate).getFullYear() + 543,
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "issuer",
align: "left",
label: "ผู้มีอำนาจลงนาม",
sortable: true,
field: "issuer",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
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: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumns = ref<String[]>([
"issuer",
"detail",
"issueDate",
"refCommandNo",
"refCommandDate",
]);
const visibleColumnsHistory = ref<String[]>([
"issuer",
"detail",
"issueDate",
"refCommandNo",
"refCommandDate",
"lastUpdateFullName",
"lastUpdatedAt",
]);
const pagination = ref({
page: 1,
rowsPerPage: 10,
});
const historyPagination = ref({
page: 1,
rowsPerPage: 10,
});
async function fetchData() {
if (!profileId.value) return;
showLoader();
try {
const res = await http.get(
config.API.profileNewHonorByProfileId(profileId.value, empType.value)
);
rows.value = res.data.result;
} catch (error) {
messageError($q, error);
} finally {
hideLoader();
}
}
async function addEditData(editStatus: boolean = false) {
if (!profileId.value) return;
const url = editStatus
? config.API.profileNewHonorById(id.value, empType.value)
: config.API.profileNewHonor(empType.value);
const method = editStatus ? "patch" : "post";
const reqBody: RequestItemsObject = {
...declHonorForm,
profileEmployeeId:
!editStatus && empType.value !== "" ? profileId.value : undefined,
profileId:
!editStatus && empType.value === "" ? profileId.value : undefined,
isDate: declHonorForm.isDate === "true" ? true : false,
issueDate:
declHonorForm.isDate === "true"
? declHonorForm.issueDate
: new Date(`${issueDateYear.value}-01-01`),
};
try {
await http[method](url, reqBody);
success($q, "บันทึกข้อมูลสำเร็จ");
await fetchData();
} catch (e) {
messageError($q, e);
} finally {
hideLoader();
}
}
function onClickOpenDialog(editStatus: boolean = false, row?: ResponseObject) {
modal.value = true;
isEdit.value = editStatus;
if (editStatus && row) {
id.value = row.id;
issueDateYear.value = new Date(row.issueDate).getFullYear();
declHonorForm.issuer = row.issuer;
declHonorForm.detail = row.detail;
declHonorForm.issueDate = row.issueDate;
declHonorForm.refCommandNo = row.refCommandNo;
declHonorForm.refCommandDate = row.refCommandDate;
declHonorForm.isDate = row.isDate ? "true" : "false";
} else {
clearData();
}
}
async function clickClose() {
clearData();
modal.value = false;
}
async function clickHistory(row: ResponseObject) {
modalHistory.value = true;
filterSearch.value = "";
showLoader();
try {
const res = await http.get(
config.API.profileNewHonorHisById(row.id, empType.value)
);
rowsHistory.value = res.data.result;
} catch (e) {
messageError($q, e);
} finally {
hideLoader();
}
}
function onSubmit() {
dialogConfirm(
$q,
async () => {
addEditData(isEdit.value);
modal.value = false;
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
function clearData() {
id.value = "";
issueDateYear.value = new Date().getFullYear();
declHonorForm.issuer = "";
declHonorForm.detail = "";
declHonorForm.issueDate = new Date();
declHonorForm.refCommandNo = "";
declHonorForm.refCommandDate = null;
declHonorForm.isDate = "false";
}
onMounted(async () => {
await fetchData();
});
</script>
<template>
<div class="row items-center q-gutter-x-sm q-pb-sm">
<q-btn
dense
color="primary"
icon="add"
flat
round
@click="onClickOpenDialog()"
><q-tooltip>เพมขอม</q-tooltip></q-btn
>
<q-space />
<q-input
standout
dense
v-model="filterSearch"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterSearch == ''"
name="search"
@click.stop.prevent="filterSearch = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterSearch"
name="cancel"
@click.stop.prevent="filterSearch = ''"
class="cursor-pointer"
/>
</template>
</q-input>
<q-select
v-if="modeView == 'table'"
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-btn-toggle
v-model="modeView"
dense
class="no-shadow toggle-borderd"
toggle-color="grey-4"
:options="[
{ value: 'table', slot: 'table' },
{ value: 'card', slot: 'card' },
]"
>
<template v-slot:table>
<q-icon
name="format_list_bulleted"
size="24px"
:style="{
color: modeView === 'table' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
<template v-slot:card>
<q-icon
name="mdi-view-grid-outline"
size="24px"
:style="{
color: modeView === 'card' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
</q-btn-toggle>
</div>
<d-table
flat
dense
bordered
:rows="rows"
:paging="true"
:columns="columns"
:filter="filterSearch"
:grid="modeView === 'card'"
v-model:pagination="pagination"
:visible-columns="visibleColumns"
:rows-per-page-options="[20, 50, 100]"
:card-container-class="modeView === 'card' ? 'q-col-gutter-md' : ''"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width />
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props" v-if="modeView === 'table'">
<q-tr :props="props">
<q-td auto-width>
<q-btn
flat
dense
round
class="q-mr-xs"
size="14px"
color="primary"
icon="mdi-pencil-outline"
@click="onClickOpenDialog(true, props.row)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="clickHistory(props.row)"
>
<q-tooltip>ประวแกไขประกาศเกยรต</q-tooltip>
</q-btn>
</q-td>
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
class="cursor-pointer"
>
<div class="table_ellipsis">
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
<template v-slot:item="props" v-else>
<div class="col-xs-12 col-sm-6 col-md-6">
<q-card bordered>
<q-card-actions class="bg-grey-3" align="right">
<q-btn
flat
round
color="primary"
icon="mdi-pencil-outline"
@click="onClickOpenDialog(true, props.row)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
flat
round
color="info"
icon="mdi-history"
@click.stop.prevent="clickHistory(props.row)"
>
<q-tooltip>ประวแกไขประกาศเกยรต</q-tooltip>
</q-btn>
</q-card-actions>
<q-separator />
<div>
<q-item
v-for="(col, index) in props.cols.filter(
(col) => col.name !== 'desc'
)"
:key="col.name"
:class="index % 2 !== 0 ? 'bg-grey-1' : ''"
>
<q-item-section class="col-7 text-grey-6">
<q-item-label>{{ col.label }}</q-item-label>
</q-item-section>
<q-item-section class="text-dark">
<q-item-label>
{{ col.value ? col.value : "-" }}
</q-item-label>
</q-item-section>
</q-item>
</div>
</q-card>
</div>
</template>
</d-table>
<q-dialog v-model="modal" persistent>
<q-card>
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader
:tittle="
isEdit
? 'แก้ไขข้อมูลประกาศเกียรติคุณ'
: 'เพิ่มข้อมูลประกาศเกียรติคุณ'
"
:close="clickClose"
/>
<q-separator />
<q-card-section>
<div class="row col-12 q-col-gutter-sm">
<div class="row col-12 q-gutter-md q-py-sm text-grey-7">
<q-radio
v-model="(declHonorForm.isDate as string)"
dense
checked-icon="task_alt"
unchecked-icon="panorama_fish_eye"
val="false"
label="ปี"
/>
<q-radio
v-model="(declHonorForm.isDate as string)"
checked-icon="task_alt"
unchecked-icon="panorama_fish_eye"
val="true"
label="วัน/เดือน/ปี"
dense
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
autoApply
year-picker
v-model="issueDateYear"
week-start="0"
menu-class-name="modalfix"
:locale="'th'"
:enableTimePicker="false"
@update:modelValue="
(v:number) =>
(declHonorForm.issueDate = new Date(
`${v}-01-01T00:00:02.010+07:00`
))
"
v-if="declHonorForm.isDate === 'false'"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
dense
outlined
hide-bottom-space
class="inputgreen"
:model-value="issueDateYear + 543"
:rules="[
(val:string) =>
!!val ||
`${'กรุณาเลือกปีที่ได้รับ'}`,
]"
:label="`${'ปีที่ได้รับ'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
/>
</template>
</q-input>
</template>
</datepicker>
<datepicker
autoApply
borderless
v-else
week-start="0"
menu-class-name="modalfix"
v-model="declHonorForm.issueDate"
:locale="'th'"
:enableTimePicker="false"
@update:modelValue="
(v: Date) =>
(issueDateYear = parseInt(
v.toString().slice(11, 15)
))
"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
dense
outlined
hide-bottom-space
for="inputDatereceive"
ref="dateReceivedRef"
class="inputgreen"
:model-value="date2Thai(declHonorForm.issueDate)"
:label="`${'วันที่ได้รับ'}`"
:rules="[(val) => !!val || `${'กรุณาเลือกวันที่ได้รับ'}`]"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
/>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
class="inputgreen"
v-model="declHonorForm.issuer"
:label="`${'ผู้มีอำนาจลงนาม'}`"
/>
</div>
<div class="col-12">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
class="inputgreen"
v-model="declHonorForm.detail"
:label="`${'รายละเอียด'}`"
:rules="[(val) => !!val || `${'กรุณากรอกรายละเอียด'}`]"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
class="inputgreen"
v-model="declHonorForm.refCommandNo"
:label="`${'เลขที่คำสั่ง'}`"
>
<template v-slot:append>
<q-icon name="mdi-file" class="cursor-pointer" />
</template>
</q-input>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
autoApply
borderless
week-start="0"
menu-class-name="modalfix"
v-model="declHonorForm.refCommandDate"
:locale="'th'"
:enableTimePicker="false"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
dense
outlined
clearable
hide-bottom-space
class="inputgreen"
:model-value="
declHonorForm.refCommandDate == null
? null
: date2Thai(declHonorForm.refCommandDate)
"
:label="`${'เอกสารอ้างอิง (ลงวันที่)'}`"
@clear="declHonorForm.refCommandDate = null"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
/>
</template>
</q-input>
</template>
</datepicker>
</div>
</div>
</q-card-section>
<q-separator color="grey-4" />
<q-card-actions align="right">
<q-btn label="บันทึก" id="onSubmit" type="submit" color="public">
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<q-dialog v-model="modalHistory" persistent>
<q-card style="min-width: 80%">
<q-card-section class="flex justify-between" style="padding: 0">
<DialogHeader
tittle="ประวัติแก้ไขประกาศเกียรติคุณ"
:close="() => (modalHistory = false)"
/>
</q-card-section>
<q-separator />
<q-card-section class="q-p-sm">
<div class="row q-gutter-sm q-mb-sm">
<q-space />
<q-input
standout
dense
v-model="filterHistory"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterHistory == ''"
name="search"
@click.stop.prevent="filterHistory = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterHistory"
name="cancel"
@click.stop.prevent="filterHistory = ''"
class="cursor-pointer"
/>
</template>
</q-input>
<q-select
v-model="visibleColumnsHistory"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columnsHistory"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
<d-table
ref="table"
flat
bordered
dense
:columns="columnsHistory"
:rows="rowsHistory"
:paging="true"
v-model:pagination="historyPagination"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumnsHistory"
:filter="filterHistory"
>
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id">
<div>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card-section>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -0,0 +1,847 @@
<script setup lang="ts">
import { onMounted, ref, watch, reactive } from "vue";
import { useRoute } from "vue-router";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps, QForm } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useResultsPerformDataStore } from "@/modules/04_registryPerson/stores/ResultsPerformance";
import DialogHeader from "@/components/DialogHeader.vue";
import type { RequestItemsObject } from "@/modules/04_registryPerson/interface/request/ResultsPerformance";
import type { ResponseObject } from "@/modules/04_registryPerson/interface/response/ResultsPerformance";
const $q = useQuasar();
const route = useRoute();
const store = useResultsPerformDataStore();
const { textRangePoint, textPoint } = store;
const mixin = useCounterMixin();
const {
date2Thai,
success,
messageError,
showLoader,
hideLoader,
dialogConfirm,
} = mixin;
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const id = ref<string>("");
const resPerformForm = reactive<RequestItemsObject>({
name: "",
point1Total: 0,
point1: 0,
point2Total: 0,
point2: 0,
pointSumTotal: 0,
pointSum: 0,
date: null,
});
const currentPage = ref<number>(1);
const maxPage = ref<number>(1);
const currentPageHistory = ref<number>(1);
const maxPageHistory = ref<number>(1);
const isEdit = ref<boolean>(false);
const modal = ref<boolean>(false);
const modeView = ref<string>("table");
const filterSearch = ref("");
const filterHistory = ref<string>("");
const modalHistory = ref<boolean>(false);
const rowsHistory = ref<ResponseObject[]>([]);
const rows = ref<ResponseObject[]>([]);
const columns = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วันที่ได้รับ",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
},
{
name: "point1Total",
align: "left",
label: "ส่วนที่1 (น้ำหนัก)",
sortable: true,
field: "point1Total",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "point1",
align: "left",
label: "ผลประเมินส่วนที่1 (คะแนน)",
sortable: true,
field: "point1",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "point2Total",
align: "left",
label: "ส่วนที่2 (น้ำหนัก)",
sortable: true,
field: "point2Total",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "point2",
align: "left",
label: "ผลประเมินส่วนที่2 (คะแนน)",
sortable: true,
field: "point2",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "pointSumTotal",
align: "left",
label: "ผลรวม (น้ำหนัก)",
sortable: true,
field: "pointSumTotal",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "pointSum",
align: "left",
label: "ผลประเมินรวม (คะแนน)",
sortable: true,
field: "pointSum",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "name",
align: "left",
label: "ผลประเมิน",
sortable: true,
field: (v) => `${textPoint(v.pointSum)} ${textRangePoint(v.pointSum)}`,
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วันที่ได้รับ",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
},
{
name: "point1Total",
align: "left",
label: "ส่วนที่1 (น้ำหนัก)",
sortable: true,
field: "point1Total",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "point1",
align: "left",
label: "ผลประเมินส่วนที่1 (คะแนน)",
sortable: true,
field: "point1",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "point2Total",
align: "left",
label: "ส่วนที่2 (น้ำหนัก)",
sortable: true,
field: "point2Total",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "point2",
align: "left",
label: "ผลประเมินส่วนที่2 (คะแนน)",
sortable: true,
field: "point2",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "pointSumTotal",
align: "left",
label: "ผลรวม (น้ำหนัก)",
sortable: true,
field: "pointSumTotal",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "pointSum",
align: "left",
label: "ผลประเมินรวม (คะแนน)",
sortable: true,
field: "pointSum",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "name",
align: "left",
label: "ผลประเมิน",
sortable: true,
field: (v) => `${textPoint(v.pointSum)} ${textRangePoint(v.pointSum)}`,
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: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumnsHistory = ref<String[]>([
"point1Total",
"point1",
"point2Total",
"point2",
"pointSumTotal",
"pointSum",
"name",
"date",
"lastUpdateFullName",
"lastUpdatedAt",
]);
const visibleColumns = ref<String[]>([
"point1Total",
"point1",
"point2Total",
"point2",
"pointSumTotal",
"pointSum",
"name",
"date",
]);
const pagination = ref({
page: 1,
rowsPerPage: 10,
});
const historyPagination = ref({
page: 1,
rowsPerPage: 10,
});
async function fetchData() {
if (!profileId.value) return;
showLoader();
try {
const res = await http.get(
config.API.profileNewAssessmentsByProfileId(
profileId.value,
empType.value
)
);
rows.value = res.data.result;
} catch (error) {
messageError($q, error);
} finally {
hideLoader();
}
}
async function addEditData(editStatus: boolean = false) {
if (!profileId.value) return;
const url = editStatus
? config.API.profileNewAssessmentsById(id.value, empType.value)
: config.API.profileNewAssessments(empType.value);
const method = editStatus ? "patch" : "post";
const reqBody: RequestItemsObject = {
...resPerformForm,
profileEmployeeId:
!editStatus && empType.value !== "" ? profileId.value : undefined,
profileId:
!editStatus && empType.value === "" ? profileId.value : undefined,
};
try {
await http[method](url, reqBody);
success($q, "บันทึกข้อมูลสำเร็จ");
await fetchData();
} catch (e) {
messageError($q, e);
} finally {
hideLoader();
}
}
function onClickOpenDialog(editStatus: boolean = false, row?: ResponseObject) {
modal.value = true;
isEdit.value = editStatus;
if (editStatus && row) {
id.value = row.id;
resPerformForm.name = row.name;
resPerformForm.point1Total = row.point1Total;
resPerformForm.point1 = row.point1;
resPerformForm.point2Total = row.point2Total;
resPerformForm.point2 = row.point2;
resPerformForm.pointSumTotal = row.pointSumTotal;
resPerformForm.pointSum = row.pointSum;
resPerformForm.date = row.date;
} else {
clearData();
}
}
async function clickClose() {
clearData();
modal.value = false;
}
async function clickHistory(row: ResponseObject) {
modalHistory.value = true;
filterSearch.value = "";
showLoader();
try {
const res = await http.get(
config.API.profileNewAssessmentsHisById(row.id, empType.value)
);
rowsHistory.value = res.data.result;
} catch (e) {
messageError($q, e);
} finally {
hideLoader();
}
}
function onSubmit() {
dialogConfirm(
$q,
async () => {
addEditData(isEdit.value);
modal.value = false;
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
function clearData() {
(id.value = ""),
(resPerformForm.name = ""),
(resPerformForm.point1Total = 0),
(resPerformForm.point1 = 0),
(resPerformForm.point2Total = 0),
(resPerformForm.point2 = 0),
(resPerformForm.pointSumTotal = 0),
(resPerformForm.pointSum = 0),
(resPerformForm.date = null);
}
onMounted(async () => {
await fetchData();
});
</script>
<template>
<div class="row items-center q-gutter-x-sm q-pb-sm">
<q-btn
dense
color="primary"
icon="add"
flat
round
@click="onClickOpenDialog()"
><q-tooltip>เพมขอม</q-tooltip></q-btn
>
<q-space />
<q-input
standout
dense
v-model="filterSearch"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterSearch == ''"
name="search"
@click.stop.prevent="filterSearch = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterSearch"
name="cancel"
@click.stop.prevent="filterSearch = ''"
class="cursor-pointer"
/>
</template>
</q-input>
<q-select
v-if="modeView == 'table'"
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-btn-toggle
v-model="modeView"
dense
class="no-shadow toggle-borderd"
toggle-color="grey-4"
:options="[
{ value: 'table', slot: 'table' },
{ value: 'card', slot: 'card' },
]"
>
<template v-slot:table>
<q-icon
name="format_list_bulleted"
size="24px"
:style="{
color: modeView === 'table' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
<template v-slot:card>
<q-icon
name="mdi-view-grid-outline"
size="24px"
:style="{
color: modeView === 'card' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
</q-btn-toggle>
</div>
<d-table
flat
dense
bordered
:rows="rows"
:paging="true"
:columns="columns"
:filter="filterSearch"
v-model:pagination="pagination"
:grid="modeView === 'card'"
:visible-columns="visibleColumns"
:rows-per-page-options="[20, 50, 100]"
:card-container-class="modeView === 'card' ? 'q-col-gutter-md' : ''"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width />
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props" v-if="modeView === 'table'">
<q-tr :props="props">
<q-td auto-width>
<q-btn
flat
dense
round
class="q-mr-xs"
size="14px"
color="primary"
icon="mdi-pencil-outline"
@click="onClickOpenDialog(true, props.row)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="clickHistory(props.row)"
>
<q-tooltip>ประวแกไขผลการประเมนการปฏราชการ</q-tooltip>
</q-btn>
</q-td>
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
class="cursor-pointer"
>
<div class="table_ellipsis">
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
<template v-slot:item="props" v-else>
<div class="col-xs-12 col-sm-6 col-md-6">
<q-card bordered>
<q-card-actions class="bg-grey-3" align="right">
<q-btn
flat
round
color="primary"
icon="mdi-pencil-outline"
@click.stop.prevent="onClickOpenDialog(true, props.row)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
flat
round
color="info"
icon="mdi-history"
@click="clickHistory(props.row)"
>
<q-tooltip>ประวแกไขเครองราชอสรยาภรณ</q-tooltip>
</q-btn>
</q-card-actions>
<q-separator />
<div>
<q-item
v-for="(col, index) in props.cols.filter(
(col) => col.name !== 'desc'
)"
:key="col.name"
:class="index % 2 !== 0 ? 'bg-grey-1' : ''"
>
<q-item-section class="text-grey-6">
<q-item-label>{{ col.label }}</q-item-label>
</q-item-section>
<q-item-section class="text-dark">
<q-item-label>
{{ col.value ? col.value : "-" }}
</q-item-label>
</q-item-section>
</q-item>
</div>
</q-card>
</div>
</template>
</d-table>
<q-dialog v-model="modal" persistent>
<q-card>
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader
:tittle="
isEdit
? 'แก้ไขผลการประเมินการปฏิบัติราชการ'
: 'เพิ่มผลการประเมินการปฏิบัติราชการ'
"
:close="clickClose"
/>
<q-separator />
<q-card-section>
<div class="row col-12 q-col-gutter-sm">
<div class="col-12">
<datepicker
autoApply
borderless
week-start="0"
menu-class-name="modalfix"
v-model="resPerformForm.date"
:locale="'th'"
:enableTimePicker="false"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
dense
outlined
for="inputDatereceive"
ref="dateReceivedRef"
class="inputgreen"
hide-bottom-space
:model-value="date2Thai(resPerformForm.date as Date)"
:label="`${'วันที่ได้รับ'}`"
:rules="[(val) => !!val || `${'กรุณาเลือกวันที่ได้รับ'}`]"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
/>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
class="inputgreen"
v-model="resPerformForm.point1Total"
input-class="text-right "
:label="`${'ส่วนที่1 (น้ำหนัก)'}`"
:rules="[(val) => !!val || `${'กรุณากรอกส่วนที่1 (น้ำหนัก)'}`]"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
class="inputgreen"
v-model="resPerformForm.point1"
input-class="text-right"
:label="`${'ผลประเมินส่วนที่1 (คะแนน)'}`"
:rules="[
(val) => !!val || `${'กรุณากรอกผลประเมินส่วนที่1 (คะแนน)'}`,
]"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
class="inputgreen"
v-model="resPerformForm.point2Total"
input-class="text-right"
:label="`${'ส่วนที่2 (น้ำหนัก)'}`"
:rules="[(val) => !!val || `${'กรุณากรอกส่วนที่2 (น้ำหนัก)'}`]"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
class="inputgreen"
v-model="resPerformForm.point2"
input-class="text-right"
:label="`${'ผลประเมินส่วนที่2 (คะแนน)'}`"
:rules="[
(val) => !!val || `${'กรุณากรอกผลประเมินส่วนที่2 (คะแนน)'}`,
]"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
class="inputgreen"
v-model="resPerformForm.pointSumTotal"
input-class="text-right"
:label="`${'ผลรวม (น้ำหนัก)'}`"
:rules="[(val) => !!val || `${'กรุณากรอกผลรวม (น้ำหนัก)'}`]"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
v-model="resPerformForm.pointSum"
class="inputgreen"
input-class="text-right"
:label="`${'ผลประเมินรวม (คะแนน)'}`"
:rules="[
(val) => !!val || `${'กรุณากรอกผลประเมินรวม (คะแนน)'}`,
]"
/>
</div>
<div class="col-12 row items-center q-pt-md justify-center">
ผลการประเม:
<div class="text-bold items-center q-px-sm">
{{ textPoint(resPerformForm.pointSum) }}
</div>
{{ textRangePoint(resPerformForm.pointSum) }}
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn label="บันทึก" id="onSubmit" type="submit" color="public">
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<q-dialog v-model="modalHistory" persistent>
<q-card style="min-width: 80%">
<DialogHeader
tittle="ประวัติแก้ไขผลการประเมินการปฏิบัติราชการ"
:close="() => (modalHistory = false)"
/>
<q-separator />
<q-card-section style="max-height: 60vh" class="scroll">
<div class="row q-gutter-sm q-mb-sm">
<q-space />
<q-input
standout
dense
v-model="filterHistory"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterHistory == ''"
name="search"
@click.stop.prevent="filterHistory = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterHistory"
name="cancel"
@click.stop.prevent="filterHistory = ''"
class="cursor-pointer"
/>
</template>
</q-input>
<q-select
v-model="visibleColumnsHistory"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columnsHistory"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
<d-table
ref="table"
flat
bordered
dense
:columns="columnsHistory"
v-model:pagination="historyPagination"
:rows="rowsHistory"
:paging="true"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumnsHistory"
:filter="filterHistory"
>
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id">
<div>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card-section>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -0,0 +1,64 @@
<script setup lang="ts">
import { ref } from "vue";
/** importComponents*/
import ProfessionalLicense from "@/modules/04_registryPerson/components/detail/Achievement/01_ProfessionalLicense.vue";
import Train from "@/modules/04_registryPerson/components/detail/Achievement/02_Train.vue";
import Insignia from "@/modules/04_registryPerson/components/detail/Achievement/03_Insignia.vue";
import DeclarationHonor from "@/modules/04_registryPerson/components/detail/Achievement/04_DeclarationHonor.vue";
import ResultsPerformance from "@/modules/04_registryPerson/components/detail/Achievement/05_ResultsPerformance.vue";
const tab = ref<string>("1");
</script>
<template>
<div class="row items-center q-my-md">
<div class="text-dark row items-center q-px-md">
<q-icon name="mdi-account" class="q-mr-md" size="22px" />
<div class="text-subtitle1 text-weight-bold">
อมลผลงานและเครองราชฯ
</div>
</div>
</div>
<q-separator />
<q-tabs
v-model="tab"
active-color="blue-8"
align="left"
bordered
narrow-indicator
indicator-color="transparent"
dense
class="text-grey q-pl-sm"
>
<q-tab name="1" label="ใบอนุญาตประกอบวิชาชีพ" />
<q-tab name="2" label="การฝึกอบรม/ดูงาน" />
<q-tab name="3" label="เครื่องราชอิสริยาภรณ์" />
<q-tab name="4" label="ประกาศเกียรติคุณ" />
<q-tab name="5" label="ผลการประเมินการปฏิบัติราชการ" />
</q-tabs>
<q-separator />
<q-tab-panels v-model="tab" animated>
<q-tab-panel name="1">
<ProfessionalLicense />
</q-tab-panel>
<q-tab-panel name="2">
<Train />
</q-tab-panel>
<q-tab-panel name="3">
<Insignia />
</q-tab-panel>
<q-tab-panel name="4">
<DeclarationHonor />
</q-tab-panel>
<q-tab-panel name="4">
<PerformSpecialWork />
</q-tab-panel>
<q-tab-panel name="5">
<ResultsPerformance />
</q-tab-panel>
</q-tab-panels>
</template>
<style scoped></style>

View file

@ -0,0 +1,633 @@
<script setup lang="ts">
import { onMounted, watch, ref, reactive } from "vue";
import { useQuasar } from "quasar";
import { useRoute } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
import type { FormEmployee } from "@/modules/04_registryPerson/interface/request/Employee";
import type {
EmployeeHistory,
ResEmployee,
} from "@/modules/04_registryPerson/interface/response/Employee";
import DialogHeader from "@/components/DialogHeader.vue";
import type { QTableColumn } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
const $q = useQuasar();
const route = useRoute();
const {
success,
showLoader,
hideLoader,
date2Thai,
messageError,
dialogConfirm,
dialogMessageNotify,
} = useCounterMixin();
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
/** ข้อมูลลูกจ้างชั่วคราว*/
const modalEdit = ref<boolean>(false);
const dataEmployee = reactive<ResEmployee>({
id: "",
positionEmployeeGroupId: "",
positionEmployeeLineId: "",
positionEmployeePositionId: "",
employeeOc: "",
employeeTypeIndividual: "",
employeeWage: "",
employeeMoneyIncrease: "",
employeeMoneyAllowance: "",
employeeMoneyEmployee: "",
employeeMoneyEmployer: "",
});
const formData = reactive<FormEmployee>({
positionEmployeeGroupId: "",
positionEmployeeLineId: "",
positionEmployeePositionId: "",
employeeOc: "",
employeeTypeIndividual: "",
employeeWage: "",
employeeMoneyIncrease: "",
employeeMoneyAllowance: "",
employeeMoneyEmployee: "",
employeeMoneyEmployer: "",
});
/** function fetch ข้อมูลลูกจ้างชั่วคราว*/
function fetchData() {
showLoader();
http
.get(config.API.informationEmployee(profileId.value))
.then((res) => {
const data = res.data.result;
dataEmployee.positionEmployeeGroupId = data.positionEmployeeGroupId;
dataEmployee.positionEmployeeLineId = data.positionEmployeeLineId;
dataEmployee.positionEmployeePositionId = data.positionEmployeePositionId;
dataEmployee.employeeOc = data.employeeOc;
dataEmployee.employeeTypeIndividual = data.employeeTypeIndividual;
dataEmployee.employeeWage = data.employeeWage;
dataEmployee.employeeMoneyIncrease = data.employeeMoneyIncrease;
dataEmployee.employeeMoneyAllowance = data.employeeMoneyAllowance;
dataEmployee.employeeMoneyEmployee = data.employeeMoneyEmployee;
dataEmployee.employeeMoneyEmployer = data.employeeMoneyEmployer;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** function เปิด Dialog แก้ไขมูลลูกจ้างชั่วคราว*/
function onClickEdit() {
modalEdit.value = true;
formData.positionEmployeeGroupId = dataEmployee.positionEmployeeGroupId;
formData.positionEmployeeLineId = dataEmployee.positionEmployeeLineId;
formData.positionEmployeePositionId = dataEmployee.positionEmployeePositionId;
formData.employeeOc = dataEmployee.employeeOc;
formData.employeeTypeIndividual = dataEmployee.employeeTypeIndividual;
formData.employeeWage = dataEmployee.employeeWage;
formData.employeeMoneyIncrease = dataEmployee.employeeMoneyIncrease;
formData.employeeMoneyAllowance = dataEmployee.employeeMoneyAllowance;
formData.employeeMoneyEmployee = dataEmployee.employeeMoneyEmployee;
formData.employeeMoneyEmployer = dataEmployee.employeeMoneyEmployer;
}
/** function ปิด Dialog แก้ไขมูลลูกจ้างชั่วคราว*/
function onCloseDialog() {
modalEdit.value = false;
formData.positionEmployeeGroupId = "";
formData.positionEmployeeLineId = "";
formData.positionEmployeePositionId = "";
formData.employeeOc = "";
formData.employeeTypeIndividual = "";
formData.employeeWage = "";
formData.employeeMoneyIncrease = "";
formData.employeeMoneyAllowance = "";
formData.employeeMoneyEmployee = "";
formData.employeeMoneyEmployer = "";
}
/** function บันทึกแก้ไขมูลลูกจ้างชั่วคราว*/
function onSubmit() {
dialogConfirm($q, () => {
showLoader();
http
.put(config.API.informationEmployee(profileId.value), formData)
.then(() => {
success($q, "บันทึกข้อมูลสำเร็จ");
fetchData();
onCloseDialog();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
/** ประวัติข้อมูลลูกจ้างชั่วคราว */
const modalHistory = ref<boolean>(false);
const filter = ref<string>("");
const rows = ref<EmployeeHistory[]>([]);
const columns = ref<QTableColumn[]>([
{
name: "positionEmployeeGroupId",
align: "left",
label: "กลุ่มงาน",
sortable: true,
field: "positionEmployeeGroupId",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionEmployeeLineId",
align: "left",
label: "สายงาน",
sortable: true,
field: "positionEmployeeLineId",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionEmployeePositionId",
align: "left",
label: "ตำแหน่งทางสายงาน",
sortable: true,
field: "positionEmployeePositionId",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "employeeOc",
align: "left",
label: "สังกัด",
sortable: true,
field: "employeeOc",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "employeeTypeIndividual",
align: "left",
label: "ประเภทบุคคล",
sortable: true,
field: "employeeTypeIndividual",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "employeeWage",
align: "left",
label: "ค่าจ้าง",
sortable: true,
field: "employeeWage",
format: (v) => (v ? Number(v).toLocaleString() : ""),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "employeeMoneyIncrease",
align: "left",
label: "เงินเพิ่มการครองชึพชั่วคราว",
sortable: true,
field: "employeeMoneyIncrease",
format: (v) => (v ? Number(v).toLocaleString() : ""),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "employeeMoneyAllowance",
align: "left",
label: "เงินช่วยเหลือการครองชึพชั่วคราว",
sortable: true,
field: "employeeMoneyAllowance",
format: (v) => (v ? Number(v).toLocaleString() : ""),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "employeeMoneyEmployee",
align: "left",
label: "เงินสมทบประกันสังคม(ลูกจ้าง)",
sortable: true,
field: "employeeMoneyEmployee",
format: (v) => (v ? Number(v).toLocaleString() : ""),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "employeeMoneyEmployer",
align: "left",
label: "เงินสมทบประกันสังคม(นายจ้าง)",
sortable: true,
field: "employeeMoneyEmployer",
format: (v) => (v ? Number(v).toLocaleString() : ""),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "createdFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "createdFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "createdAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
},
]);
const visibleColumns = ref<String[]>([
"positionEmployeeGroupId",
"positionEmployeeLineId",
"positionEmployeePositionId",
"employeeOc",
"employeeTypeIndividual",
"employeeWage",
"employeeMoneyIncrease",
"employeeMoneyAllowance",
"employeeMoneyEmployee",
"employeeMoneyEmployer",
"createdFullName",
"createdAt",
]);
function onClickHistory() {
showLoader();
modalHistory.value = true;
http
.get(config.API.informationHistoryEmployee(profileId.value))
.then((res) => {
const data = res.data.result;
rows.value = data;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(() => {
profileId.value && fetchData();
});
</script>
<template>
<div class="row q-gutter-sm items-center">
<div class="toptitle col text-right q-gutter-x-sm">
<q-btn
flat
round
dense
icon="mdi-pencil-outline"
color="primary"
@click="onClickEdit"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
flat
round
dense
icon="mdi-history"
color="info"
@click="onClickHistory"
>
<q-tooltip>ประวแกไขขอมลลกจางชวคราว</q-tooltip>
</q-btn>
</div>
</div>
<q-card bordered class="my-card bg-grey-1 q-pa-md">
<div :class="$q.screen.gt.xs ? 'row' : 'column'">
<!-- column 1 -->
<div class="col-md-7 col-12 row">
<div class="col-5 text-grey-6 text-weight-medium">
<div class="q-py-xs">กลมงาน</div>
<div class="q-py-xs">สายงาน</div>
<div class="q-py-xs">ตำแหนงทางสายงาน</div>
<div class="q-py-xs">งก</div>
<div class="q-py-xs">ประเภทบคคล</div>
</div>
<!-- data -->
<div class="col-7">
<div class="q-py-xs">
{{ dataEmployee.positionEmployeeGroupId }}
</div>
<div class="q-py-xs">
{{ dataEmployee.positionEmployeeLineId }}
</div>
<div class="q-py-xs">
{{ dataEmployee.positionEmployeePositionId }}
</div>
<div class="q-py-xs">
{{ dataEmployee.employeeOc }}
</div>
<div class="q-py-xs">
{{ dataEmployee.employeeTypeIndividual }}
</div>
</div>
</div>
<!-- column 2 -->
<div class="col-md-5 col-12 row">
<div class="col-5 col text-grey-6 text-weight-medium">
<div class="q-py-xs">าจาง</div>
<div class="q-py-xs">เงนเพมการครองชพชวคราว</div>
<div class="q-py-xs">เงนชวยเหลอการครองชพชวคราว</div>
<div class="q-py-xs">เงนสมทบประกนสงคม(กจาง)</div>
<div class="q-py-xs">เงนสมทบประกนสงคม(นายจาง)</div>
</div>
<div class="col-7">
<div class="q-py-xs">
{{ dataEmployee.employeeWage }}
</div>
<div class="q-py-xs">
{{ dataEmployee.employeeMoneyIncrease }}
</div>
<div class="q-py-xs">
{{ dataEmployee.employeeMoneyAllowance }}
</div>
<div class="q-py-xs">
{{ dataEmployee.employeeMoneyEmployee }}
</div>
<div class="q-py-xs">
{{ dataEmployee.employeeMoneyEmployer }}
</div>
</div>
</div>
</div>
</q-card>
<!-- Dialog แกไขขอมลลกจางชวคราว -->
<q-dialog v-model="modalEdit" persistent full-width>
<q-card>
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader
tittle="แก้ไขข้อมูลลูกจ้างชั่วคราว"
:close="onCloseDialog"
/>
<q-separator />
<q-card-section>
<div class="row col-12 q-col-gutter-sm">
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
v-model="formData.positionEmployeeGroupId"
class="inputgreen"
label="กลุ่มงาน"
:rules="[(val: string) => !!val || `${'กรุณากรอกกลุ่มงาน'}`]"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
v-model="formData.positionEmployeeLineId"
class="inputgreen"
label="สายงาน"
:rules="[(val: string) => !!val || `${'กรุณากรอกสายงาน'}`]"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
v-model="formData.positionEmployeePositionId"
class="inputgreen"
label="ตำแหน่งทางสายงาน"
:rules="[(val: string) => !!val || `${'กรุณากรอกตำแหน่งทางสายงาน'}`]"
/>
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
v-model="formData.employeeOc"
class="inputgreen"
label="สังกัด"
/>
<!-- :rules="[(val: string) => !!val || `${'กรุณากรอกสังกัด'}`]" -->
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
v-model="formData.employeeTypeIndividual"
class="inputgreen"
label="ประเภทบุคคล"
/>
<!-- :rules="[(val: string) => !!val || `${'กรุณากรอกประเภทบุคคล'}`]" -->
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
v-model="formData.employeeWage"
class="inputgreen"
label="ค่าจ้าง"
mask="###,###,###,###"
reverse-fill-mask
/>
<!-- :rules="[(val: string) => !!val || `${'กรุณากรอกค่าจ้าง'}`]" -->
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
v-model="formData.employeeMoneyIncrease"
class="inputgreen"
label="เงินเพิ่มการครองชึพชั่วคราว"
mask="###,###,###,###"
reverse-fill-mask
/>
<!-- :rules="[(val: string) => !!val || `${'กรุณากรอกเงินเพิ่มการครองชึพชั่วคราว'}`]" -->
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
v-model="formData.employeeMoneyAllowance"
class="inputgreen"
label="เงินช่วยเหลือการครองชึพชั่วคราว"
mask="###,###,###,###"
reverse-fill-mask
/>
<!-- :rules="[(val: string) => !!val || `${'กรุณากรอกเงินช่วยเหลือการครองชึพชั่วคราว'}`]" -->
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
v-model="formData.employeeMoneyEmployee"
class="inputgreen"
label="เงินสมทบประกันสังคม(ลูกจ้าง)"
mask="###,###,###,###"
reverse-fill-mask
/>
<!-- :rules="[(val: string) => !!val || `${'กรุณากรอกเงินสมทบประกันสังคม(ลูกจ้าง)'}`]" -->
</div>
<div class="col-xs-6 col-sm-3 col-md-3">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
v-model="formData.employeeMoneyEmployer"
class="inputgreen"
label="เงินสมทบประกันสังคม(นายจ้าง)"
mask="###,###,###,###"
reverse-fill-mask
/>
<!-- :rules="[(val: string) => !!val || `${'กรุณากรอกเงินสมทบประกันสังคม(นายจ้าง))'}`]" -->
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn label="บันทึก" id="onSubmit" type="submit" color="public">
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<!-- Dialog ประวการแกไขขอมลลกจางชวคราว -->
<q-dialog v-model="modalHistory" persistent>
<q-card style="min-width: 80%">
<DialogHeader
tittle="ประวัติแก้ไขข้อมูลส่วนตัว"
:close="() => (modalHistory = false)"
/>
<q-separator />
<q-card-section style="max-height: 50vh" class="scroll">
<div class="row q-gutter-sm q-mb-sm">
<q-space />
<q-input
standout
dense
v-model="filter"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filter == ''"
name="search"
@click.stop.prevent="filter = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filter"
name="cancel"
@click.stop.prevent="filter = ''"
class="cursor-pointer"
/>
</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>
<d-table
ref="table"
flat
bordered
dense
:columns="columns"
:rows="rows"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumns"
:filter="filter"
>
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id">
<div>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card-section>
<q-separator />
<q-card-actions align="right"> </q-card-actions>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -0,0 +1,514 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { useRoute } from "vue-router";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps } from "quasar";
import type {
Employment,
EmploymentHistory,
} from "@/modules/04_registryPerson/interface/response/Employee";
import type { FormEmployment } from "@/modules/04_registryPerson/interface/request/Employee";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
const $q = useQuasar();
const route = useRoute();
const {
date2Thai,
dialogConfirm,
dialogRemove,
success,
messageError,
hideLoader,
showLoader,
} = useCounterMixin();
const profileId = ref<string>(route.params.id.toString());
/** ข้อมูลการจ้าง*/
const rows = ref<Employment[]>([]);
const filter = ref<string>("");
const columns = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วันที่จ้าง",
sortable: false,
field: "date",
format: (v) => date2Thai(v),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "command",
align: "left",
label: "คำสั่งจ้าง",
sortable: false,
field: "command",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "createdFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "createdFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "createdAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
},
]);
const visibleColumns = ref<string[]>([
"date",
"command",
"createdFullName",
"createdAt",
]);
const modalEmployment = ref<boolean>(false);
const isEdit = ref<boolean>(false);
const employmentId = ref<string>("");
const formData = reactive<FormEmployment>({
date: null,
command: "",
});
/** function fetch ข้อมูลรายการการจ้าง*/
function fetchListEmployment() {
showLoader();
http
.get(config.API.employmentEmployee(profileId.value))
.then((res) => {
const data = res.data.result;
rows.value = data;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* function fetch อมลการจาง
* @param id รายการการจาง
*/
function fetchDataEmployment(id: string) {
showLoader();
http
.get(config.API.employmentEmployeeId(id))
.then((res) => {
const data = res.data.result;
formData.date = data.date;
formData.command = data.command;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* function เป dialog Form อมลการจาง
* @param statusEdit สถานะการ สราง หร แกไข
* @param id รายการการจาง
*/
function onOpenDialog(statusEdit: boolean = false, id: string = "") {
modalEmployment.value = true;
if (statusEdit) {
isEdit.value = statusEdit;
employmentId.value = id;
fetchDataEmployment(id);
}
}
/** function ปิด dialog Form ข้อมูลการจ้าง*/
function onCloseDialog() {
modalEmployment.value = false;
isEdit.value = false;
employmentId.value = "";
formData.date = null;
formData.command = "";
}
/** function บันทึกข้อมูลการจ้าง*/
function onSubmit() {
dialogConfirm($q, () => {
showLoader();
const methods = isEdit.value ? "put" : "post";
const id = isEdit.value ? employmentId.value : profileId.value;
http[methods](config.API.employmentEmployee(id), formData)
.then(() => {
success($q, "บันทึกข้อมูลสำเร็จ");
fetchListEmployment();
onCloseDialog();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
/**
* function ลบขอมลรายการจาง
* @param id รายการจาง
*/
function onDeleteEmployment(id: string) {
dialogRemove($q, () => {
showLoader();
http
.delete(config.API.employmentEmployee(id))
.then(() => {
success($q, "ลบข้อมูลสำเร็จ");
fetchListEmployment();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
/** ประวัติข้อมูลการจ้าง*/
const modalHistory = ref<boolean>(false);
const rowsHistory = ref<EmploymentHistory[]>([]);
const filterHistory = ref<string>("");
/**
* function เป dialog ประวการแกไขขอมลการจาง
* @param id รายการการจาง
*/
function onClickHistory(id: string) {
modalHistory.value = true;
showLoader();
http
.get(config.API.employmentHistoryEmployee(id))
.then((res) => {
const data = res.data.result;
rowsHistory.value = data;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(() => {
profileId.value && fetchListEmployment();
});
</script>
<template>
<div class="flex items-center">
<div class="q-gutter-sm">
<q-btn
size="12px"
flat
round
color="primary"
icon="mdi-plus"
@click="onOpenDialog()"
>
<q-tooltip>เพมขอมลการจาง</q-tooltip>
</q-btn>
</div>
<q-space />
<div class="q-gutter-sm" style="display: flex">
<q-input outlined dense v-model="filter" label="ค้นหา">
<template v-slot:append>
<q-icon
v-if="filter == ''"
name="search"
@click.stop.prevent="filter = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filter"
name="cancel"
@click.stop.prevent="filter = ''"
class="cursor-pointer"
/> </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?.slice(0, 2)"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</div>
<div class="q-mt-sm">
<d-table
flat
bordered
id="table"
ref="table"
:columns="columns?.slice(0, 2)"
:rows="rows"
:filter="filter"
row-key="dateEmployment"
:paging="true"
dense
:visible-columns="visibleColumns"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<q-th auto-width></q-th>
<q-th auto-width></q-th>
<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" style="height: 40px">
<q-td>
<q-btn
dense
flat
round
color="primary"
icon="mdi-pencil"
@click="onOpenDialog(true, props.row.id)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
</q-td>
<q-td>
<q-btn
dense
flat
round
color="red"
icon="mdi-delete"
@click="onDeleteEmployment(props.row.id)"
>
<q-tooltip>ลบขอม</q-tooltip></q-btn
>
</q-td>
<q-td>
<q-btn
dense
flat
round
color="info"
icon="mdi-history"
@click="onClickHistory(props.row.id)"
>
<q-tooltip>ประวอมลการจาง </q-tooltip>
</q-btn>
</q-td>
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</div>
<!-- Dialog เพมขอมลการจาง -->
<q-dialog v-model="modalEmployment" persistent>
<q-card style="width: 700px; max-width: 80vw">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader
:tittle="isEdit ? 'แก้ข้อมูลการจ้าง' : 'เพิ่มข้อมูลการจ้าง'"
:close="onCloseDialog"
/>
<q-separator />
<q-card-section>
<div class="row col-12 q-col-gutter-sm">
<div class="col-xs-12 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
v-model="formData.date"
:locale="'th'"
autoApply
borderless
:enableTimePicker="false"
week-start="0"
class="inputgreen"
>
<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="
formData.date != null ? date2Thai(formData.date) : null
"
label="วันที่จ้าง"
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณาเลือกวันที่จ้าง'}`]"
>
<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-xs-12 col-sm-6 col-md-6">
<q-input
dense
class="inputgreen"
outlined
v-model="formData.command"
label="คำสั่งจ้าง"
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกคำสั่งจ้าง'}`]"
lazy-rules
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn label="บันทึก" id="onSubmit" type="submit" color="public">
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<!-- Dialog ประวการแกไขขอมลลกจางชวคราว -->
<q-dialog v-model="modalHistory" persistent>
<q-card style="min-width: 80%">
<DialogHeader
tittle="ประวัติแก้ไขข้อมูลส่วนตัว"
:close="() => (modalHistory = false)"
/>
<q-separator />
<q-card-section style="max-height: 50vh" class="scroll">
<div class="row q-gutter-sm q-mb-sm">
<q-space />
<q-input
standout
dense
v-model="filterHistory"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterHistory == ''"
name="search"
@click.stop.prevent="filterHistory = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterHistory"
name="cancel"
@click.stop.prevent="filterHistory = ''"
class="cursor-pointer"
/>
</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>
<d-table
ref="table"
flat
bordered
dense
:columns="columns"
:rows="rowsHistory"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumns"
:filter="filterHistory"
>
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id">
<div>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card-section>
<q-separator />
<q-card-actions align="right"> </q-card-actions>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -0,0 +1,43 @@
<script setup lang="ts">
import { ref } from "vue";
import DataEmployee from "@/modules/04_registryPerson/components/detail/Employee/01_DataEmployee.vue";
import Employment from "@/modules/04_registryPerson/components/detail/Employee/02_Employment.vue";
const tab = ref<string>("1");
</script>
<template>
<div class="row items-center q-my-md">
<div class="text-dark row items-center q-px-md">
<q-icon name="mdi-account" class="q-mr-md" size="22px" />
<div class="text-subtitle1 text-weight-bold">อมลลกจางชวคราว</div>
</div>
</div>
<q-separator />
<q-tabs
v-model="tab"
active-color="blue-8"
align="left"
bordered
narrow-indicator
indicator-color="transparent"
dense
class="text-grey q-pl-sm"
>
<q-tab name="1" label="ข้อมูลลูกจ้างชั่วคราว" />
<q-tab name="2" label="ข้อมูลการจ้าง" />
</q-tabs>
<q-separator />
<q-tab-panels v-model="tab" animated>
<q-tab-panel name="1">
<DataEmployee />
</q-tab-panel>
<q-tab-panel name="2">
<Employment />
</q-tab-panel>
</q-tab-panels>
</template>
<style scoped></style>

View file

@ -0,0 +1,867 @@
<script setup lang="ts">
import { ref, reactive, watch, onMounted } from "vue";
import { useCounterMixin } from "@/stores/mixin";
import type {
RequestItemsHistoryObject,
FormMain,
} from "@/modules/04_registryPerson/interface/index/government";
import DialogHeader from "@/components/DialogHeader.vue";
import { useQuasar } from "quasar";
import type { QTableProps } from "quasar";
import { useRoute } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
/** ฟังชั่นกลาง */
const route = useRoute();
const $q = useQuasar();
const mixin = useCounterMixin();
const {
date2Thai,
dateToISO,
dialogConfirm,
messageError,
showLoader,
hideLoader,
success,
} = mixin;
const profileId = ref<string>(route.params.id.toString());
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
/** ตัวแปรข้อมูลหลัก */
const formMain = reactive<FormMain>({
ocId: "", //
positionId: "", //
positionLine: "", //
positionLevel: "", //
numberId: "", //
positionExecutive: "", //
positionExecutiveSide: "", //
positionType: "", //
positionPathSide: "", ///
containDate: null, //
workDate: null, //
reasonSameDate: "",
retireDate: null, //
ageAll: {
year: 0,
month: 0,
day: 0,
}, //
absent: 0, //
age: 0, //
});
/** dialog */
const modalEdit = ref<boolean>(false);
const modalHistory = ref<boolean>(false);
const rowsHistory = ref<RequestItemsHistoryObject[]>([]);
const filterKeyword = ref<string>("");
const containDate = ref<Date | null>(null);
const workDate = ref<Date | null>(null);
const reasonSameDate = ref<string | null>(null);
const containDateRef = ref<object | null>(null);
const workDateRef = ref<object | null>(null);
const reasonSameDateRef = ref<object | null>(null);
const visibleColumnsHistory = ref<String[]>([
"oc",
"position",
"positionPathSide",
"posNo",
"positionLine",
"positionType",
"positionLevel",
"positionExecutive",
"positionExecutiveSide",
"dateAppoint",
"dateStart",
"retireDate",
"govAge",
"govAgeAbsent",
"govAgePlus",
"reasonSameDate",
"lastUpdateFullName",
"lastUpdatedAt",
]);
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "oc",
align: "left",
label: "สังกัด",
sortable: true,
field: "oc",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "positionPathSide",
align: "left",
label: "ตำแหน่ง",
sortable: true,
field: "positionPathSide",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "position",
align: "left",
label: "ด้าน/สาขา",
sortable: true,
field: "position",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posNo",
align: "left",
label: "ตำแหน่งเลขที่",
sortable: true,
field: "posNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "positionLine",
align: "left",
label: "สายงาน",
sortable: true,
field: "positionLine",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "positionType",
align: "left",
label: "ประเภท",
sortable: true,
field: "positionType",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "positionLevel",
align: "left",
label: "ระดับ",
sortable: true,
field: "positionLevel",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "positionExecutive",
align: "left",
label: "ตำแหน่งทางการบริหาร",
sortable: true,
field: "positionExecutive",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "positionExecutiveSide",
align: "left",
label: "ด้านทางการบริหาร",
sortable: true,
field: "positionExecutiveSide",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "dateAppoint",
align: "left",
label: "วันที่บรรจุ",
sortable: true,
field: "dateAppoint",
format: (v) => date2Thai(v),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "dateStart",
align: "left",
label: "เริ่มปฎิบัติราชการ",
sortable: true,
field: "dateStart",
format: (v) => date2Thai(v),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "retireDate",
align: "left",
label: "วันเกษียณอายุ",
sortable: true,
field: "retireDate",
format: (v) => date2Thai(v),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "govAge",
align: "left",
label: "อายุราชการ",
sortable: true,
field: "govAge",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "govAgeAbsent",
align: "left",
label: "ขาดราชการ",
sortable: true,
field: "govAgeAbsent",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "govAgePlus",
align: "left",
label: "อายุราชการเกื้อกูล",
sortable: true,
field: "govAgePlus",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reasonSameDate",
align: "left",
label: "เหตุผลกรณีไม่ตรงวัน",
sortable: true,
field: "reasonSameDate",
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: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
format: (v) => date2Thai(v),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
/** เปิด dialog */
function openDialogEdit() {
modalEdit.value = true;
containDate.value = formMain.containDate ? formMain.containDate : null;
workDate.value = formMain.workDate ? formMain.workDate : null;
reasonSameDate.value = formMain.reasonSameDate
? formMain.reasonSameDate
: null;
}
function openDialogHistory() {
modalHistory.value = true;
filterKeyword.value = "";
getDataHistory();
}
/** ปิด dialog */
function closeDialog() {
modalEdit.value = false;
containDate.value = null;
workDate.value = null;
reasonSameDate.value = null;
}
function onSubmit() {
dialogConfirm($q, () => {
showLoader();
http
.patch(
config.API.profileNewGovernmentById(profileId.value, empType.value),
{
dateAppoint: containDate.value,
dateStart: workDate.value,
reasonSameDate:
dateToISO(containDate.value as Date) ===
dateToISO(workDate.value as Date)
? ""
: reasonSameDate.value,
}
)
.then(() => {
getData();
success($q, "บันทึกข้อมูลสำเร็จ");
closeDialog();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
});
}
/** ดึงข้อมูลราชการ */
function getData() {
showLoader();
http
.get(config.API.profileNewGovernmentById(profileId.value, empType.value))
.then((res) => {
const data = res.data.result;
formMain.ocId = data.org ?? "-"; //
formMain.positionId = data.position ?? "-"; //
formMain.positionLine = data.positionField ?? "-"; //
formMain.positionLevel = data.posLevel ?? "-"; //
formMain.numberId = data.posMasterNo ?? "-"; //
formMain.positionType = data.posType ?? "-"; //
formMain.positionExecutive = data.posExecutive ?? "-"; //
formMain.positionPathSide = data.positionArea ?? "-"; //
formMain.positionExecutiveSide = data.positionExecutiveField ?? "-"; //
formMain.containDate = data.dateAppoint;
formMain.workDate = data.dateStart;
formMain.reasonSameDate = data.reasonSameDate;
formMain.retireDate = data.dateLeave;
formMain.dateRetireLaw = data.dateRetireLaw;
formMain.ageAll = data.govAge;
formMain.absent = data.govAgeAbsent;
formMain.age = data.govAgePlus;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
/** ดึงข้อมูลประวัติ */
function getDataHistory() {
showLoader();
http
.get(config.API.profileNewGovernmentHistory(profileId.value, empType.value))
.then((res) => {
let data = res.data.result;
rowsHistory.value = [];
data.map((e: RequestItemsHistoryObject) => {
rowsHistory.value.push({
oc: e.oc,
position: e.position,
positionPathSide: e.positionPathSide,
posNo: e.posNo,
positionLine: e.positionLine,
positionType: e.positionType,
positionLevel: e.positionLevel,
positionExecutive: e.positionExecutive,
positionExecutiveSide: e.positionExecutiveSide,
dateAppoint: new Date(e.dateAppoint),
dateStart: new Date(e.dateStart),
dateRetire: e.dateRetire,
dateRetireLaw: e.dateRetireLaw,
govAge: e.govAge,
govAgeAbsent: e.govAgeAbsent,
govAgePlus: e.govAgePlus,
reasonSameDate: e.reasonSameDate,
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
lastUpdatedAt: e.lastUpdatedAt,
lastUpdateFullName: e.lastUpdateFullName,
});
});
})
.catch((e) => {
messageError($q, e);
modalHistory.value = false;
})
.finally(() => {
hideLoader();
});
}
onMounted(() => {
getData();
});
</script>
<template>
<div class="row">
<q-space />
<div class="q-gutter-x-sm">
<q-btn
color="primary"
icon="mdi-pencil-outline"
flat
dense
round
@click="openDialogEdit()"
><q-tooltip>แกไขขอม</q-tooltip></q-btn
>
<q-btn
color="info"
icon="mdi-history"
flat
dense
round
@click="openDialogHistory()"
><q-tooltip>ประวอมลราชการ</q-tooltip></q-btn
>
</div>
</div>
<q-card bordered class="bg-grey-1 q-pa-md q-mt-sm">
<div class="row q-col-gutter-md">
<div class="col-12 col-sm-6 col-md-6 q-gutter-y-sm">
<div class="row items-center">
<div class="col-12 col-sm-12 col-md-5 text-grey-6 text-weight-medium">
<div>งก</div>
</div>
<div class="col-12 col-sm-12 col-md-7">{{ formMain.ocId }}</div>
</div>
<div class="row items-center">
<div class="col-12 col-sm-12 col-md-5 text-grey-6 text-weight-medium">
<div>{{ empType === "" ? `ตำแหน่งในสายงาน` : `ตำแหน่ง` }}</div>
</div>
<div class="col-12 col-sm-12 col-md-7">{{ formMain.positionId }}</div>
</div>
<div class="row items-center">
<div class="col-12 col-sm-12 col-md-5 text-grey-6 text-weight-medium">
<div>ตำแหนงเลขท</div>
</div>
<div class="col-12 col-sm-12 col-md-7">{{ formMain.numberId }}</div>
</div>
<div class="row items-center" v-if="empType === ''">
<div class="col-12 col-sm-12 col-md-5 text-grey-6 text-weight-medium">
<div>ตำแหนงทางการบรหาร</div>
</div>
<div class="col-12 col-sm-12 col-md-7">
{{ formMain.positionExecutive }}
</div>
</div>
<div class="row items-center" v-if="empType === ''">
<div class="col-12 col-sm-12 col-md-5 text-grey-6 text-weight-medium">
<div>านทางการบรหาร</div>
</div>
<div class="col-12 col-sm-12 col-md-7">
{{ formMain.positionExecutiveSide }}
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-6 q-gutter-y-sm">
<div class="row items-center" v-if="empType === ''">
<div class="col-12 col-sm-12 col-md-5 text-grey-6 text-weight-medium">
<div>สายงาน</div>
</div>
<div class="col-12 col-sm-12 col-md-7">
{{ formMain.positionLine }}
</div>
</div>
<div class="row items-center">
<div class="col-12 col-sm-12 col-md-5 text-grey-6 text-weight-medium">
<div>{{ empType === "" ? "ตำแหน่งประเภท" : "กลุ่มงาน" }}</div>
</div>
<div class="col-12 col-sm-12 col-md-7">
{{ formMain.positionType }}
</div>
</div>
<div class="row items-center">
<div class="col-12 col-sm-12 col-md-5 text-grey-6 text-weight-medium">
<div>{{ empType === "" ? "ระดับ" : "ระดับชั้นงาน" }}</div>
</div>
<div class="col-12 col-sm-12 col-md-7">
{{ formMain.positionLevel }}
</div>
</div>
<div class="row items-center" v-if="empType === ''">
<div class="col-12 col-sm-12 col-md-5 text-grey-6 text-weight-medium">
<div>าน/สาขา</div>
</div>
<div class="col-12 col-sm-12 col-md-7">
{{ formMain.positionPathSide }}
</div>
</div>
</div>
</div>
<q-separator color="grey-4" class="q-my-md" />
<div class="row q-col-gutter-md">
<div class="col-12 col-sm-6 col-md-6 q-gutter-y-sm">
<div class="col-12 col-sm-6 col-md-6">
<div class="row">
<div class="col-12 col-sm-12 col-md-5">
<span class="text-grey-6 text-weight-medium"
>นทงบรรจ</span
>
</div>
<div class="col-12 col-sm-12 col-md-7">
<span>{{
formMain.containDate ? date2Thai(formMain.containDate) : "-"
}}</span>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-6">
<div class="row">
<div class="col-12 col-sm-12 col-md-5">
<span class="text-grey-6 text-weight-medium"
>นทเรมปฏราชการ</span
>
</div>
<div class="col-12 col-sm-12 col-md-7">
<span>{{
formMain.workDate !== null
? date2Thai(formMain.workDate as Date)
: "-"
}}</span>
</div>
</div>
</div>
<div
v-if="dateToISO(formMain.containDate as Date) !== dateToISO(formMain.workDate as Date)"
class="col-12 col-sm-6 col-md-6"
>
<div class="row">
<div class="col-12 col-sm-12 col-md-5">
<span class="text-grey-6 text-weight-medium"
>เหตผลทนทไมตรงก</span
>
</div>
<div class="col-12 col-sm-12 col-md-7">
<span>{{
formMain.reasonSameDate !== "" ? formMain.reasonSameDate : "-"
}}</span>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-6">
<div class="row">
<div class="col-12 col-sm-12 col-md-5">
<span class="text-grey-6 text-weight-medium"
>นครบเกษยณอาย</span
>
</div>
<div class="col-12 col-sm-12 col-md-7">
<span>{{
formMain.retireDate
? date2Thai(formMain.retireDate as Date)
: "-"
}}</span>
</div>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-6 q-gutter-y-sm">
<div class="col-12 col-sm-6 col-md-6">
<div class="row">
<div class="col-12 col-sm-12 col-md-5">
<span class="text-grey-6 text-weight-medium">อายราชการ</span>
</div>
<div class="col-12 col-sm-12 col-md-7">
<span>{{
formMain.ageAll
? `${formMain.ageAll.day} วัน ${formMain.ageAll.month} เดือน ${formMain.ageAll.year} ปี`
: "-"
}}</span>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-6">
<div class="row">
<div class="col-12 col-sm-12 col-md-5">
<span class="text-grey-6 text-weight-medium">ขาดราชการ</span>
</div>
<div class="col-12 col-sm-12 col-md-7">
<span>{{ formMain.absent ? formMain.absent : 0 }}</span>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-6">
<div class="row">
<div class="col-12 col-sm-12 col-md-5">
<span class="text-grey-6 text-weight-medium"
>อายราชการเกอก</span
>
</div>
<div class="col-12 col-sm-12 col-md-7">
<span>{{ formMain.age ? formMain.age : 0 }}</span>
</div>
</div>
</div>
<div class="col-12 col-sm-6 col-md-6">
<div class="row">
<div class="col-12 col-sm-12 col-md-5">
<span class="text-grey-6 text-weight-medium"
>นทเกษยณอายราชการตามกฏหมาย</span
>
</div>
<div class="col-12 col-sm-12 col-md-7">
<span>{{
formMain.dateRetireLaw
? date2Thai(formMain.dateRetireLaw as Date)
: "-"
}}</span>
</div>
</div>
</div>
</div>
</div>
</q-card>
<!-- dialog edit -->
<q-dialog v-model="modalEdit" persistent>
<q-card>
<q-form @submit.prevent greedy @validation-success="onSubmit">
<DialogHeader tittle="แก้ไขข้อมูลราชการ" :close="closeDialog" />
<q-separator color="grey-4" />
<q-card-section>
<div class="row q-col-gutter-sm">
<div class="col-xs-12 col-md-6">
<datepicker
v-model="containDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
ref="containDateRef"
class="full-width inputgreen cursor-pointer"
hide-bottom-space
dense
outlined
:model-value="containDate !== null
? date2Thai(containDate as Date)
: null
"
:rules="[
(val) => !!val || 'กรุณาเลือก วัน/เดือน/ปี ที่บรรจุ',
]"
label="วัน/เดือน/ปี ที่บรรจุ"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="'color: var(--q-primary)'"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-12 col-md-6">
<datepicker
v-model="workDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
ref="workDateRef"
class="full-width inputgreen cursor-pointer"
hide-bottom-space
dense
outlined
:model-value="workDate !== null
? date2Thai(workDate as Date)
: null
"
:rules="[(val) => !!val || 'กรุณาเลือกเริ่มปฎิบัติราชการ']"
label="วัน/เดือน/ปี เริ่มปฎิบัติราชการ"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="'color: var(--q-primary)'"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div
class="col-12"
v-if="containDate && workDate ? dateToISO(containDate as Date) !== dateToISO(workDate as Date) : false"
>
<q-input
ref="reasonSameDateRef"
class="full-width inputgreen cursor-pointer"
label="เหตุผลกรณีไม่ตรงกัน"
type="textarea"
outlined
dense
hide-bottom-space
:rules="[(val) => !!val || 'กรุณากรอก เหตุผลกรณีไม่ตรงกัน']"
v-model="reasonSameDate"
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn label="บันทึก" id="onSubmit" type="submit" color="public">
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<!-- dialog History -->
<q-dialog v-model="modalHistory" persistent>
<q-card style="min-width: 80%">
<DialogHeader
tittle="ประวัติแก้ไขข้อมูลราชการ"
:close="() => (modalHistory = !modalHistory)"
/>
<q-separator color="grey-4" />
<q-card-section style="max-height: 50vh" class="scroll">
<div class="row q-pb-sm q-gutter-x-sm">
<q-space />
<q-input
standout
dense
v-model="filterKeyword"
ref="filterRef"
outlined
placeholder="ค้นหา"
class="col-2"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterKeyword == ''"
name="search"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterKeyword"
name="cancel"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
</template>
</q-input>
<q-select
v-model="visibleColumnsHistory"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columnsHistory"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
<d-table
ref="table"
:columns="columnsHistory"
:rows="rowsHistory"
flat
bordered
:paging="true"
dense
class="custom-header-table"
:filter="filterKeyword"
:visible-columns="visibleColumnsHistory"
>
<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>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card-section>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -0,0 +1,743 @@
<script setup lang="ts">
import { ref, onMounted, reactive } from "vue";
import type { QTableProps } from "quasar";
import type {
RequestItemsObject,
FormFilter,
DataOption,
DisciplineOps,
} from "@/modules/04_registryPerson/interface/index/discipline";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
import DialogHeader from "@/components/DialogHeader.vue";
import http from "@/plugins/http";
import config from "@/app.config";
import DialogHistory from "@/modules/04_registryPerson/components/detail/GovernmentInformation/02_DisciplineHistory.vue";
import { useRoute } from "vue-router";
const route = useRoute();
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const $q = useQuasar();
const mixin = useCounterMixin();
const {
date2Thai,
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
dialogRemove,
} = mixin;
const disciplineData = reactive<RequestItemsObject>({
date: null,
level: "",
detail: "",
unStigma: "",
refCommandNo: "",
profileId: profileId.value,
refCommandDate: null,
});
const rows = ref<RequestItemsObject[]>([]);
const mode = ref<string>("table");
const filterKeyword = ref<string>("");
const formFilter = reactive<FormFilter>({
page: 1,
pageSize: 12,
keyword: "",
type: "",
posType: "",
posLevel: "",
retireYear: "",
rangeYear: { min: 0, max: 60 },
isShowRetire: false,
isProbation: false,
});
const currentPage = ref<number>(1);
const maxPage = ref<number>(1);
const pagination = ref({
page: 1,
rowsPerPage: 10,
});
const visibleColumns = ref<String[]>([
"level",
"detail",
"unStigma",
"refCommandNo",
"refCommandDate",
"date",
]);
const columns = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วัน เดือน ปี",
sortable: true,
field: "date",
format: (v) => date2Thai(v),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "level",
align: "left",
label: "ระดับการลงโทษทางวินัย",
sortable: true,
field: "level",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "unStigma",
align: "left",
label: "ประเภทคำสั่ง",
sortable: true,
field: "unStigma",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
format: (v) => date2Thai(v),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const dateRef = ref<object | null>(null);
const detailRef = ref<object | null>(null);
const refCommandNoRef = ref<object | null>(null);
/** dialog */
const edit = ref<boolean>(false);
const modal = ref<boolean>(false);
const modalHistory = ref<boolean>(false);
const id = ref<string>("");
const Ops = ref<any>({
levelOptions: [
{ id: "0", name: "ไม่ร้ายแรง", disable: true },
{ id: "ภาคทัณฑ์", name: "ภาคทัณฑ์", disable: false },
{
id: "ตัดเงินเดือน",
name: "ตัดเงินเดือน",
disable: false,
},
{
id: "ลดขั้นเงินเดือน",
name: "ลดขั้นเงินเดือน",
disable: false,
},
{ id: "4", name: "ร้ายแรง", disable: true },
{ id: "ปลดออก", name: "ปลดออก", disable: false },
{ id: "ไล่ออก", name: "ไล่ออก", disable: false },
{ id: "อื่นๆ", name: "อื่นๆ", disable: false },
],
});
const OpsFilter = ref<any>({
levelOptions: [
{ id: "0", name: "ไม่ร้ายแรง", disable: true },
{ id: "ภาคทัณฑ์", name: "ภาคทัณฑ์", disable: false },
{
id: "ตัดเงินเดือน",
name: "ตัดเงินเดือน",
disable: false,
},
{
id: "ลดขั้นเงินเดือน",
name: "ลดขั้นเงินเดือน",
disable: false,
},
{ id: "4", name: "ร้ายแรง", disable: true },
{ id: "ปลดออก", name: "ปลดออก", disable: false },
{ id: "ไล่ออก", name: "ไล่ออก", disable: false },
{ id: "อื่นๆ", name: "อื่นๆ", disable: false },
],
});
function filterSelector(val: string, update: Function, refData: string) {
switch (refData) {
case "levelOptions":
update(() => {
Ops.value.levelOptions = OpsFilter.value.levelOptions.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
default:
break;
}
}
/** ปิด dialog */
function closeDialog() {
modal.value = false;
edit.value = false;
disciplineData.date = null;
disciplineData.detail = "";
disciplineData.level = "";
disciplineData.unStigma = "";
disciplineData.refCommandNo = "";
disciplineData.refCommandDate = null;
}
function openDialogAdd() {
modal.value = true;
}
async function fetchData(id: string) {
showLoader();
await http
.get(config.API.profileNewDisciplineByProfileId(id, empType.value))
.then(async (res) => {
rows.value = res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
const body = {
date: disciplineData.date,
level: disciplineData.level,
detail: disciplineData.detail,
unStigma: disciplineData.unStigma,
refCommandNo: disciplineData.refCommandNo,
profileId: empType.value === "" ? disciplineData.profileId : undefined,
profileEmployeeId:
empType.value !== "" ? disciplineData.profileId : undefined,
refCommandDate: disciplineData.refCommandDate,
};
await http
.post(config.API.profileNewDiscipline(empType.value), body)
.then(() => {
fetchData(profileId.value);
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(idData: string) {
await http
.patch(
config.API.profileNewDisciplineByDisciplineId(idData, empType.value),
{
...disciplineData,
profileId: undefined,
}
)
.then(() => {
fetchData(profileId.value);
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
// function deleteData(idData: string) {
// dialogRemove($q, () =>
// http
// .delete(config.API.profileNewDisciplineByDisciplineId(idData))
// .then(() => {
// fetchData(profileId.value);
// success($q, "");
// })
// .catch((err) => {
// messageError($q, err);
// })
// .finally(() => {
// hideLoader();
// })
// );
// }
/**
* กดเลอกขอมลทจะแกไข
* @param props props ใน row เลอก
*/
function openDialogEdit(props: RequestItemsObject) {
modal.value = true;
edit.value = true;
id.value = props.id ? props.id : "";
disciplineData.date = props.date;
disciplineData.detail = props.detail;
disciplineData.level = props.level;
disciplineData.unStigma = props.unStigma;
disciplineData.refCommandNo = props.refCommandNo;
disciplineData.refCommandDate = props.refCommandDate;
}
function openDialogHistory(idOrder: string) {
modalHistory.value = true;
id.value = idOrder;
}
async function onSubmit() {
dialogConfirm(
$q,
async () => {
edit.value ? editData(id.value) : addData();
closeDialog();
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
onMounted(async () => {
await fetchData(profileId.value);
});
</script>
<template>
<div class="row items-center q-gutter-x-sm q-pb-sm">
<q-btn dense color="primary" icon="add" flat round @click="openDialogAdd()"
><q-tooltip>เพมขอม</q-tooltip></q-btn
>
<q-space />
<q-input
standout
dense
v-model="filterKeyword"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterKeyword == ''"
name="search"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterKeyword"
name="cancel"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
</template>
</q-input>
<q-select
v-if="mode == 'table'"
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-btn-toggle
v-model="mode"
dense
class="no-shadow toggle-borderd"
toggle-color="grey-4"
:options="[
{ value: 'table', slot: 'table' },
{ value: 'card', slot: 'card' },
]"
>
<template v-slot:table>
<q-icon
name="format_list_bulleted"
size="24px"
:style="{
color: mode === 'table' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
<template v-slot:card>
<q-icon
name="mdi-view-grid-outline"
size="24px"
:style="{
color: mode === 'card' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
</q-btn-toggle>
</div>
<d-table
:card-container-class="mode === 'card' ? 'q-col-gutter-md' : ''"
:grid="mode === 'card'"
ref="table"
row-key="id"
flat
bordered
dense
:columns="columns"
:rows="rows"
:paging="true"
:filter="filterKeyword"
v-model:pagination="pagination"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumns"
>
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<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" v-if="mode === 'table'">
<q-tr :props="props" class="cursor-pointer">
<q-td auto-width>
<q-btn
color="primary"
flat
dense
round
class="q-mr-xs"
size="14px"
icon="mdi-pencil-outline"
clickable
@click="openDialogEdit(props.row)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="openDialogHistory(props.row.id)"
>
<q-tooltip>ประวแกไขว</q-tooltip>
</q-btn>
<!-- <q-btn
color="red"
flat
dense
round
size="14px"
icon="mdi-delete"
clickable
@click.stop="deleteData(props.row.id)"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn> -->
</q-td>
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name === 'no'">
{{
(formFilter.page - 1) * formFilter.pageSize + props.rowIndex + 1
}}
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
<template v-slot:item="props" v-else>
<div class="col-xs-12 col-sm-6 col-md-6">
<q-card flat bordered class="q-pa-none">
<div class="row bg-grey-3">
<q-space />
<div class="q-gutter-x-sm">
<q-btn
color="primary"
icon="mdi-pencil-outline"
flat
round
@click="openDialogEdit(props.row)"
><q-tooltip>แกไข</q-tooltip></q-btn
>
<q-btn
color="info"
icon="mdi-history"
flat
round
@click="openDialogHistory(props.row.id)"
><q-tooltip>ประวแกไขว</q-tooltip></q-btn
>
</div>
</div>
<q-separator />
<q-card-section class="q-pa-none">
<div class="row q-pa-sm">
<div class="col-3 text-grey-6 text-weight-medium">
ระดบการลงโทษทางว
</div>
<div class="col-3">
{{ props.row.level !== "" ? props.row.level : "-" }}
</div>
<div class="col-3 text-grey-6 text-weight-medium">
/เดอน/
</div>
<div class="col-3">
{{ props.row.date ? date2Thai(props.row.date) : "-" }}
</div>
</div>
<q-separator />
<div class="row q-pa-sm bg-grey-2">
<div class="col-3 text-grey-6 text-weight-medium">างมลท</div>
<div class="col-3">
{{ props.row.unStigma !== "" ? props.row.unStigma : "-" }}
</div>
<div class="col-3 text-grey-6 text-weight-medium">
เลขทคำส
</div>
<div class="col-3">
{{
props.row.refCommandNo !== "" ? props.row.refCommandNo : "-"
}}
</div>
</div>
<q-separator />
<div class="row q-pa-sm">
<div class="col-3 text-grey-6 text-weight-medium">
เอกสารอางอ (ลงวนท)
</div>
<div class="col-3">
{{
props.row.refCommandDate
? date2Thai(props.row.refCommandDate)
: "-"
}}
</div>
</div>
<q-separator />
<div class="row q-pa-sm bg-grey-2">
<div class="col-3 text-grey-6 text-weight-medium">รายละเอยด</div>
<div class="col-9">
{{ props.row.detail !== "" ? props.row.detail : "-" }}
</div>
</div>
</q-card-section>
</q-card>
</div>
</template>
</d-table>
<!-- dialog add edit -->
<q-dialog v-model="modal" persistent>
<q-card>
<q-form @submit.prevent greedy @validation-success="onSubmit()">
<DialogHeader
:tittle="edit ? 'แก้ไขข้อมูลวินัย' : 'เพิ่มข้อมูลวินัย'"
:close="closeDialog"
/>
<q-separator color="grey-4" />
<q-card-section style="max-height: 50vh" class="scroll">
<div class="row col-12 q-col-gutter-x-xs q-col-gutter-y-xs">
<div class="col-xs-12 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
v-model="disciplineData.date"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
ref="dateRef"
class="full-width inputgreen cursor-pointer"
outlined
dense
:model-value="date2Thai(disciplineData.date)"
:rules="[(val:string) => !!val || `${'กรุณาเลือก วัน/เดือน/ปี'}`]"
hide-bottom-space
:label="`${'วัน/เดือน/ปี'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-12 col-sm-6 col-md-6">
<q-input
ref="detailRef"
class="full-width inputgreen cursor-pointer"
outlined
dense
lazy-rules
v-model="disciplineData.detail"
:rules="[(val:string) => !!val || `${'กรุณากรอกรายละเอียด'}`]"
hide-bottom-space
:label="`${'รายละเอียด'}`"
/>
</div>
<div class="col-xs-12 col-sm-6 col-md-6">
<selector
class="full-width inputgreen cursor-pointer"
outlined
dense
lazy-rules
v-model="disciplineData.level"
:label="`${'ระดับการลงโทษทางวินัย'}`"
emit-value
map-options
option-label="name"
:options="Ops.levelOptions"
option-value="id"
use-input
clearable
hide-bottom-space
input-debounce="0"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'levelOptions'
) "
/>
</div>
<div class="col-xs-12 col-sm-6 col-md-6">
<q-input
class="full-width inputgreen cursor-pointer"
outlined
dense
v-model="disciplineData.unStigma"
hide-bottom-space
:label="`${'ประเภทคำสั่ง'}`"
/>
</div>
<div class="col-xs-12 col-sm-6 col-md-6">
<q-input
ref="refCommandNoRef"
class="full-width inputgreen cursor-pointer"
outlined
dense
lazy-rules
v-model="disciplineData.refCommandNo"
hide-bottom-space
:label="`${'เลขที่คำสั่ง'}`"
:rules="[(val:string) => !!val || `${'กรุณากรอกเลขที่คำสั่ง'}`]"
>
<template v-slot:append>
<q-icon name="mdi-file" class="cursor-pointer" />
</template>
</q-input>
</div>
<div class="col-xs-12 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
v-model="disciplineData.refCommandDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
ref="dateRef"
class="full-width inputgreen cursor-pointer"
outlined
clearable
dense
:model-value="date2Thai(disciplineData.refCommandDate)"
hide-bottom-space
:label="`${'เอกสารอ้างอิง (ลงวันที่)'}`"
@clear="disciplineData.refCommandDate = null"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn label="บันทึก" id="onSubmit" type="submit" color="public">
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<DialogHistory v-model:modal="modalHistory" v-model:id="id" />
</template>
<style scoped></style>

View file

@ -0,0 +1,282 @@
<script setup lang="ts">
import { ref, watch, reactive } from "vue";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar, type QTableProps } from "quasar";
import { useRoute } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
import type {
RequestItemsObject,
FormFilter,
} from "@/modules/04_registryPerson/interface/index/discipline";
const modal = defineModel<boolean>("modal", { required: true });
const id = defineModel<string>("id", { required: true });
const $q = useQuasar();
const route = useRoute();
const mixin = useCounterMixin();
const { showLoader, hideLoader, messageError, date2Thai } = mixin;
const currentPage = ref<number>(1);
const maxPage = ref<number>(1);
const filterKeyword = ref<string>("");
const rows = ref<RequestItemsObject[]>([]); //select data history
const formFilter = reactive<FormFilter>({
page: 1,
pageSize: 12,
keyword: "",
type: "",
posType: "",
posLevel: "",
retireYear: "",
rangeYear: { min: 0, max: 60 },
isShowRetire: false,
isProbation: false,
});
const historyPagination = ref({
page: 1,
rowsPerPage: 10,
});
const visibleColumns = ref<String[]>([
"level",
"detail",
"unStigma",
"refCommandNo",
"refCommandDate",
"date",
"createdFullName",
"createdAt",
"lastUpdateFullName",
"lastUpdatedAt",
]);
const columns = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วัน เดือน ปี",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "level",
align: "left",
label: "ระดับการลงโทษทางวินัย",
sortable: true,
field: "level",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "unStigma",
align: "left",
label: "ล้างมลทิน",
sortable: true,
field: "unStigma",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
format: (v) => date2Thai(v),
field: "refCommandDate",
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: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
format: (v) => date2Thai(v),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
function getHistory() {
showLoader();
http
.get(
config.API.profileNewDisciplineHisByDisciplineId(id.value, empType.value)
)
.then((res) => {
let data = res.data.result;
rows.value = [];
data.map((e: RequestItemsObject) => {
rows.value.push({
...e,
id: e.id,
level: e.level,
detail: e.detail,
unStigma: e.unStigma,
refCommandNo: e.refCommandNo,
refCommandDate:
e.refCommandDate == null ? null : new Date(e.refCommandDate),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
watch(modal, (status) => {
if (status == true) {
getHistory();
filterKeyword.value = "";
} else {
filterKeyword.value = "";
}
});
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="min-width: 80%">
<DialogHeader tittle="ประวัติแก้ไขวินัย" :close="() => (modal = false)" />
<q-separator color="grey-4" />
<q-card-section style="max-height: 60vh" class="scroll">
<div class="row q-gutter-sm q-mb-sm">
<q-space />
<q-input
standout
dense
v-model="filterKeyword"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterKeyword == ''"
name="search"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterKeyword"
name="cancel"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
</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>
<d-table
ref="table"
flat
bordered
dense
:columns="columns"
:rows="rows"
:paging="true"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumns"
v-model:pagination="historyPagination"
:filter="filterKeyword"
>
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name === 'no'">
{{
(formFilter.page - 1) * formFilter.pageSize +
props.rowIndex +
1
}}
</div>
<div v-else>
{{ 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,815 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { useRoute } from "vue-router";
import { useQuasar, type QTableProps } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import DialogHeader from "@/components/DialogHeader.vue";
import type {
DetailData,
FormFilter,
DataOptionLeave,
DataOption,
ResponseTotalObject,
MyObjectRef,
} from "@/modules/04_registryPerson/interface/index/leave";
import DialogHistory from "@/modules/04_registryPerson/components/detail/GovernmentInformation/03_LeaveHistory.vue";
import http from "@/plugins/http";
import config from "@/app.config";
const rowsTotal = ref<ResponseTotalObject[]>([]);
const id = ref<string>("");
const route = useRoute();
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const $q = useQuasar();
const mixin = useCounterMixin();
const {
dialogConfirm,
messageError,
showLoader,
hideLoader,
success,
date2Thai,
dateToISO,
} = mixin;
const pagination = ref({
page: 1,
rowsPerPage: 10,
});
const mode = ref<string>("table");
const filterKeyword = ref<string>("");
const currentPage = ref<number>(1);
const maxPage = ref<number>(1);
const rows = ref<DetailData[]>([]);
const formFilter = reactive<FormFilter>({
page: 1,
pageSize: 12,
keyword: "",
type: "",
posType: "",
posLevel: "",
retireYear: "",
rangeYear: { min: 0, max: 60 },
isShowRetire: false,
isProbation: false,
});
const modal = ref<boolean>(false);
const edit = ref<boolean>(false);
const modalHistory = ref<boolean>(false);
const reason = ref<string>(""); //
const numLeave = ref<number>(1);
const dateRange = ref<[Date, Date]>([new Date(), new Date()]);
const numUsedLeave = ref<number>(0);
const typeLeave = ref<any>();
const typeLeaveOption = ref<DataOptionLeave[]>([]);
const typeLeaveOptionFilter = ref<DataOptionLeave[]>([]);
const statLeave = ref<string>("");
const statLeaveOption = ref<DataOption[]>([
{ id: "approve", name: "ผ่านการอนุมัติ" },
{ id: "reject", name: "ไม่ผ่านการอนุมัติ" },
{ id: "cancel", name: "ยกเลิก" },
{ id: "waitting", name: "รออนุมัติ" },
]);
const statLeaveOptionFilter = ref<DataOption[]>([
{ id: "approve", name: "ผ่านการอนุมัติ" },
{ id: "reject", name: "ไม่ผ่านการอนุมัติ" },
{ id: "cancel", name: "ยกเลิก" },
{ id: "waitting", name: "รออนุมัติ" },
]);
const statusLeave = (val: string) => {
switch (val) {
case "waitting":
return "รออนุมัติ";
case "reject":
return "ไม่ผ่านการอนุมัติ";
case "approve":
return "ผ่านการอนุมัติ";
case "cancel":
return "ยกเลิก";
default:
return "-";
}
};
const clickEditRowType = () => {
const filter = typeLeaveOptionFilter.value.filter(
(v: DataOptionLeave) => v.id == typeLeave.value
);
if (filter.length > 0) {
numUsedLeave.value = filter[0].totalLeave;
}
};
const typeLeaveRef = ref<object | null>(null);
const dateRangeRef = ref<object | null>(null);
const numLeaveRef = ref<object | null>(null);
const statLeaveRef = ref<object | null>(null);
const reasonRef = ref<object | null>(null);
const objectRef: MyObjectRef = {
typeLeave: typeLeaveRef,
dateRange: dateRangeRef,
numLeave: numLeaveRef,
statLeave: statLeaveRef,
reason: reasonRef,
};
const visibleColumns = ref<String[]>([
"no",
"typeLeave",
"dateLeave",
"numLeave",
"status",
"reason",
]);
const columns = ref<QTableProps["columns"]>([
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: false,
field: "no",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "typeLeave",
align: "left",
label: "ประเภทการลา",
sortable: true,
field: "typeLeave",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "dateLeave",
align: "left",
label: "วัน เดือน ปี ที่ลา",
sortable: true,
field: "dateLeave",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "numLeave",
align: "left",
label: "จำนวนวันลา",
sortable: true,
field: "numLeave",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "status",
align: "left",
label: "สถานะ",
sortable: true,
field: "status",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reason",
align: "left",
label: "เหตุผล",
sortable: true,
field: "reason",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
function openDialogAdd() {
modal.value = true;
edit.value = false;
showLoader();
http
.get(config.API.profileNewLeaveType())
.then((res) => {
const dataOp = res.data.result.map((item: any) => ({
id: item.id,
name: item.name,
code: item.code,
}));
typeLeaveOption.value = dataOp;
typeLeaveOptionFilter.value = dataOp;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
/**
* งชนดอมลประวแกไขขอมลทเลอก
* @param row อม row ประวการแกไข
*/
function clickTotal() {
rowsTotal.value = [];
showLoader();
http
.get(config.API.profileNewLeaveType())
.then((res) => {
const dataOp = res.data.result.map((item: DataOption) => ({
id: item.id,
name: item.name,
}));
typeLeaveOption.value = dataOp;
typeLeaveOptionFilter.value = dataOp;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
function openDialogEdit(props: DetailData) {
edit.value = true;
modal.value = true;
id.value = props.id;
typeLeave.value = {
id: props.typeLeaveId,
name: props.typeLeave,
code: props.code,
};
statLeave.value = props.status;
reason.value = props.reason;
dateRange.value = [
new Date(props.dateStartLeave as Date),
new Date(props.dateEndLeave as Date),
];
numLeave.value = props.numLeave;
clickTotal();
if (rowsTotal.value.length > 0) {
let data: DataOptionLeave[] = [];
rowsTotal.value.map((e: ResponseTotalObject) => {
data.push({
id: e.typeLeaveId,
name: e.typeLeave,
totalLeave: e.totalLeave,
});
});
typeLeaveOption.value = data;
typeLeaveOptionFilter.value = data;
}
}
/** ปิด dialog */
function closeDialog() {
modal.value = false;
edit.value = false;
id.value = "";
typeLeave.value = "";
statLeave.value = "";
reason.value = "";
dateRange.value = [new Date(), new Date()];
numLeave.value = 1;
numUsedLeave.value = 0;
}
function filterSelector(val: any, update: Function, filtername: string) {
switch (filtername) {
case "typeLeaveOption":
update(() => {
typeLeaveOption.value = typeLeaveOptionFilter.value.filter(
(v: DataOptionLeave) =>
v.name.toLowerCase().indexOf(val.toLowerCase()) > -1
);
});
break;
case "statLeaveOption":
update(() => {
statLeaveOption.value = statLeaveOptionFilter.value.filter(
(v: DataOption) =>
v.name.toLowerCase().indexOf(val.toLowerCase()) > -1
);
});
break;
default:
break;
}
}
/**
* แปลงชวงวนทา2คาเปนวนเดยวกนจะโชววนเดยวแตาไมเทากนจะแสดงเปนชวง
* @param val วงวนท
*/
function 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])} `;
}
}
/**
* งชนดอมลประวแกไขขอมลทเลอก
* @param row อม row ประวการแกไข
*/
function openDialogHistory(idOrder: string) {
modalHistory.value = true;
id.value = idOrder;
}
/** validate check*/
function validateForm() {
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, async () => {
if (edit.value == false) {
saveData();
} else {
editData();
}
});
}
}
/**
* นทกเพมขอม
*/
function saveData() {
showLoader();
http
.post(config.API.profileNewLeave(empType.value), {
leaveTypeId: typeLeave.value.id,
dateLeaveStart: dateToISO(dateRange.value[0]),
dateLeaveEnd: dateToISO(dateRange.value[1]),
leaveDays: numLeave.value,
leaveCount: 0,
totalLeave: 0,
status: statLeave.value,
reason: reason.value,
profileId: empType.value === "" ? profileId.value : undefined,
profileEmployeeId: empType.value !== "" ? profileId.value : undefined,
})
.then(() => {
success($q, "บันทึกข้อมูลสำเร็จ");
closeDialog();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
getData();
});
}
/**
* นทกแกไขขอม
*/
const editData = async () => {
showLoader();
http
.patch(config.API.profileNewLeaveById(id.value, empType.value), {
leaveTypeId: typeLeave.value.id,
dateLeaveStart: dateToISO(dateRange.value[0]),
dateLeaveEnd: dateToISO(dateRange.value[1]),
leaveDays: numLeave.value,
leaveCount: 0,
totalLeave: 0,
status: statLeave.value,
reason: reason.value,
})
.then(() => {
success($q, "บันทึกข้อมูลสำเร็จ");
closeDialog();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
getData();
});
};
function getData() {
showLoader();
http
.get(config.API.profileNewLeaveById(profileId.value, empType.value))
.then((res) => {
console.log(res.data.result);
const data = res.data.result;
rows.value = data.map((item: any) => ({
id: item.id,
typeLeave: item.leaveType.name,
code: item.leaveType.refCommandDate,
dateStartLeave: item.dateLeaveStart,
dateEndLeave: item.dateLeaveEnd,
numLeave: item.leaveDays,
status: item.status,
reason: item.reason,
typeLeaveId: item.leaveTypeId,
}));
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
onMounted(() => {
getData();
});
</script>
<template>
<div class="row items-center q-gutter-x-sm q-pb-sm">
<q-btn dense color="primary" icon="add" flat round @click="openDialogAdd()"
><q-tooltip>เพมขอม</q-tooltip></q-btn
>
<q-space />
<q-input
standout
dense
v-model="filterKeyword"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterKeyword == ''"
name="search"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterKeyword"
name="cancel"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
</template>
</q-input>
<q-select
v-if="mode == 'table'"
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-btn-toggle
v-model="mode"
dense
class="no-shadow toggle-borderd"
toggle-color="grey-4"
:options="[
{ value: 'table', slot: 'table' },
{ value: 'card', slot: 'card' },
]"
>
<template v-slot:table>
<q-icon
name="format_list_bulleted"
size="24px"
:style="{
color: mode === 'table' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
<template v-slot:card>
<q-icon
name="mdi-view-grid-outline"
size="24px"
:style="{
color: mode === 'card' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
</q-btn-toggle>
</div>
<d-table
ref="table"
flat
bordered
dense
:card-container-class="mode === 'card' ? 'q-col-gutter-md' : ''"
:columns="columns"
:rows="rows"
:grid="mode === 'card'"
:paging="true"
v-model:pagination="pagination"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumns"
:filter="filterKeyword"
>
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<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" v-if="mode === 'table'">
<q-tr :props="props" class="cursor-pointer">
<q-td auto-width>
<q-btn
color="primary"
flat
dense
round
class="q-mr-xs"
size="14px"
icon="mdi-pencil-outline"
clickable
@click="openDialogEdit(props.row)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="openDialogHistory(props.row.id)"
>
<q-tooltip>ประวแกไขการลา</q-tooltip>
</q-btn>
</q-td>
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name === 'no'">
{{
(formFilter.page - 1) * formFilter.pageSize + props.rowIndex + 1
}}
</div>
<div v-else-if="col.name == 'dateLeave'">
{{
dateThaiRange([props.row.dateStartLeave, props.row.dateEndLeave])
}}
</div>
<div v-else-if="col.name == 'status'">
{{ statusLeave(col.value) }}
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
<template v-slot:item="props" v-else>
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-6">
<q-card flat bordered class="q-pa-none">
<div class="row bg-grey-3">
<q-space />
<div class="q-gutter-x-sm">
<q-btn
color="primary"
icon="mdi-pencil-outline"
flat
round
@click="openDialogEdit(props.row)"
><q-tooltip>แกไขขอม</q-tooltip></q-btn
>
<q-btn
color="info"
icon="mdi-history"
flat
round
@click="openDialogHistory(props.row.id)"
><q-tooltip>ประวแกไขการลา</q-tooltip></q-btn
>
</div>
</div>
<q-separator />
<q-card-section class="q-pa-none">
<div class="row q-pa-sm">
<div class="col-3 text-grey-6 text-weight-medium">
ประเภทการลา
</div>
<div class="col-9">
{{ props.row.typeLeave !== "" ? props.row.typeLeave : "-" }}
</div>
</div>
<q-separator />
<div class="row q-pa-sm bg-grey-2">
<div class="col-3 text-grey-6 text-weight-medium">
/เดอน/ ลา
</div>
<div class="col-3">
{{
props.row.dateStartLeave
? date2Thai(props.row.dateStartLeave)
: "-"
}}
</div>
<div class="col-3 text-grey-6 text-weight-medium">จำนวนวนลา</div>
<div class="col-3">
{{ props.row.numLeave ? props.row.numLeave : "-" }}
</div>
</div>
<q-separator />
<div class="row q-pa-sm bg-grey-2">
<div class="col-3 text-grey-6 text-weight-medium">สถานะ</div>
<div class="col-3">
{{ props.row.status ? statusLeave(props.row.status) : "-" }}
</div>
<div class="col-3 text-grey-6 text-weight-medium">เหตผล</div>
<div class="col-3">
{{ props.row.reason !== "" ? props.row.reason : "-" }}
</div>
</div>
</q-card-section>
</q-card>
</div>
</template>
</d-table>
<!-- dialog add edit -->
<q-dialog v-model="modal" persistent>
<q-card>
<form @submit.prevent="validateForm">
<DialogHeader
:tittle="edit ? 'แก้ไขข้อมูลการลา' : 'เพิ่มข้อมูลการลา'"
:close="closeDialog"
/>
<q-separator color="grey-4" />
<q-card-section>
<div class="row col-12 q-col-gutter-x-xs q-col-gutter-y-xs">
<div class="col-xs-6 col-sm-6 col-md-6">
<q-select
ref="typeLeaveRef"
class="full-width inputgreen cursor-pointer"
outlined
dense
lazy-rules
v-model="typeLeave"
:rules="[(val:string) => !!val || `${'กรุณาเลือกประเภทการลา'}`]"
hide-bottom-space
:label="`${'ประเภทการลา'}`"
@update:modelValue="clickEditRowType"
map-options
option-label="name"
:options="typeLeaveOption"
option-value="id"
use-input
input-debounce="0"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'typeLeaveOption'
) "
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
:readonly="!typeLeave"
menu-class-name="modalfix"
v-model="dateRange"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
range
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
:readonly="!typeLeave"
class="full-width inputgreen cursor-pointer"
outlined
dense
ref="dateRangeRef"
:model-value="dateThaiRange(dateRange)"
:rules="[(val:string) => !!val || `${'กรุณาเลือกวัน เดือน ปีที่ลา'}`]"
hide-bottom-space
:label="`${'วัน เดือน ปีที่ลา'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
ref="numLeaveRef"
class="full-width inputgreen cursor-pointer"
outlined
dense
lazy-rules
v-model="numLeave"
type="number"
:rules="[(val:string) => !!val || `${'กรุณากรอกจำนวนวันที่ลา'}`]"
hide-bottom-space
:label="`${'จำนวนวันที่ลา'}`"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-select
ref="statLeaveRef"
class="full-width inputgreen cursor-pointer"
outlined
dense
lazy-rules
v-model="statLeave"
:rules="[(val:string) => !!val || `${'กรุณาเลือกสถานะการลา'}`]"
hide-bottom-space
:label="`${'สถานะการลา'}`"
emit-value
map-options
option-label="name"
:options="statLeaveOption"
option-value="id"
use-input
input-debounce="0"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'statLeaveOption'
) "
/>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<q-input
ref="reasonRef"
class="full-width inputgreen cursor-pointer"
outlined
dense
lazy-rules
v-model="reason"
type="textarea"
:rules="[(val:string) => !!val || `${'กรุณากรอกเหตุผล'}`]"
hide-bottom-space
:label="`${'เหตุผล'}`"
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn label="บันทึก" id="onSubmit" type="submit" color="public">
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</form>
</q-card>
</q-dialog>
<DialogHistory v-model:modal="modalHistory" v-model:id="id" />
</template>
<style scoped></style>

View file

@ -0,0 +1,319 @@
<script setup lang="ts">
import { ref, watch, reactive } from "vue";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar, type QTableProps } from "quasar";
import { useRoute } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
import type {
DetailData,
FormFilter,
} from "@/modules/04_registryPerson/interface/index/leave";
const modal = defineModel<boolean>("modal", { required: true });
const id = defineModel<string>("id", { required: true });
const $q = useQuasar();
const route = useRoute();
const mixin = useCounterMixin();
const { showLoader, hideLoader, messageError, date2Thai } = mixin;
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const filterKeyword = ref<string>("");
const rows = ref<DetailData[]>([]); //select data history
const formFilter = reactive<FormFilter>({
page: 1,
pageSize: 12,
keyword: "",
type: "",
posType: "",
posLevel: "",
retireYear: "",
rangeYear: { min: 0, max: 60 },
isShowRetire: false,
isProbation: false,
});
const historyPagination = ref({
page: 1,
rowsPerPage: 10,
});
const visibleColumns = ref<String[]>([
"no",
"typeLeave",
"dateLeave",
"numLeave",
"sumLeave",
"totalLeave",
"status",
"reason",
"lastUpdateFullName",
"lastUpdatedAt",
]);
const columns = ref<QTableProps["columns"]>([
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: false,
field: "no",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "typeLeave",
align: "left",
label: "ประเภทการลา",
sortable: true,
field: "typeLeave",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "dateLeave",
align: "left",
label: "วัน เดือน ปี ที่ลา",
sortable: true,
field: "dateLeave",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "numLeave",
align: "left",
label: "จำนวนวันลา",
sortable: true,
field: "numLeave",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "status",
align: "left",
label: "สถานะ",
sortable: true,
field: "status",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reason",
align: "left",
label: "เหตุผล",
sortable: true,
field: "reason",
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: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
format: (v) => date2Thai(v),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
function getHistory() {
showLoader();
http
.get(config.API.profileNewLeaveHistory(id.value, empType.value))
.then((res) => {
let data = res.data.result;
rows.value = [];
data.map((e: any) => {
rows.value.push({
...e,
id: e.id,
typeLeave: e.leaveType.name,
code: e.leaveType.refCommandDate,
dateStartLeave: e.dateLeaveStart,
dateEndLeave: e.dateLeaveEnd,
numLeave: e.leaveDays,
status: e.status,
reason: e.reason,
typeLeaveId:
e.typeLeaveId !== "00000000-0000-0000-0000-000000000000"
? e.typeLeaveId
: "",
});
});
})
.catch((e) => {
// messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
/**
* แปลงชวงวนทา2คาเปนวนเดยวกนจะโชววนเดยวแตาไมเทากนจะแสดงเปนชวง
* @param val วงวนท
*/
function 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])} `;
}
}
function statusLeave(val: string) {
switch (val) {
case "waitting":
return "รออนุมัติ";
case "reject":
return "ไม่ผ่านการอนุมัติ";
case "approve":
return "ผ่านการอนุมัติ";
case "cancel":
return "ยกเลิก";
default:
return "-";
}
}
watch(modal, (status) => {
if (status == true) {
getHistory();
filterKeyword.value = "";
} else {
filterKeyword.value = "";
}
});
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="min-width: 80%">
<DialogHeader tittle="ประวัติแก้ไขการลา" :close="() => (modal = false)" />
<q-separator color="grey-4" />
<q-card-section style="max-height: 60vh" class="scroll">
<div class="row q-gutter-sm q-mb-sm">
<q-space />
<q-input
standout
dense
v-model="filterKeyword"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterKeyword == ''"
name="search"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterKeyword"
name="cancel"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
</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>
<d-table
ref="table"
flat
bordered
dense
:columns="columns"
:rows="rows"
:paging="true"
v-model:pagination="historyPagination"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumns"
:filter="filterKeyword"
>
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name === 'no'">
{{
(formFilter.page - 1) * formFilter.pageSize +
props.rowIndex +
1
}}
</div>
<div v-else-if="col.name == 'dateLeave'">
{{
dateThaiRange([
props.row.dateStartLeave,
props.row.dateEndLeave,
])
}}
</div>
<div v-else-if="col.name == 'status'">
{{ statusLeave(col.value) }}
</div>
<div v-else>
{{ 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,723 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { useQuasar, type QTableProps } from "quasar";
import type {
FormFilter,
RequestItemsObject,
} from "@/modules/04_registryPerson/interface/index/performSpecialWork";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import DialogHeader from "@/components/DialogHeader.vue";
import DialogHistory from "@/modules/04_registryPerson/components/detail/GovernmentInformation/04_PerformSpecialWorkHistory.vue";
import { useRoute } from "vue-router";
const route = useRoute();
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const $q = useQuasar();
const mixin = useCounterMixin();
const {
date2Thai,
dialogConfirm,
messageError,
showLoader,
hideLoader,
success,
dialogRemove,
} = mixin;
const modal = ref<boolean>(false);
const edit = ref<boolean>(false);
const modalHistory = ref<boolean>(false);
const id = ref<string>("");
const dateStartRef = ref<object | null>(null);
const dateEndRef = ref<object | null>(null);
const detailRef = ref<object | null>(null);
const referenceRef = ref<object | null>(null);
const dutyData = reactive<RequestItemsObject>({
profileId: profileId.value,
dateStart: new Date(),
dateEnd: null,
detail: "",
reference: "",
refCommandNo: "",
refCommandDate: null,
});
const pagination = ref({
page: 1,
rowsPerPage: 10,
});
const rows = ref<RequestItemsObject[]>([]);
const filterKeyword = ref<string>("");
const mode = ref<string>("table");
const formFilter = reactive<FormFilter>({
page: 1,
pageSize: 12,
keyword: "",
type: "",
posType: "",
posLevel: "",
retireYear: "",
rangeYear: { min: 0, max: 60 },
isShowRetire: false,
isProbation: false,
});
const currentPage = ref<number>(1);
const maxPage = ref<number>(1);
const visibleColumns = ref<String[]>([
"dateStart",
"dateEnd",
"detail",
"reference",
"refCommandNo",
"refCommandDate",
]);
const columns = ref<QTableProps["columns"]>([
{
name: "dateStart",
align: "left",
label: "เริ่มต้น",
sortable: true,
field: "dateStart",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "dateEnd",
align: "left",
label: "สิ้นสุด",
sortable: true,
field: "dateEnd",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reference",
align: "left",
label: "เอกสารอ้างอิง",
sortable: true,
field: "reference",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
/** เปิด dialog */
function openDialogAdd() {
modal.value = true;
}
/** กดแก้ไข */
function openDialogEdit(props: RequestItemsObject) {
modal.value = true;
edit.value = true;
id.value = props.id ? props.id : "";
dutyData.dateStart = props.dateStart;
dutyData.dateEnd = props.dateEnd;
dutyData.detail = props.detail;
dutyData.reference = props.reference;
dutyData.refCommandNo = props.refCommandNo;
dutyData.refCommandDate = props.refCommandDate;
}
/**
* งชนดอมลประวแกไขขอมลทเลอก
* @param row อม row ประวการแกไข
*/
function openDialogHistory(idOrder: string) {
modalHistory.value = true;
id.value = idOrder;
}
/** ปิด dialog */
function closeDialog() {
modal.value = false;
edit.value = false;
dutyData.dateStart = new Date();
dutyData.dateEnd = null;
dutyData.detail = "";
dutyData.reference = "";
dutyData.refCommandNo = "";
dutyData.refCommandDate = null;
}
/** fetch ข้อมูล */
async function fetchData(id: string) {
showLoader();
await http
.get(config.API.profileNewDutyByProfileId(id, empType.value))
.then(async (res) => {
rows.value = res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** เพิ่มข้อมูล */
async function addData() {
const body = {
profileId: empType.value === "" ? profileId.value : undefined,
profileEmployeeId: empType.value !== "" ? profileId.value : undefined,
dateStart: dutyData.dateStart,
dateEnd: dutyData.dateEnd,
detail: dutyData.detail,
reference: dutyData.reference,
refCommandNo: dutyData.refCommandNo,
refCommandDate: dutyData.refCommandDate,
};
await http
.post(config.API.profileNewDuty(empType.value), body)
.then(() => {
fetchData(profileId.value);
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** แก้ไขข้อมูล */
async function editData(idData: string) {
await http
.patch(config.API.profileNewDutyByDutyId(idData, empType.value), {
...dutyData,
profileId: undefined,
})
.then(() => {
fetchData(profileId.value);
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** ลบข้อมูล */
// function deleteData(idData: string) {
// dialogRemove($q, () =>
// http
// .delete(config.API.profileNewDutyByDutyId(idData))
// .then(() => {
// fetchData(profileId.value);
// success($q, "");
// })
// .catch((err) => {
// messageError($q, err);
// })
// .finally(() => {
// hideLoader();
// })
// );
// }
/** กด Submit */
async function onSubmit() {
dialogConfirm(
$q,
async () => {
edit.value ? editData(id.value) : addData();
closeDialog();
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
onMounted(async () => {
await fetchData(profileId.value);
});
</script>
<template>
<div class="row items-center q-gutter-x-sm q-pb-sm">
<q-btn dense color="primary" icon="add" flat round @click="openDialogAdd()"
><q-tooltip>เพมขอม</q-tooltip></q-btn
>
<q-space />
<q-input
standout
dense
v-model="filterKeyword"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterKeyword == ''"
name="search"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterKeyword"
name="cancel"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
</template>
</q-input>
<q-select
v-if="mode == 'table'"
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-btn-toggle
v-model="mode"
dense
class="no-shadow toggle-borderd"
toggle-color="grey-4"
:options="[
{ value: 'table', slot: 'table' },
{ value: 'card', slot: 'card' },
]"
>
<template v-slot:table>
<q-icon
name="format_list_bulleted"
size="24px"
:style="{
color: mode === 'table' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
<template v-slot:card>
<q-icon
name="mdi-view-grid-outline"
size="24px"
:style="{
color: mode === 'card' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
</q-btn-toggle>
</div>
<d-table
ref="table"
flat
bordered
dense
:card-container-class="mode === 'card' ? 'q-col-gutter-md' : ''"
:columns="columns"
:rows="rows"
:grid="mode === 'card'"
:paging="true"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumns"
:filter="filterKeyword"
v-model:pagination="pagination"
>
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<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" v-if="mode === 'table'">
<q-tr :props="props" class="cursor-pointer">
<q-td auto-width>
<q-btn
color="primary"
flat
dense
round
class="q-mr-xs"
size="14px"
icon="mdi-pencil-outline"
clickable
@click="openDialogEdit(props.row)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="openDialogHistory(props.row.id)"
>
<q-tooltip>ประวแกไขปฏราชการพเศษ</q-tooltip>
</q-btn>
<!-- <q-btn
color="red"
flat
dense
round
size="14px"
icon="mdi-delete"
clickable
@click.stop="deleteData(props.row.id)"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn> -->
</q-td>
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name === 'no'">
{{
(formFilter.page - 1) * formFilter.pageSize + props.rowIndex + 1
}}
</div>
<div v-else>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
<template v-slot:item="props" v-else>
<div class="col-xs-12 col-sm-6 col-md-6">
<q-card flat bordered class="q-pa-none">
<div class="row bg-grey-3">
<q-space />
<div class="q-gutter-x-sm">
<q-btn
color="primary"
icon="mdi-pencil-outline"
flat
round
@click="openDialogEdit(props.row)"
><q-tooltip>แกไขขอม</q-tooltip></q-btn
>
<q-btn
color="info"
icon="mdi-history"
flat
round
@click="openDialogHistory(props.row.id)"
><q-tooltip>ประวแกไขปฏราชการพเศษ</q-tooltip></q-btn
>
</div>
</div>
<q-separator />
<q-card-section class="q-pa-none">
<div class="row q-pa-sm">
<div class="col text-grey-6 text-weight-medium">เรมต</div>
<div class="col">
{{ props.row.dateStart ? date2Thai(props.row.dateStart) : "-" }}
</div>
<div class="col text-grey-6 text-weight-medium">นส</div>
<div class="col">
{{ props.row.dateEnd ? date2Thai(props.row.dateEnd) : "-" }}
</div>
</div>
<q-separator />
<div class="row q-pa-sm bg-grey-2">
<div class="col-3 text-grey-6 text-weight-medium">
เอกสารอางอ
</div>
<div class="col-3">
{{ props.row.reference ? props.row.reference : "-" }}
</div>
<div class="col text-grey-6 text-weight-medium">เลขทคำส</div>
<div class="col">
{{ props.row.refCommandNo ? props.row.refCommandNo : "-" }}
</div>
</div>
<q-separator />
<div class="row q-pa-sm">
<div class="col-3 text-grey-6 text-weight-medium">
เอกสารอางอ (ลงวนท)
</div>
<div class="col-3">
{{
props.row.refCommandDate
? date2Thai(props.row.refCommandDate)
: "-"
}}
</div>
</div>
<q-separator />
<div class="row q-pa-sm bg-grey-2">
<div class="col-3 text-grey-6 text-weight-medium">รายละเอยด</div>
<div class="col-9">
{{ props.row.detail ? props.row.detail : "-" }}
</div>
</div>
</q-card-section>
</q-card>
</div>
</template>
</d-table>
<q-dialog v-model="modal" persistent>
<q-card>
<q-form @submit.prevent greedy @validation-success="onSubmit()">
<DialogHeader
:tittle="
edit
? 'แก้ไขข้อมูลปฏิบัติราชการพิเศษ'
: 'เพิ่มข้อมูลปฏิบัติราชการพิเศษ'
"
:close="closeDialog"
/>
<q-separator />
<q-card-section>
<div class="row col-12 q-col-gutter-x-xs q-col-gutter-y-xs">
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
v-model="dutyData.dateStart"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
ref="dateStartRef"
class="full-width inputgreen cursor-pointer"
outlined
dense
:model-value="date2Thai(dutyData.dateStart)"
:rules="[(val) => !!val || `${'กรุณาเลือกวันที่เริ่มต้น'}`]"
hide-bottom-space
:label="`${'วันที่เริ่มต้น'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
v-model="dutyData.dateEnd"
:locale="'th'"
autoApply
:enableTimePicker="false"
:min-date="dutyData.dateStart"
:readonly="!dutyData.dateStart"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
ref="dateEndRef"
:readonly="!dutyData.dateStart"
class="full-width inputgreen cursor-pointer"
dense
outlined
:model-value="date2Thai(dutyData.dateEnd)"
:rules="[(val) => !!val || `${'กรุณาเลือกวันที่สิ้นสุด'}`]"
hide-bottom-space
:label="`${'วันที่สิ้นสุด'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-12">
<q-input
ref="referenceRef"
class="full-width inputgreen cursor-pointer"
outlined
dense
lazy-rules
autogrow
v-model="dutyData.reference"
:rules="[(val) => !!val || `${'กรุณากรอกเอกสารอ้างอิง'}`]"
hide-bottom-space
:label="`${'เอกสารอ้างอิง'}`"
/>
</div>
<div class="col-12">
<q-input
ref="detailRef"
class="full-width inputgreen cursor-pointer"
outlined
dense
lazy-rules
autogrow
v-model="dutyData.detail"
:rules="[(val) => !!val || `${'กรุณากรอกรายละเอียด'}`]"
hide-bottom-space
:label="`${'รายละเอียด'}`"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
class="full-width inputgreen cursor-pointer"
outlined
dense
v-model="dutyData.refCommandNo"
:label="`${'เลขที่คำสั่ง'}`"
>
<template v-slot:append>
<q-icon name="mdi-file" class="cursor-pointer" />
</template>
</q-input>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
v-model="dutyData.refCommandDate"
:locale="'th'"
autoApply
: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="full-width inputgreen cursor-pointer"
outlined
dense
lazy-rules
:borderless="!edit"
:model-value="
dutyData.refCommandDate == null ? null : date2Thai(dutyData.refCommandDate as Date)
"
hide-bottom-space
:label="`${'เอกสารอ้างอิง (ลงวันที่)'}`"
clearable
@clear="dutyData.refCommandDate = null"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
>
</q-icon>
</template>
<template
v-if="dutyData.refCommandDate && edit"
v-slot:append
>
</template>
</q-input>
</template>
</datepicker>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn label="บันทึก" id="onSubmit" type="submit" color="public">
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<DialogHistory v-model:modal="modalHistory" v-model:id="id" />
</template>
<style scoped></style>

View file

@ -0,0 +1,284 @@
<script setup lang="ts">
import { ref, watch, reactive } from "vue";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar, type QTableProps } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import type {
FormFilter,
ResponseObject,
} from "@/modules/04_registryPerson/interface/index/performSpecialWork";
import { useRoute } from "vue-router";
const modal = defineModel<boolean>("modal", { required: true });
const id = defineModel<string>("id", { required: true });
const route = useRoute();
const $q = useQuasar();
const mixin = useCounterMixin();
const { showLoader, hideLoader, messageError, date2Thai } = mixin;
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const currentPage = ref<number>(1);
const maxPage = ref<number>(1);
const filterKeyword = ref<string>("");
const rows = ref<ResponseObject[]>([]); //select data history
const formFilter = reactive<FormFilter>({
page: 1,
pageSize: 12,
keyword: "",
type: "",
posType: "",
posLevel: "",
retireYear: "",
rangeYear: { min: 0, max: 60 },
isShowRetire: false,
isProbation: false,
});
const historyPagination = ref({
page: 1,
rowsPerPage: 10,
});
const visibleColumns = ref<String[]>([
"dateStart",
"dateEnd",
"detail",
"reference",
"refCommandNo",
"refCommandDate",
"lastUpdateFullName",
"lastUpdatedAt",
]);
const columns = ref<QTableProps["columns"]>([
{
name: "dateStart",
align: "left",
label: "เริ่มต้น",
sortable: true,
field: "dateStart",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "dateEnd",
align: "left",
label: "สิ้นสุด",
sortable: true,
field: "dateEnd",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reference",
align: "left",
label: "เอกสารอ้างอิง",
sortable: true,
field: "reference",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
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: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
function getHistory() {
showLoader();
http
.get(config.API.profileNewDutyHisByDutyId(id.value, empType.value))
.then((res) => {
let data = res.data.result;
rows.value = [];
data.map((e: ResponseObject) => {
rows.value.push({
id: e.id,
dateStart: new Date(e.dateStart),
dateEnd: new Date(e.dateEnd),
detail: e.detail,
reference: e.reference,
refCommandNo: e.refCommandNo,
refCommandDate:
e.refCommandDate == null ? null : new Date(e.refCommandDate),
createdFullName: e.createdFullName,
createdAt: new Date(e.createdAt),
lastUpdateFullName: e.lastUpdateFullName,
lastUpdatedAt: e.lastUpdatedAt,
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
watch(modal, (status) => {
if (status == true) {
getHistory();
filterKeyword.value = "";
} else {
filterKeyword.value = "";
}
});
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="min-width: 80%">
<DialogHeader
tittle="ประวัติแก้ไขปฏิบัติราชการพิเศษ"
:close="() => (modal = false)"
/>
<q-card-section style="max-height: 60vh" class="scroll">
<div class="row q-gutter-sm q-mb-sm">
<q-space />
<q-input
standout
dense
v-model="filterKeyword"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterKeyword == ''"
name="search"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterKeyword"
name="cancel"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
</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>
<d-table
ref="table"
flat
bordered
dense
:columns="columns"
:rows="rows"
:paging="true"
v-model:pagination="historyPagination"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumns"
:filter="filterKeyword"
>
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id">
<div v-if="col.name === 'no'">
{{
(formFilter.page - 1) * formFilter.pageSize +
props.rowIndex +
1
}}
</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,54 @@
<script setup lang="ts">
import { ref } from "vue";
/** importComponents*/
import Info from "@/modules/04_registryPerson/components/detail/GovernmentInformation/01_Info.vue";
import Discipline from "@/modules/04_registryPerson/components/detail/GovernmentInformation/02_Discipline.vue";
import Leave from "@/modules/04_registryPerson/components/detail/GovernmentInformation/03_Leave.vue";
import PerformSpecialWork from "@/modules/04_registryPerson/components/detail/GovernmentInformation/04_PerformSpecialWork.vue";
const tab = ref<string>("1");
</script>
<template>
<div class="row items-center q-my-md">
<div class="text-dark row items-center q-px-md">
<q-icon name="mdi-account" class="q-mr-md" size="22px" />
<div class="text-subtitle1 text-weight-bold">อมลราชการ</div>
</div>
</div>
<q-separator />
<q-tabs
v-model="tab"
active-color="blue-8"
align="left"
bordered
narrow-indicator
indicator-color="transparent"
dense
class="text-grey q-pl-sm"
>
<q-tab name="1" label="ข้อมูลราชการ" />
<q-tab name="2" label="วินัย" />
<q-tab name="3" label="การลา" />
<q-tab name="4" label="ปฏิบัติราชการพิเศษ" />
</q-tabs>
<q-separator />
<q-tab-panels v-model="tab" animated>
<q-tab-panel name="1">
<Info />
</q-tab-panel>
<q-tab-panel name="2">
<Discipline />
</q-tab-panel>
<q-tab-panel name="3">
<Leave />
</q-tab-panel>
<q-tab-panel name="4">
<PerformSpecialWork />
</q-tab-panel>
</q-tab-panels>
</template>
<style scoped></style>

View file

@ -0,0 +1,482 @@
<script setup lang="ts">
import { useQuasar, type QTableProps } from "quasar";
import { ref, onMounted, reactive } from "vue";
import type {
RowList,
FormFilter,
MyObjectRef,
} from "@/modules/04_registryPerson/interface/index/other";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import DialogHeader from "@/components/DialogHeader.vue";
import { useRoute } from "vue-router";
import DialogHistory from "@/modules/04_registryPerson/components/detail/Other/01_OtherInformationHistory.vue";
const route = useRoute();
const $q = useQuasar();
const mixin = useCounterMixin();
const {
date2Thai,
showLoader,
hideLoader,
success,
messageError,
dialogRemove,
dialogConfirm,
} = mixin;
const id = ref<string>("");
const pagination = ref({
page: 1,
rowsPerPage: 10,
});
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const mode = ref<string>("table");
const filterKeyword = ref<string>("");
const rows = ref<RowList[]>([]);
const currentPage = ref<number>(1);
const maxPage = ref<number>(1);
const formFilter = reactive<FormFilter>({
page: 1,
pageSize: 12,
keyword: "",
type: "",
posType: "",
posLevel: "",
retireYear: "",
rangeYear: { min: 0, max: 60 },
isShowRetire: false,
isProbation: false,
});
/** modal */
const modal = ref<boolean>(false);
const edit = ref<boolean>(false);
const modalHistory = ref<boolean>(false);
const date = ref<Date | null>(null);
const detail = ref<string>();
const dateRef = ref<object | null>(null);
const detailRef = ref<object | null>(null);
const objectRef: MyObjectRef = {
date: dateRef,
detail: detailRef,
};
const visibleColumns = ref<String[]>(["date", "detail"]);
const columns = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วันที่",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px; width: 50px;",
format: (v) => date2Thai(v),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
/** เปิด dialog */
function openDialogAdd() {
modal.value = true;
}
/** dialog
* @param props อม row ของรายการ
*/
function openDialogEdit(props: RowList) {
modal.value = true;
edit.value = true;
id.value = props.id;
date.value = props.date;
detail.value = props.detail;
}
/** dialog
* @param id id รายการ
*/
function openDialogHistory(idOrder: string) {
id.value = idOrder;
modalHistory.value = true;
}
/** ปิด dialog */
function closeDialog() {
modal.value = false;
edit.value = false;
date.value = null;
detail.value = "";
}
/** validate check*/
function validateForm() {
dialogConfirm(
$q,
async () => {
if (edit.value) {
editData();
} else {
saveData();
}
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
/**
* นทกเพมขอม
*/
async function saveData() {
showLoader();
await http
.post(config.API.profileNewOther(empType.value), {
profileId: empType.value === "" ? profileId.value : undefined,
profileEmployeeId: empType.value !== "" ? profileId.value : undefined,
date: date.value,
detail: detail.value,
})
.then(() => {
success($q, "บันทึกข้อมูลสำเร็จ");
getData();
closeDialog();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
/**
* นทกแกไขขอม
*/
async function editData() {
showLoader();
await http
.patch(config.API.profileNewOtherById(id.value, empType.value), {
date: date.value,
detail: detail.value,
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
getData();
closeDialog();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
async function getData() {
showLoader();
await http
.get(config.API.profileNewOtherByProfileId(profileId.value, empType.value))
.then((res) => {
rows.value = res.data.result;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
onMounted(() => {
getData();
});
</script>
<template>
<div class="row items-center q-gutter-x-sm q-pb-sm">
<q-btn dense color="primary" icon="add" flat round @click="openDialogAdd()"
><q-tooltip>เพมขอม</q-tooltip></q-btn
>
<q-space />
<q-input
standout
dense
v-model="filterKeyword"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterKeyword == ''"
name="search"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterKeyword"
name="cancel"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
</template>
</q-input>
<q-select
v-if="mode == 'table'"
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-btn-toggle
v-model="mode"
dense
class="no-shadow toggle-borderd"
toggle-color="grey-4"
:options="[
{ value: 'table', slot: 'table' },
{ value: 'card', slot: 'card' },
]"
>
<template v-slot:table>
<q-icon
name="format_list_bulleted"
size="24px"
:style="{
color: mode === 'table' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
<template v-slot:card>
<q-icon
name="mdi-view-grid-outline"
size="24px"
:style="{
color: mode === 'card' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
</q-btn-toggle>
</div>
<d-table
:rows="rows"
:columns="columns"
dense
bordered
v-model:pagination="pagination"
flat
:card-container-class="mode === 'card' ? 'q-col-gutter-md' : ''"
:grid="mode === 'card'"
:paging="true"
:visible-columns="visibleColumns"
:filter="filterKeyword"
:rows-per-page-options="[10, 25, 50, 100]"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<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" v-if="mode === 'table'">
<q-tr :props="props" class="cursor-pointer">
<q-td auto-width>
<q-btn
flat
dense
round
class="q-mr-xs"
size="14px"
color="primary"
icon="mdi-pencil-outline"
@click="openDialogEdit(props.row)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="openDialogHistory(props.row.id)"
>
<q-tooltip>ประวแกไขอนๆ</q-tooltip>
</q-btn>
</q-td>
<q-td v-for="col in props.cols" :key="col.id">
<div>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
<template v-slot:item="props" v-else>
<div class="col-xs-12 col-sm-6 col-md-6">
<q-card flat bordered>
<div class="row bg-grey-3">
<q-space />
<div class="q-gutter-x-sm">
<q-btn
color="primary"
icon="mdi-pencil-outline"
flat
round
@click="openDialogEdit(props.row)"
><q-tooltip>แกไข</q-tooltip></q-btn
>
<q-btn
color="info"
icon="mdi-history"
flat
round
@click="openDialogHistory(props.row.id)"
><q-tooltip>ประวแกไขอนๆ</q-tooltip></q-btn
>
</div>
</div>
<q-separator />
<q-card-section class="q-pa-none">
<q-item
v-for="(col, index) in props.cols.filter(
(col) => col.name !== 'desc'
)"
:key="col.name"
:class="index % 2 !== 0 ? 'bg-grey-1' : ''"
>
<q-item-section class="text-grey-6">
<q-item-label>{{ col.label }}</q-item-label>
</q-item-section>
<q-item-section class="text-dark">
<q-item-label>
{{ col.value ? col.value : "-" }}
</q-item-label>
</q-item-section>
</q-item>
</q-card-section>
</q-card>
</div>
</template>
</d-table>
<!-- dialog add edit -->
<q-dialog v-model="modal" persistent>
<q-card style="min-width: 70%">
<q-form greedy @submit.prevent @validation-success="validateForm">
<DialogHeader
:tittle="edit ? 'แก้ไขข้อมูลอื่นๆ' : 'เพิ่มข้อมูลอื่นๆ'"
:close="closeDialog"
/>
<q-separator />
<q-card-section style="max-height: 60vh" class="scroll">
<div class="row col-12 q-col-gutter-sm">
<div class="col-xs-6 col-sm-3 col-md-3">
<datepicker
menu-class-name="modalfix"
v-model="date"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
ref="dateRef"
class="full-width inputgreen cursor-pointer"
outlined
dense
:model-value="date2Thai(date)"
:rules="[(val) => !!val || `${'กรุณาเลือกวันที่'}`]"
hide-bottom-space
:label="`${'วันที่'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
:style="'color: var(--q-primary)'"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-6 col-sm-9 col-md-9">
<q-input
ref="detailRef"
class="full-width inputgreen cursor-pointer"
outlined
dense
lazy-rules
v-model="detail"
:rules="[(val) => !!val || `${'กรุณากรอกรายละเอียด'}`]"
hide-bottom-space
:label="`${'รายละเอียด'}`"
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
dense
unelevated
label="บันทึก"
id="onSubmit"
type="submit"
color="public"
class="q-px-md"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<DialogHistory v-model:modal="modalHistory" v-model:id="id" />
</template>
<style scoped></style>

View file

@ -0,0 +1,217 @@
<script setup lang="ts">
import { ref, watch, reactive } from "vue";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar, type QTableProps } from "quasar";
import { useRoute } from "vue-router";
import type { RowList } from "@/modules/04_registryPerson/interface/index/other";
import http from "@/plugins/http";
import config from "@/app.config";
import type {
RequestItemsObject,
FormFilter,
} from "@/modules/04_registryPerson/interface/index/discipline";
const modal = defineModel<boolean>("modal", { required: true });
const id = defineModel<string>("id", { required: true });
const $q = useQuasar();
const route = useRoute();
const mixin = useCounterMixin();
const { showLoader, hideLoader, messageError, date2Thai } = mixin;
const historyPagination = ref({
page: 1,
rowsPerPage: 10,
});
const currentPage = ref<number>(1);
const maxPage = ref<number>(1);
const filterKeyword = ref<string>("");
const rows = ref<RowList[]>([]); //select data history
const formFilter = reactive<FormFilter>({
page: 1,
pageSize: 12,
keyword: "",
type: "",
posType: "",
posLevel: "",
retireYear: "",
rangeYear: { min: 0, max: 60 },
isShowRetire: false,
isProbation: false,
});
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const visibleColumns = ref<String[]>([
"date",
"detail",
"lastUpdateFullName",
"lastUpdatedAt",
]);
const columns = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วันที่",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px; width: 50px;",
format: (v) => date2Thai(v),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
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: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
function getHistory() {
showLoader();
http
.get(config.API.profileNewOtherHisById(id.value, empType.value))
.then((res) => {
let data = res.data.result;
rows.value = [];
data.map((e: RowList) => {
rows.value.push({
id: e.id,
date: e.date,
detail: e.detail,
lastUpdateFullName: e.lastUpdateFullName,
lastUpdatedAt: new Date(e.lastUpdatedAt),
});
});
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
watch(modal, (status) => {
if (status == true) {
getHistory();
filterKeyword.value = "";
} else {
filterKeyword.value = "";
}
});
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="min-width: 80%">
<DialogHeader tittle="ประวัติแก้ไขอื่นๆ" :close="() => (modal = false)" />
<q-separator color="grey-4" />
<q-card-section style="max-height: 60vh" class="scroll">
<div class="row q-gutter-sm q-mb-sm">
<q-space />
<q-input
standout
dense
v-model="filterKeyword"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterKeyword == ''"
name="search"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterKeyword"
name="cancel"
@click.stop.prevent="filterKeyword = ''"
class="cursor-pointer"
/>
</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>
<d-table
ref="table"
flat
bordered
dense
:columns="columns"
:rows="rows"
:paging="true"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumns"
:filter="filterKeyword"
>
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id">
<div>
{{ 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,258 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { useRoute } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
import axios from "axios";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
import type { ArrayFileList } from "@/modules/04_registryPerson/interface/index/document";
const $q = useQuasar();
const route = useRoute();
const mixin = useCounterMixin();
const {
success,
messageError,
showLoader,
hideLoader,
dialogConfirm,
dialogRemove,
} = mixin;
const documentFile = ref<any>(null);
const fileList = ref<ArrayFileList[]>([]);
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
async function getData() {
showLoader();
await http
.get(
config.API.file("ระบบทะเบียนประวัติ", "เอกสารหลักฐาน", profileId.value)
)
.then((res) => {
fileList.value = res.data;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
/**
* งกนสำหรบอพโหลดไฟลเอกสารหลกฐาน
*/
async function uploadFileDoc(uploadUrl: string, file: any) {
const Data = new FormData();
Data.append("file", documentFile.value);
showLoader();
await axios
.put(uploadUrl, file, {
headers: {
"Content-Type": file.type,
},
})
.then((res) => {
success($q, "อัปโหลดไฟล์สำเร็จ");
getData();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
documentFile.value = null;
});
}
async function clickUpload(file: any) {
const fileName = { fileName: file.name };
dialogConfirm(
$q,
async () => {
const selectedFile = file;
const formdata = new FormData();
formdata.append("file", selectedFile);
await http
.post(
config.API.file(
"ระบบทะเบียนประวัติ",
"เอกสารหลักฐาน",
profileId.value
),
{
replace: false,
fileList: fileName,
}
)
.then(async (res) => {
const foundKey: string | undefined = Object.keys(res.data).find(
(key) =>
res.data[key]?.fileName !== undefined &&
res.data[key]?.fileName !== ""
);
foundKey &&
uploadFileDoc(res.data[foundKey]?.uploadUrl, documentFile.value);
})
.catch((err) => {
messageError($q, err);
});
},
"ยืนยันการอัปโหลดไฟล์",
"ต้องการยืนยันการอัปโหลดไฟล์นี้หรือไม่ ?"
);
}
/**
* ดาวนโหลดลงคไฟล
* @param fileName file name
*/
function downloadFile(fileName: string) {
showLoader();
http
.get(
config.API.fileByFile(
"ระบบทะเบียนประวัติ",
"เอกสารหลักฐาน",
profileId.value,
fileName
)
)
.then((res) => {
const data = res.data.downloadUrl;
window.open(data, "_blank");
})
.catch((e) => {
messageError($q, e);
})
.finally(async () => {
hideLoader();
});
}
/**
* ลบไฟล
* @param fileName file name
*/
function deleteFile(fileName: string) {
dialogRemove($q, async () => {
showLoader();
http
.delete(
config.API.fileByFile(
"ระบบทะเบียนประวัติ",
"เอกสารหลักฐาน",
profileId.value,
fileName
)
)
.then((res) => {
success($q, `ลบไฟล์สำเร็จ`);
setTimeout(() => {
getData();
hideLoader();
}, 1000);
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
});
}
onMounted(() => {
getData();
});
</script>
<template>
<q-card bordered class="row col-12" style="border: 1px solid #d6dee1">
<div class="col-12 text-weight-medium bg-grey-1 q-py-sm q-px-md">
ปโหลดไฟลเอกสารหลกฐาน
</div>
<div class="col-12"><q-separator /></div>
<div class="row col-12 q-col-gutter-y-sm q-pa-sm">
<div class="col-12 row">
<q-file
for="inputFiles"
class="col-12"
outlined
dense
v-model="documentFile"
label="ไฟล์เอกสารหลักฐาน"
hide-bottom-space
accept=".pdf,.xlsx,.docx,.png,.jpg"
clearable
>
<template v-slot:prepend>
<q-icon name="attach_file" color="primary" />
</template>
<template v-slot:after>
<q-btn
size="14px"
v-if="documentFile"
flat
round
dense
color="add"
icon="mdi-upload"
@click="clickUpload(documentFile)"
><q-tooltip>ปโหลดไฟล</q-tooltip></q-btn
>
</template>
</q-file>
<!-- <div class="col-1 self-center" v-if="formData.documentFile"></div> -->
</div>
<div v-if="fileList.length > 0" class="col-xs-12 row">
<q-list class="full-width rounded-borders" bordered separator>
<q-item
clickable
v-ripple
v-for="data in fileList"
:key="data.id"
class="items-center"
>
<q-item-section>{{ data.fileName }}</q-item-section>
<q-space />
<div>
<q-btn
size="12px"
flat
round
dense
color="blue"
icon="mdi-download"
@click="downloadFile(data.fileName)"
><q-tooltip>ดาวนโหลดไฟล</q-tooltip></q-btn
>
<q-btn
size="12px"
flat
round
dense
color="red"
class="q-ml-sm"
icon="mdi-delete-outline"
@click="deleteFile(data.fileName)"
><q-tooltip>ลบไฟล</q-tooltip></q-btn
>
</div>
</q-item>
</q-list>
</div>
<div class="col-12" v-else>
<q-card class="q-pa-md" bordered> ไมรายการเอกสาร </q-card>
</div>
</div>
</q-card>
</template>
<style scoped></style>

View file

@ -0,0 +1,44 @@
<script setup lang="ts">
import { ref } from "vue";
/** importComponents*/
import OtherInformation from "@/modules/04_registryPerson/components/detail/Other/01_OtherInformation.vue";
import Documentipline from "@/modules/04_registryPerson/components/detail/Other/02_Document.vue";
const tab = ref<string>("1");
</script>
<template>
<div class="row items-center q-my-md">
<div class="text-dark row items-center q-px-md">
<q-icon name="mdi-account" class="q-mr-md" size="22px" />
<div class="text-subtitle1 text-weight-bold">เอกสารหลกฐานและอนๆ</div>
</div>
</div>
<q-separator />
<q-tabs
v-model="tab"
active-color="blue-8"
align="left"
bordered
narrow-indicator
indicator-color="transparent"
dense
class="text-grey q-pl-sm"
>
<q-tab name="1" label="ข้อมูลอื่นๆ" />
<q-tab name="2" label="เอกสารหลักฐาน" />
</q-tabs>
<q-separator />
<q-tab-panels v-model="tab" animated>
<q-tab-panel name="1">
<OtherInformation />
</q-tab-panel>
<q-tab-panel name="2">
<Documentipline />
</q-tab-panel>
</q-tab-panels>
</template>
<style scoped></style>

View file

@ -0,0 +1,899 @@
<script setup lang="ts">
import { onMounted, watch, ref, reactive } from "vue";
import { useRoute } from "vue-router";
import { useQuasar } from "quasar";
import type { QTableColumn, QForm } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import { useProfileDataStore } from "@/modules/04_registryPerson/stores/profile";
import DialogHeader from "@/components/DialogHeader.vue";
import type { RequestObject } from "@/modules/04_registryPerson/interface/request/Profile";
import type { ResponseObject } from "@/modules/04_registryPerson/interface/response/Profile";
const $q = useQuasar();
const route = useRoute();
const mixin = useCounterMixin();
const store = useProfileDataStore();
const {
success,
showLoader,
hideLoader,
date2Thai,
messageError,
dialogConfirm,
dialogMessageNotify,
} = mixin;
const { calculateAge, fetchPerson, filterSelector } = store;
const props = defineProps({
fetchDataPersonal: { type: Function, require: true },
});
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const currentPage = ref<number>(1);
const maxPage = ref<number>(1);
const modal = ref<boolean>(false);
const informaData = ref<ResponseObject>();
const rowsHistory = ref<ResponseObject[]>([]);
const filterHistory = ref<string>("");
const modalHistory = ref<boolean>(false);
const id = ref<string>("");
const age = ref<string | null>("");
const formData = reactive<RequestObject>(store.defaultProfile);
const pagination = ref({
page: 1,
rowsPerPage: 10,
});
const dataLabel = {
citizenId: "เลขประจำตัวประชาชน",
name: "ชื่อ - สกุล",
birthDate: "วัน/เดือน/ปีเกิด",
age: "อายุ",
gender: "เพศ",
relationship: "สถานภาพ",
nationality: "สัญชาติ",
ethnicity: "เชื้อชาติ",
religion: "ศาสนา",
bloodGroup: "หมู่เลือด",
phone: "เบอร์โทร",
prefix: "คำนำหน้าชื่อ",
rank: "ยศ",
firstName: "ชื่อ",
lastName: "นามสกุล",
};
const columnsHistory = ref<QTableColumn[]>([
{
name: "citizenId",
align: "left",
label: "เลขประจำตัวประชาชน",
sortable: true,
field: "citizenId",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "prefix",
align: "left",
label: "คำนำหน้าชื่อ",
sortable: true,
field: "prefix",
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: "firstName",
align: "left",
label: "ชื่อ",
sortable: true,
field: "firstName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastName",
align: "left",
label: "นามสกุล",
sortable: true,
field: "lastName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "birthDate",
align: "left",
label: "วัน/เดือน/ปี เกิด",
sortable: true,
field: "birthDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => {
return v ? date2Thai(v) : "";
},
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "gender",
align: "left",
label: "เพศ",
sortable: true,
field: "gender",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "relationship",
align: "left",
label: "สถานภาพ",
sortable: true,
field: "relationship",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "bloodGroup",
align: "left",
label: "หมู่เลือด",
sortable: true,
field: "bloodGroup",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "nationality",
align: "left",
label: "สัญชาติ",
sortable: true,
field: "nationality",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "ethnicity",
align: "left",
label: "เชื้อชาติ",
sortable: true,
field: "ethnicity",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "religion",
align: "left",
label: "ศาสนา",
sortable: true,
field: "religion",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "phone",
align: "left",
label: "เบอร์โทร",
sortable: true,
field: "phone",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "createdFullName",
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: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumnsHistory = ref<String[]>([
"citizenId",
"prefix",
"rank",
"firstName",
"lastName",
"birthDate",
"gender",
"relationship",
"bloodGroup",
"nationality",
"ethnicity",
"religion",
"phone",
"createdFullName",
"createdAt",
]);
async function getData() {
showLoader();
http
.get(config.API.registryNewByProfileId(profileId.value, empType.value))
.then((res) => {
informaData.value = res.data.result;
id.value = res.data.result.id;
if (res.data.result.birthDate) {
console.log("birthDate===>", res.data.result.birthDate);
age.value = calculateAge(res.data.result.birthDate);
console.log("age===>", age.value);
}
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
const calculateMaxDate = () => {
const today = new Date();
today.setFullYear(today.getFullYear() - 18);
return today;
};
async function editData() {
showLoader();
await http
.put(config.API.profileNewProfileById(id.value, empType.value), {
...formData,
employeeClass: route.name === "registry-employeeId" ? "TEMP" : undefined,
})
.then(() => {
success($q, "บันทึกข้อมูลสำเร็จ");
getData(), (modal.value = false);
props.fetchDataPersonal?.();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
function onClickOpenDialog() {
if (!informaData.value) return;
modal.value = true;
console.log("onClickOpenDialog birthDate===>", informaData.value.birthDate);
id.value = informaData.value.id;
age.value = calculateAge(informaData.value.birthDate);
formData.citizenId = informaData.value.citizenId;
formData.prefix = informaData.value.prefix;
formData.rank = informaData.value.rank;
formData.firstName = informaData.value.firstName;
formData.lastName = informaData.value.lastName;
formData.birthDate = informaData.value.birthDate;
formData.gender = informaData.value.gender;
formData.relationship = informaData.value.relationship;
formData.nationality = informaData.value.nationality;
formData.ethnicity = informaData.value.ethnicity;
formData.religion = informaData.value.religion;
formData.bloodGroup = informaData.value.bloodGroup;
formData.phone = informaData.value.phone;
}
function onSubmit() {
dialogConfirm(
$q,
async () => {
editData();
modal.value = false;
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
async function clickHistory() {
modalHistory.value = true;
await http
.get(config.API.profileNewProfileHisById(id.value, empType.value))
.then((res) => {
rowsHistory.value = res.data.result;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
function changeCardID(citizenId: string | number | null) {
if (citizenId != null && typeof citizenId == "string") {
if (citizenId.length == 13 && citizenId) {
http
.put(config.API.profileNewCitizenId(citizenId), {
citizenId: citizenId,
})
.then(() => {})
.catch((err) => {
if (err.response.data.status === 500) {
dialogMessageNotify($q, err.response.data.message);
} else {
messageError($q, err);
}
});
}
}
}
watch(
() => formData.birthDate,
(v) => {
console.log("v===>", v);
if (v) {
age.value = calculateAge(v);
}
}
);
onMounted(async () => {
await getData();
if (store.Ops && store.Ops.prefixOps && store.Ops.prefixOps.length === 0) {
fetchPerson();
}
// store.Ops.prefixOps.length === 0 ||
// store.Ops.genderOps.length === 0 ||
// store.Ops.bloodOps.length === 0 ||
// store.Ops.statusOps.length === 0 ||
// store.Ops.religionOps.length === 0
});
</script>
<template>
<div class="row q-gutter-sm items-center">
<div class="toptitle col text-right q-gutter-x-sm">
<q-btn
flat
round
dense
icon="mdi-pencil-outline"
color="primary"
@click="onClickOpenDialog"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
dense
flat
round
icon="mdi-history"
color="info"
@click="clickHistory"
>
<q-tooltip>ประวแกไขขอมลสวนต</q-tooltip>
</q-btn>
</div>
</div>
<q-card bordered class="my-card bg-grey-1 q-pa-md">
<div v-if="informaData" :class="$q.screen.gt.xs ? 'row' : 'column'">
<!-- column 1 -->
<div class="col-md-7 col-12 row">
<div class="col-5 text-grey-6 text-weight-medium">
<div
v-for="label in Object.keys(dataLabel).slice(0, 6)"
class="q-py-xs"
>
{{ dataLabel[label as keyof typeof dataLabel] }}
</div>
</div>
<!-- data -->
<div class="col-7">
<div class="q-py-xs">
{{ informaData.citizenId }}
</div>
<div class="q-py-xs">
{{
`${
informaData.rank ? informaData.rank : informaData.prefix ?? ""
} ${informaData.firstName} ${informaData.lastName}`
}}
</div>
<div class="q-py-xs">
{{ informaData.birthDate ? date2Thai(informaData.birthDate) : "" }}
</div>
<div class="q-py-xs">
{{ age ? age : "-" }}
</div>
<div class="q-py-xs">
{{ informaData.gender ? informaData.gender : "-" }}
</div>
<div class="q-py-xs">
{{ informaData.relationship ? informaData.relationship : "-" }}
</div>
</div>
</div>
<!-- column 2 -->
<div class="col-md-5 col-12 row">
<div class="col-5 col text-grey-6 text-weight-medium">
<div
v-for="label in Object.keys(dataLabel).slice(6, 11)"
class="q-py-xs"
>
{{ dataLabel[label as keyof typeof dataLabel] }}
</div>
</div>
<!-- data -->
<div class="col-7">
<div class="q-py-xs">
{{ informaData.nationality ? informaData.nationality : "-" }}
</div>
<div class="q-py-xs">
{{ informaData.ethnicity ? informaData.ethnicity : "-" }}
</div>
<div class="q-py-xs">
{{ informaData.religion ? informaData.religion : "-" }}
</div>
<div class="q-py-xs">
{{ informaData.bloodGroup ? informaData.bloodGroup : "-" }}
</div>
<div class="q-py-xs">
{{ informaData.phone ? informaData.phone : "-" }}
</div>
</div>
</div>
</div>
</q-card>
<!-- Edit Dialog -->
<q-dialog v-model="modal" persistent>
<q-card>
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader
tittle="แก้ไขประวัติส่วนตัว"
:close="() => (modal = false)"
/>
<q-separator />
<q-card-section>
<div class="row col-12 q-col-gutter-x-xs q-col-gutter-y-xs">
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
maxlength="13"
mask="#############"
v-model="formData.citizenId"
class="inputgreen"
:label="dataLabel.citizenId"
:rules="[
(val: string) => !!val || `${'กรุณากรอก เลขประจำตัวประชาชน'}`,
(val: string) =>
val.length >= 13 ||
`${'กรุณากรอกเลขประจำตัวประชาชนให้ครบ'}`,
]"
@update:model-value="changeCardID"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-select
dense
outlined
use-input
lazy-rules
emit-value
map-options
hide-bottom-space
input-debounce="0"
option-label="name"
option-value="name"
v-model="formData.prefix"
class="inputgreen"
:options="store.Ops.prefixOps"
:label="dataLabel.prefix"
:rules="[(val: string) => !!val || `${'กรุณาเลือก คำนำหน้าชื่อ'}`]"
@filter="(inputValue: any,
doneFn: Function) => filterSelector(inputValue, doneFn, 'prefixOps'
)"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-select
dense
outlined
clearable
use-input
lazy-rules
emit-value
map-options
hide-bottom-space
input-debounce="0"
option-label="name"
option-value="name"
v-model="formData.rank"
class="inputgreen"
:options="store.Ops.rankOps"
:label="dataLabel.rank"
@filter="(inputValue: any,
doneFn: Function) => filterSelector(inputValue, doneFn, 'rankOps'
)"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
v-model="formData.firstName"
class="inputgreen"
:label="dataLabel.firstName"
:rules="[(val: string) => !!val || `${'กรุณากรอก ชื่อ'}`]"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
v-model="formData.lastName"
class="inputgreen"
:label="dataLabel.lastName"
:rules="[(val: string) => !!val || `${'กรุณากรอก นามสกุล'}`]"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<datepicker
autoApply
:max-date="calculateMaxDate()"
borderless
:enableTimePicker="false"
week-start="0"
menu-class-name="modalfix"
v-model="formData.birthDate"
:locale="'th'"
>
<template #year="{ year }">
{{ year + 543 }}
</template>
<template #year-overlay-value="{ value }">
{{ parseInt(value + 543) }}
</template>
<template #trigger>
<q-input
for="inputDatereceive"
ref="dateReceivedRef"
outlined
dense
hide-bottom-space
class="inputgreen"
:model-value="
formData.birthDate ? date2Thai(formData.birthDate) : null
"
:label="dataLabel.birthDate"
:rules="[
(val) => !!val || `${'กรุณาเลือก วัน/เดือน/ปี เกิด'}`,
]"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
color="primary"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
readonly
v-model="age"
:label="dataLabel.age"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-select
dense
outlined
use-input
clearable
lazy-rules
emit-value
map-options
hide-bottom-space
input-debounce="0"
option-label="name"
option-value="name"
v-model="formData.gender"
class="inputgreen"
:options="store.Ops.genderOps"
:label="dataLabel.gender"
@filter="(inputValue: any,
doneFn: Function) => filterSelector(inputValue, doneFn, 'genderOps'
)"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-select
dense
outlined
use-input
clearable
lazy-rules
emit-value
map-options
hide-bottom-space
option-value="name"
option-label="name"
input-debounce="0"
class="inputgreen"
v-model="formData.relationship"
:options="store.Ops.statusOps"
:label="dataLabel.relationship"
@filter="(inputValue: any,
doneFn: Function) => filterSelector(inputValue, doneFn, 'statusOps'
)"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
class="inputgreen"
v-model="formData.nationality"
:label="dataLabel.nationality"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
class="inputgreen"
v-model="formData.ethnicity"
:label="dataLabel.ethnicity"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-select
dense
outlined
use-input
clearable
lazy-rules
emit-value
map-options
hide-bottom-space
option-value="name"
option-label="name"
input-debounce="0"
v-model="formData.religion"
class="inputgreen"
:options="store.Ops.religionOps"
:label="dataLabel.religion"
@filter="(inputValue: any,
doneFn: Function) => filterSelector(inputValue, doneFn, 'religionOps'
)"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-select
dense
outlined
use-input
clearable
lazy-rules
emit-value
map-options
hide-bottom-space
option-value="name"
option-label="name"
input-debounce="0"
v-model="formData.bloodGroup"
class="inputgreen"
:options="store.Ops.bloodOps"
:label="dataLabel.bloodGroup"
@filter="(inputValue: any,
doneFn: Function) => filterSelector(inputValue, doneFn, 'bloodOps'
)"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
mask="##########"
class="inputgreen"
v-model="formData.phone"
:label="dataLabel.phone"
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
label="บันทึก"
id="onSubmit"
type="submit"
color="public"
class="q-px-md"
>
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<q-dialog v-model="modalHistory" persistent>
<q-card style="min-width: 80%">
<DialogHeader
tittle="ประวัติแก้ไขข้อมูลส่วนตัว"
:close="() => (modalHistory = false)"
/>
<q-separator />
<q-card-section style="max-height: 50vh" class="scroll">
<div class="row q-gutter-sm q-mb-sm">
<q-space />
<q-input
standout
dense
v-model="filterHistory"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterHistory == ''"
name="search"
@click.stop.prevent="filterHistory = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterHistory"
name="cancel"
@click.stop.prevent="filterHistory = ''"
class="cursor-pointer"
/>
</template>
</q-input>
<q-select
v-model="visibleColumnsHistory"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columnsHistory"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
<d-table
ref="table"
flat
bordered
dense
:columns="columnsHistory"
:rows="rowsHistory"
class="custom-header-table"
v-model:pagination="pagination"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumnsHistory"
:filter="filterHistory"
>
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id">
<div>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card-section>
<q-separator />
<q-card-actions align="right"> </q-card-actions>
</q-card>
</q-dialog>
</template>
<style lang="scss">
.modalfix {
position: fixed !important;
}
</style>

View file

@ -0,0 +1,934 @@
<script setup lang="ts">
import { onMounted, ref, reactive, watch } from "vue";
import { useRoute } from "vue-router";
import { useQuasar } from "quasar";
import type { QTableProps } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
import { useAddressDataStore } from "@/modules/04_registryPerson/stores/Address";
import type { ResponseObject } from "@/modules/04_registryPerson/interface/response/Address";
import type { RequestObject } from "@/modules/04_registryPerson/interface/request/Address";
const $q = useQuasar();
const store = useAddressDataStore();
const mixin = useCounterMixin();
const route = useRoute();
const {
fetchProvince,
fetchDistrict,
fetchSubDistrict,
findData,
filterSelector,
} = store;
const {
date2Thai,
success,
messageError,
showLoader,
hideLoader,
dialogConfirm,
} = mixin;
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const currentPage = ref<number>(1);
const maxPage = ref<number>(1);
const modal = ref<boolean>(false);
const modalHistory = ref<boolean>(false);
const rowsHistory = ref<ResponseObject[]>([]);
const filterHistory = ref<string>("");
const id = ref<string>("");
const rawSameAddress = ref<string>("0");
const sameAddress = ref<string>("0");
const addressData = reactive<ResponseObject>(store.defaultAddress);
const formData = reactive<RequestObject>(store.defaultAddressForm);
const adsName = reactive({
regisP: "",
regisD: "",
regisSD: "",
currentP: "",
currentD: "",
currentSD: "",
});
const dataLabel = {
registrationAddress: "ที่อยู่ตามทะเบียนบ้าน",
registrationProvince: "จังหวัด",
registrationDistrict: "เขต/อำเภอ",
registrationSubDistrict: "แขวง / ตำบล",
registrationZipCode: "รหัสไปรษณีย์",
currentAddress: "ที่อยู่ปัจจุบัน",
currentProvince: "จังหวัด",
currentDistrict: "เขต/อำเภอ",
currentSubDistrict: "แขวง / ตำบล",
currentZipCode: "รหัสไปรษณีย์",
registrationSame: "ที่อยู่ปัจจุบันตรงกับที่อยู่ตามทะเบียนบ้าน",
};
const visibleColumnsHistory = ref<String[]>([
"currentAddress",
"currentDistrict",
"currentSubDistrict",
"currentZipCode",
"registrationAddress",
"registrationDistrict",
"registrationProvince",
"registrationSame",
"registrationSubDistrict",
"registrationZipCode",
"lastUpdateFullName",
"lastUpdatedAt",
]);
const columnsHistory = ref<QTableProps["columns"]>([
{
name: "registrationAddress",
align: "left",
label: "ที่อยู่ตามทะเบียนบ้าน",
sortable: true,
field: "registrationAddress",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "registrationProvince",
align: "left",
label: "จังหวัดตามทะเบียนบ้าน",
sortable: true,
field: "registrationProvince",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => (v ? v.name : "-"),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "registrationDistrict",
align: "left",
label: "เขต/อำเภอตามทะเบียนบ้าน",
sortable: true,
field: "registrationDistrict",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => (v ? v.name : "-"),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "registrationSubDistrict",
align: "left",
label: "แขวง/ตำบลตามทะเบียนบ้าน",
sortable: true,
field: "registrationSubDistrict",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => (v ? v.name : "-"),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "registrationZipCode",
align: "left",
label: "รหัสไปรษณีย์ตามทะเบียนบ้าน",
sortable: true,
field: "registrationZipCode",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "registrationSame",
align: "left",
label: "ที่อยู่ปัจจุบันตรงกับที่อยู่ตามทะเบียนบ้าน",
sortable: true,
field: (v) =>
v.registrationAddress === v.currentAddress &&
v.registrationZipCode === v.currentZipCode
? "ใช่"
: "ไม่",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "currentAddress",
align: "left",
label: "ที่อยู่ปัจจุบัน",
sortable: true,
field: "currentAddress",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "currentProvince",
align: "left",
label: "จังหวัดปัจจุบัน",
sortable: true,
field: "currentProvince",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => (v ? v.name : "-"),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "currentDistrict",
align: "left",
label: "เขต/อำเภอปัจจุบัน",
sortable: true,
field: "currentDistrict",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => (v ? v.name : "-"),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "currentSubDistrict",
align: "left",
label: "แขวง/ตำบลปัจจุบัน",
sortable: true,
field: "currentSubDistrict",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => (v ? v.name : "-"),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "currentZipCode",
align: "left",
label: "รหัสไปรษณีย์ปัจจุบัน",
sortable: true,
field: "currentZipCode",
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: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
async function getData() {
showLoader();
await http
.get(
config.API.profileNewAddressByProfileId(profileId.value, empType.value)
)
.then((res) => {
Object.assign(addressData, res.data.result);
if (addressData) {
id.value = addressData.id;
addressData.currentAddress === addressData.registrationAddress &&
addressData.currentZipCode === addressData.registrationZipCode &&
addressData.currentAddress &&
addressData.registrationAddress &&
addressData.currentZipCode &&
addressData.registrationZipCode
? (rawSameAddress.value = "1")
: (rawSameAddress.value = "0");
}
sameAddress.value = rawSameAddress.value;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
async function selectProvince(e: string | null, name: string) {
if (!e) return;
if (name == "1") {
formData.registrationDistrictId = "";
formData.registrationSubDistrictId = "";
formData.registrationZipCode = "";
} else {
formData.currentDistrictId = "";
formData.currentSubDistrictId = "";
formData.currentZipCode = "";
}
await fetchDistrict(e, name);
}
async function selectDistrict(e: string | null, name: string) {
if (!e) return;
if (name == "1") {
formData.registrationSubDistrictId = "";
formData.registrationZipCode = "";
} else {
formData.currentSubDistrictId = "";
formData.currentZipCode = "";
}
await fetchSubDistrict(e, name);
}
function selectSubDistrict(e: string | null, name: string) {
if (!e) return;
if (name == "1") {
const findcode = store.Ops.subdistrictOps.filter((r) => r.id == e);
const namecode = findcode.length > 0 ? findcode[0].zipCode : "";
formData.registrationZipCode = namecode;
} else {
const findcode = store.Ops.subdistrictCOps.filter((r) => r.id == e);
const namecode = findcode.length > 0 ? findcode[0].zipCode : "";
formData.currentZipCode = namecode;
}
}
async function fetchAll() {
await getData();
if (!store.profileIdBefore) {
store.profileIdBefore = profileId.value;
}
if (
store.Ops.provinceOps.length === 0 ||
store.Ops.districtOps.length === 0 ||
store.Ops.districtCOps.length === 0 ||
store.Ops.subdistrictOps.length === 0 ||
store.Ops.subdistrictCOps.length === 0 ||
store.profileIdBefore !== profileId.value
) {
await fetchProvince();
await fetchDistrict(addressData.registrationProvinceId, "1");
await fetchDistrict(addressData.currentProvinceId, "2");
await fetchSubDistrict(addressData.registrationDistrictId, "1");
await fetchSubDistrict(addressData.currentDistrictId, "2");
store.profileIdBefore = profileId.value;
}
adsName.regisP = findData(
store.Ops.provinceOps,
addressData.registrationProvinceId
).name;
adsName.regisD = findData(
store.Ops.districtOps,
addressData.registrationDistrictId
).name;
adsName.regisSD = findData(
store.Ops.subdistrictOps,
addressData.registrationSubDistrictId
).name;
adsName.currentP = findData(
store.Ops.provinceOps,
addressData.currentProvinceId
).name;
adsName.currentD = findData(
store.Ops.districtCOps,
addressData.currentDistrictId
).name;
adsName.currentSD = findData(
store.Ops.subdistrictCOps,
addressData.currentSubDistrictId
).name;
}
async function editData() {
showLoader();
await http
.patch(config.API.profileNewAddressById(id.value, empType.value), {
...formData,
id: undefined,
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
fetchAll();
modal.value = false;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
function clickClose() {
Object.assign(formData, store.defaultAddressForm);
modal.value = false;
}
async function onClickOpenDialog() {
if (!addressData) return;
await Object.assign(formData, addressData);
const checkDistrict = await store.Ops.districtOps.some(
(e: any) => e.id === addressData.registrationDistrictId
);
const checkSubDistrict = await store.Ops.subdistrictOps.some(
(e: any) => e.id === addressData.registrationSubDistrictId
);
const checkDistrictC = await store.Ops.districtCOps.some(
(e: any) => e.id === addressData.currentDistrictId
);
const checkSubDistrictC = await store.Ops.subdistrictCOps.some(
(e: any) => e.id === addressData.currentSubDistrictId
);
modal.value = true;
sameAddress.value = rawSameAddress.value;
if (!checkDistrict || !checkSubDistrict) {
await fetchDistrict(addressData.registrationProvinceId, "1");
await fetchSubDistrict(addressData.registrationDistrictId, "1");
}
if (!checkDistrictC || !checkSubDistrictC) {
await fetchDistrict(addressData.currentProvinceId, "2");
await fetchSubDistrict(addressData.currentDistrictId, "2");
}
}
async function clickHistory() {
modalHistory.value = true;
await http
.get(config.API.profileNewAddressHisById(id.value, empType.value))
.then((res) => {
rowsHistory.value = res.data.result;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
function onSubmit() {
dialogConfirm(
$q,
async () => {
if (sameAddress.value === "1") {
formData.currentAddress = formData.registrationAddress;
formData.currentProvinceId = formData.registrationProvinceId;
formData.currentDistrictId = formData.registrationDistrictId;
formData.currentSubDistrictId = formData.registrationSubDistrictId;
formData.currentZipCode = formData.registrationZipCode;
store.Ops.districtCOps = store.Ops.districtOps;
store.Ops.subdistrictCOps = store.Ops.subdistrictOps;
}
editData();
modal.value = false;
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
function sameAddressToggle(v: string) {
if (v === "0") {
formData.currentAddress = "";
formData.currentProvinceId = "";
formData.currentDistrictId = "";
formData.currentSubDistrictId = "";
formData.currentZipCode = "";
store.Ops.districtCOps = [];
store.Ops.subdistrictCOps = [];
}
}
watch(
() => sameAddress.value,
(v) => {
sameAddressToggle(v);
}
);
onMounted(async () => {
await fetchAll();
});
</script>
<template>
<div class="row q-gutter-sm items-center">
<div class="toptitle col text-right q-gutter-x-sm">
<q-btn
flat
round
dense
icon="mdi-pencil-outline"
color="primary"
@click="onClickOpenDialog"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
dense
flat
round
icon="mdi-history"
color="info"
@click="clickHistory"
>
<q-tooltip>ประวอมลทอย</q-tooltip>
</q-btn>
</div>
</div>
<div class="row">
<div class="col-md-6 col-12">
<q-card bordered class="bg-grey-1 q-ma-sm">
<q-card-section>
<div class="text-bold">อยตามทะเบยนบาน</div>
</q-card-section>
<q-separator />
<q-card-section>
<div :class="$q.screen.gt.xs ? '' : 'column'">
<!-- column 1 -->
<div class="col-md-6 col-12 row">
<div class="col-5 text-grey-6 text-weight-medium">อย</div>
<div class="col-7">
<div class="q-py-xs">
{{ addressData.registrationAddress || "-" }}
</div>
</div>
<div class="col-5 text-grey-6 text-weight-medium">แขวง/ตำบล</div>
<div class="col-7">
<div class="q-py-xs">
{{ adsName.regisSD || "-" }}
</div>
</div>
<div class="col-5 text-grey-6 text-weight-medium">เขต/อำเภอ</div>
<div class="col-7">
<div class="q-py-xs">
{{ adsName.regisD || "-" }}
</div>
</div>
<div class="col-5 text-grey-6 text-weight-medium">งหว</div>
<div class="col-7">
<div class="q-py-xs">
{{ adsName.regisP || "-" }}
</div>
</div>
<div class="col-5 text-grey-6 text-weight-medium">
รหสไปรษณ
</div>
<div class="col-7">
<div class="q-py-xs">
{{ addressData.registrationZipCode || "-" }}
</div>
</div>
</div>
</div>
</q-card-section>
</q-card>
</div>
<div class="col-md-6 col-12">
<q-card bordered class="bg-grey-1 q-ma-sm">
<q-card-section>
<div class="text-bold">อยจจ</div>
</q-card-section>
<q-separator />
<q-card-section>
<div :class="$q.screen.gt.xs ? '' : 'column'">
<div class="col-md-6 col-12 row">
<div class="col-5 text-grey-6 text-weight-medium">อย</div>
<div class="col-7">
<div class="q-py-xs">
{{ addressData.currentAddress || "-" }}
</div>
</div>
<div class="col-5 text-grey-6 text-weight-medium">แขวง/ตำบล</div>
<div class="col-7">
<div class="q-py-xs">
{{ adsName.currentSD || "-" }}
</div>
</div>
<div class="col-5 text-grey-6 text-weight-medium">เขต/อำเภอ</div>
<div class="col-7">
<div class="q-py-xs">
{{ adsName.currentD || "-" }}
</div>
</div>
<div class="col-5 text-grey-6 text-weight-medium">งหว</div>
<div class="col-7">
<div class="q-py-xs">
{{ adsName.currentP || "-" }}
</div>
</div>
<div class="col-5 text-grey-6 text-weight-medium">
รหสไปรษณ
</div>
<div class="col-7">
<div class="q-py-xs">
{{ addressData.currentZipCode || "-" }}
</div>
</div>
</div>
</div>
</q-card-section>
</q-card>
</div>
</div>
<!-- Edit Dialog -->
<q-dialog v-model="modal" persistent>
<q-card>
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader tittle="แก้ไขข้อมูลที่อยู่" :close="clickClose" />
<q-separator />
<q-card-section>
<div class="col-12 q-pb-xs">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
type="textarea"
class="inputgreen"
v-model="formData.registrationAddress"
:label="dataLabel.registrationAddress"
:rules="[(val:string) => !!val || `${'กรุณากรอก ที่อยู่ตามทะเบียนบ้าน'}`]"
/>
</div>
<div class="row col-12 q-col-gutter-x-xs q-col-gutter-y-xs">
<div class="col-xs-6 col-sm-6 col-md-6">
<q-select
dense
outlined
use-input
lazy-rules
emit-value
map-options
hide-bottom-space
option-value="id"
option-label="name"
input-debounce="0"
class="inputgreen"
v-model="formData.registrationProvinceId"
:options="store.Ops.provinceOps"
:label="dataLabel.registrationProvince"
:rules="[(val:string) => !!val || `${'กรุณาเลือก จังหวัด'}`]"
@update:model-value="(value:string) => selectProvince(value, '1')"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'provinceOps'
) "
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-select
dense
outlined
use-input
lazy-rules
emit-value
map-options
hide-bottom-space
option-value="id"
option-label="name"
input-debounce="0"
class="inputgreen"
v-model="formData.registrationDistrictId"
:options="store.Ops.districtOps"
:label="dataLabel.registrationDistrict"
:rules="[(val:string) => !!val || `${'กรุณาเลือก เขต / อำเภอ'}`]"
@update:model-value="(value:string) => selectDistrict(value, '1')"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'districtOps'
) "
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-select
dense
outlined
use-input
lazy-rules
emit-value
map-options
hide-bottom-space
option-value="id"
option-label="name"
input-debounce="0"
class="inputgreen"
v-model="formData.registrationSubDistrictId"
:options="store.Ops.subdistrictOps"
:label="dataLabel.registrationSubDistrict"
:rules="[(val:string) => !!val || `${'กรุณาเลือก แขวง / ตำบล'}`]"
@update:model-value="(value:string) => selectSubDistrict(value, '1')"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'subdistrictOps'
) "
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
readonly
outlined
lazy-rules
hide-bottom-space
v-model="formData.registrationZipCode"
:label="dataLabel.registrationZipCode"
/>
</div>
</div>
<!-- same address ? -->
<div class="col-xs-12 q-gutter-sm items-center flex q-my-sm">
<label class="text-medium"
>อยจจนตรงกบทอยตามทะเบยนบาน</label
>
<q-radio
dense
val="1"
label="ใช่"
checked-icon="task_alt"
class="inputgreen"
v-model="sameAddress"
unchecked-icon="panorama_fish_eye"
/>
<q-radio
dense
val="0"
label="ไม่"
checked-icon="task_alt"
class="inputgreen"
v-model="sameAddress"
unchecked-icon="panorama_fish_eye"
/>
</div>
<!-- current address -->
<div v-if="sameAddress === '0'">
<div class="col-12 q-pb-xs">
<q-input
dense
outlined
lazy-rules
hide-bottom-space
type="textarea"
class="inputgreen"
v-model="formData.currentAddress"
:label="dataLabel.currentAddress"
:rules="[(val:string) => !!val || `${'กรุณากรอก ที่อยู่ปัจจุบัน'}`]"
/>
</div>
<div class="row col-12 q-col-gutter-x-xs q-col-gutter-y-xs">
<div class="col-xs-6 col-sm-6 col-md-6">
<q-select
dense
outlined
use-input
lazy-rules
emit-value
map-options
hide-bottom-space
option-value="id"
option-label="name"
input-debounce="0"
class="inputgreen"
v-model="formData.currentProvinceId"
:options="store.Ops.provinceOps"
:label="dataLabel.currentProvince"
:rules="[(val:string) => !!val || `${'กรุณาเลือก จังหวัด'}`]"
@update:model-value="(value:string) => selectProvince(value, '2')"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'provinceOps'
) "
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-select
dense
outlined
use-input
lazy-rules
emit-value
map-options
hide-bottom-space
option-value="id"
option-label="name"
input-debounce="0"
class="inputgreen"
v-model="formData.currentDistrictId"
:options="store.Ops.districtCOps"
:label="dataLabel.currentDistrict"
:rules="[(val:string) => !!val || `${'กรุณาเลือก เขต / อำเภอ'}`]"
@update:model-value="(value:string) => selectDistrict(value, '2')"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'districtCOps'
) "
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-select
dense
outlined
use-input
lazy-rules
emit-value
map-options
hide-bottom-space
option-value="id"
option-label="name"
input-debounce="0"
class="inputgreen"
v-model="formData.currentSubDistrictId"
:options="store.Ops.subdistrictCOps"
:label="dataLabel.currentSubDistrict"
:rules="[(val:string) => !!val || `${'กรุณาเลือก แขวง / ตำบล'}`]"
@update:model-value="(value:string) => selectSubDistrict(value, '2')"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(inputValue, doneFn,'subdistrictCOps'
) "
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
dense
readonly
outlined
lazy-rules
hide-bottom-space
v-model="formData.currentZipCode"
:label="dataLabel.registrationZipCode"
/>
</div>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn label="บันทึก" id="onSubmit" type="submit" color="public">
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<q-dialog v-model="modalHistory" persistent>
<q-card style="min-width: 80%">
<DialogHeader
tittle="ประวัติแก้ไขข้อมูลที่อยู่"
:close="() => (modalHistory = false)"
/>
<q-separator color="grey-4" />
<q-card-section style="max-height: 50vh" class="scroll">
<div class="row q-gutter-sm q-mb-sm">
<q-space />
<q-input
standout
dense
v-model="filterHistory"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="filterHistory == ''"
name="search"
@click.stop.prevent="filterHistory = ''"
class="cursor-pointer"
/>
<q-icon
v-if="filterHistory"
name="cancel"
@click.stop.prevent="filterHistory = ''"
class="cursor-pointer"
/>
</template>
</q-input>
<q-select
v-model="visibleColumnsHistory"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columnsHistory"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
<d-table
ref="table"
flat
bordered
dense
:columns="columnsHistory"
:rows="rowsHistory"
:paging="true"
:rows-per-page-options="[10, 25, 50, 100]"
:visible-columns="visibleColumnsHistory"
:filter="filterHistory"
>
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id">
<div>
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card-section>
</q-card>
</q-dialog>
</template>
<style scoped></style>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,670 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { QForm, useQuasar } from "quasar";
import dialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
import type { RequestItemsObject } from "@/modules/04_registryPerson/interface/request/SpecialSkill";
import type { ResponseObject } from "@/modules/04_registryPerson/interface/response/SpecialSkill";
import type { QTableProps } from "quasar";
import { useRoute } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
const mixin = useCounterMixin();
const $q = useQuasar();
const mode = ref<string>("table");
const {
dialogRemove,
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
date2Thai,
} = mixin;
const columns = ref<QTableProps["columns"]>([
{
name: "field",
align: "left",
label: "ด้าน",
sortable: true,
field: "field",
headerStyle: "font-size: 14px; width: 50px;",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "remark",
align: "left",
label: "หมายเหตุ",
sortable: true,
field: "remark",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reference",
align: "left",
label: "เอกสารอ้างอิง",
sortable: true,
field: "reference",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const historyColumns = ref<QTableProps["columns"]>([
{
name: "field",
align: "left",
label: "ด้าน",
sortable: true,
field: "field",
headerStyle: "font-size: 14px; width: 50px;",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "remark",
align: "left",
label: "หมายเหตุ",
sortable: true,
field: "remark",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reference",
align: "left",
label: "เอกสารอ้างอิง",
sortable: true,
field: "reference",
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: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
format: (v) => date2Thai(v),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const historyDialog = ref<boolean>(false);
const dialog = ref<boolean>(false);
const dialogStatus = ref<string>("create");
const rows = ref<ResponseObject[]>([]);
const historyRows = ref<ResponseObject[]>([]);
const route = useRoute();
const id = ref<string>(route.params.id.toString());
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const editId = ref<string>("");
const keyword = ref<string>("");
const historyKeyword = ref<string>("");
const specialSkill = reactive<RequestItemsObject>({
field: "",
detail: "",
remark: "",
reference: "",
profileId: id.value,
dateStart: null,
dateEnd: null,
});
const pagination = ref({
page: 1,
rowsPerPage: 10,
});
const historyPagination = ref({
page: 1,
rowsPerPage: 10,
});
const visibleColumns = ref<string[]>([
"field",
"detail",
"remark",
"reference",
]);
const historyVisibleColumns = ref<string[]>([
"field",
"detail",
"remark",
"reference",
"lastUpdateFullName",
"lastUpdatedAt",
]);
function closeDialog() {
dialog.value = false;
}
function closeHistoryDialog() {
historyDialog.value = false;
}
async function onSubmit() {
dialogConfirm(
$q,
async () => {
dialogStatus.value === "create" ? addData() : editData(editId.value);
closeDialog();
},
"ยืนยันการบันทึกข้อมูล",
"ต้องการยืนยันการบันทึกข้อมูลนี้หรือไม่ ?"
);
}
function clearForm() {
specialSkill.detail = "";
specialSkill.field = "";
specialSkill.reference = "";
specialSkill.remark = "";
}
function editForm(row: any) {
dialogStatus.value = "edit";
editId.value = row.id;
specialSkill.detail = row.detail;
specialSkill.field = row.field;
specialSkill.reference = row.reference;
specialSkill.remark = row.remark;
dialog.value = true;
}
async function fetchData(id: string) {
showLoader();
await http
.get(config.API.profileNewAbilityByProfileId(id, empType.value))
.then(async (res) => {
rows.value = res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function fetchHistoryData(id: string) {
showLoader();
await http
.get(config.API.profileNewAbilityHisByAbilityId(id, empType.value))
.then(async (res) => {
historyRows.value = res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function addData() {
await http
.post(config.API.profileNewAbility(empType.value), {
...specialSkill,
dateStart: null,
dateEnd: null,
profileId: empType.value === "" ? id.value : undefined,
profileEmployeeId: empType.value !== "" ? id.value : undefined,
})
.then(() => {
fetchData(id.value);
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function editData(idData: string) {
await http
.patch(config.API.profileNewAbilityByAbilityId(idData, empType.value), {
...specialSkill,
dateStart: null,
dateEnd: null,
profileId: undefined,
})
.then(() => {
fetchData(id.value);
success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
async function deleteData(idData: string) {
dialogRemove($q, () =>
http
.delete(config.API.profileNewAbilityByAbilityId(idData, empType.value))
.then(() => {
fetchData(id.value);
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
})
);
}
onMounted(async () => {
await fetchData(id.value);
});
</script>
<template>
<div class="row items-center q-gutter-x-sm q-pb-sm">
<q-btn
flat
round
color="primary"
dense
icon="add"
@click="() => ((dialogStatus = 'create'), clearForm(), (dialog = true))"
>
<q-tooltip>เพมขอม</q-tooltip>
</q-btn>
<q-space />
<q-input dense outlined v-model="keyword" label="ค้นหา">
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
v-if="mode === 'table'"
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-btn-toggle
v-model="mode"
dense
class="no-shadow toggle-borderd"
toggle-color="grey-4"
:options="[
{ value: 'table', slot: 'table' },
{ value: 'card', slot: 'card' },
]"
>
<template v-slot:table>
<q-icon
name="format_list_bulleted"
size="24px"
:style="{
color: mode === 'table' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
<template v-slot:card>
<q-icon
name="mdi-view-grid-outline"
size="24px"
:style="{
color: mode === 'card' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
</q-btn-toggle>
</div>
<d-table
:grid="mode === 'card'"
ref="table"
row-key="id"
flat
bordered
dense
:columns="columns"
:rows="rows"
:paging="true"
:filter="keyword"
v-model:pagination="pagination"
:rows-per-page-options="[20, 50, 100]"
:visible-columns="visibleColumns"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width />
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props" v-if="mode === 'table'">
<q-tr :props="props" class="cursor-pointer">
<q-td auto-width>
<q-btn
color="primary"
flat
dense
round
class="q-mr-xs"
size="14px"
icon="mdi-pencil-outline"
clickable
@click="
() => {
editForm(props.row);
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click="
fetchHistoryData(props.row.id);
historyDialog = true;
"
>
<q-tooltip>ประวแกไขความสามารถพเศษ</q-tooltip>
</q-btn>
</q-td>
<q-td v-for="col in props.cols" :key="col.id">
<div>{{ col.value ? col.value : "-" }}</div>
</q-td>
</q-tr>
</template>
<template v-slot:item="props" v-else>
<div
class="q-pa-xs col-xs-12 col-sm-6 col-md-6 col-lg-6 grid-style-transition"
>
<q-card bordered>
<q-card-actions align="right" class="bg-grey-3">
<q-btn
flat
round
color="primary"
icon="mdi-pencil-outline"
size="14px"
@click="
() => {
editForm(props.row);
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
flat
class="no-shadow toggle-borderd"
round
size="14px"
color="info"
icon="mdi-history"
@click="
fetchHistoryData(props.row.id);
historyDialog = true;
"
>
<q-tooltip>ประวแกไขความสามารถพเศษ</q-tooltip>
</q-btn>
</q-card-actions>
<q-separator />
<div class="row">
<div class="col q-pa-sm label-color">าน</div>
<div class="col q-pa-sm">{{ props.cols[0].value }}</div>
<div class="col q-pa-sm label-color">รายละเอยด</div>
<div class="col q-pa-sm">{{ props.cols[1].value }}</div>
</div>
<div :class="`row bg-color`">
<div class="col q-pa-sm label-color">หมายเหต</div>
<div class="col q-pa-sm">{{ props.cols[2].value }}</div>
<div class="col q-pa-sm label-color">เอกสารอางอ</div>
<div class="col q-pa-sm">{{ props.cols[3].value }}</div>
</div>
</q-card>
</div>
</template>
</d-table>
<q-dialog v-model="dialog" class="dialog" persistent>
<q-card>
<q-form @submit.prevent greedy @validation-success="onSubmit()">
<dialog-header
:tittle="
dialogStatus == 'edit'
? 'แก้ไขข้อมูลความสามารถพิเศษ'
: 'เพิ่มข้อมูลความสามารถพิเศษ'
"
:close="closeDialog"
/>
<q-separator />
<q-card-section>
<div class="row col-12 q-col-gutter-x-xs q-col-gutter-y-xs">
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
outlined
class="inputgreen"
dense
bg-color="white"
v-model="specialSkill.field"
label="ด้าน"
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกด้านความสามารถพิเศษ'}`]"
/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
<q-input
outlined
dense
class="inputgreen"
bg-color="white"
v-model="specialSkill.detail"
label="รายละเอียด"
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกรายละเอียด'}`]"
/>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<q-input
class="inputgreen"
outlined
dense
bg-color="white"
v-model="specialSkill.remark"
label="หมายเหตุ"
/>
</div>
<div class="col-xs-12 col-sm-12 col-md-12">
<q-input
class="inputgreen"
outlined
dense
bg-color="white"
v-model="specialSkill.reference"
label="เอกสารอ้างอิง"
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn label="บันทึก" id="onSubmit" type="submit" color="public">
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<q-dialog v-model="historyDialog" class="dialog" persistent>
<q-layout
view="lHh lpr lFf"
container
style="height: 500px; min-width: 80%"
class="bg-white"
>
<q-header>
<q-toolbar>
<dialog-header
tittle="ประวัติแก้ไขความสามารถพิเศษ"
:close="closeHistoryDialog"
/>
</q-toolbar>
<q-separator color="grey-4" />
</q-header>
<q-page-container>
<q-page class="q-pa-md">
<q-toolbar style="padding: 0px" class="text-primary q-mb-sm">
<q-space />
<q-input
dense
outlined
bg-color="white"
v-model="historyKeyword"
label="ค้นหา"
class="q-mr-sm"
>
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
<q-select
v-model="historyVisibleColumns"
multiple
outlined
dense
bg-color="white"
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="historyColumns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</q-toolbar>
<d-table
ref="table"
:columns="historyColumns"
:rows="historyRows"
row-key="name"
flat
bordered
:paging="true"
dense
:filter="historyKeyword"
v-model:pagination="historyPagination"
:rows-per-page-options="[20, 50, 100]"
class="custom-header-table"
:visible-columns="historyVisibleColumns"
>
<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" v-if="mode === 'table'">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.id">
<div>{{ col.value ? col.value : "-" }}</div>
</q-td>
<q-td auto-width> </q-td>
</q-tr>
</template>
</d-table>
</q-page>
</q-page-container>
</q-layout>
</q-dialog>
</template>
<style scoped>
.label-color {
color: #747474cc;
}
.bg-color {
background-color: #fafafa;
}
</style>

View file

@ -0,0 +1,74 @@
<script setup lang="ts">
import { ref } from "vue";
/** importComponents*/
import Profile from "@/modules/04_registryPerson/components/detail/PersonalInformation/01_Profile.vue";
import NameChangeHistory from "@/modules/04_registryPerson/components/detail/PersonalInformation/02_NameChangeHistory.vue";
import Address from "@/modules/04_registryPerson/components/detail/PersonalInformation/03_Address.vue";
import Family from "@/modules/04_registryPerson/components/detail/PersonalInformation/04_Family.vue";
import Education from "@/modules/04_registryPerson/components/detail/PersonalInformation/05_Education.vue";
import SpecialSkill from "@/modules/04_registryPerson/components/detail/PersonalInformation/06_SpecialSkill.vue";
import FamilyNew from "@/modules/04_registryPerson/components/detail/PersonalInformation/04_FamilyNew.vue";
const tab = ref<string>("1");
const props = defineProps({
fetchDataPersonal: { type: Function, require: true },
});
</script>
<template>
<div class="row items-center q-my-md">
<div class="text-dark row items-center q-px-md">
<q-icon name="mdi-account" class="q-mr-md" size="22px" />
<div class="text-subtitle1 text-weight-bold">อมลสวนต</div>
</div>
</div>
<q-separator />
<q-card>
<q-tabs
v-model="tab"
active-color="blue-8"
align="left"
bordered
narrow-indicator
indicator-color="transparent"
dense
class="text-grey q-pl-sm"
>
<q-tab name="1" label="ประวัติส่วนตัว" />
<q-tab name="2" label="ประวัติการเปลี่ยนชื่อ-นามสกุล" />
<q-tab name="3" label="ข้อมูลที่อยู่" />
<q-tab name="4" label="ข้อมูลครอบครัว" />
<q-tab name="5" label="ประวัติการศึกษา" />
<q-tab name="6" label="ความสามารถพิเศษ" />
</q-tabs>
<q-separator />
<q-tab-panels v-model="tab" animated>
<q-tab-panel name="1">
<Profile :fetchDataPersonal="props.fetchDataPersonal" />
</q-tab-panel>
<q-tab-panel name="2">
<NameChangeHistory :fetchDataPersonal="props.fetchDataPersonal" />
</q-tab-panel>
<q-tab-panel name="3">
<Address />
</q-tab-panel>
<q-tab-panel name="4">
<FamilyNew />
</q-tab-panel>
<q-tab-panel name="5">
<Education />
</q-tab-panel>
<q-tab-panel name="6">
<SpecialSkill />
</q-tab-panel>
<!-- <q-tab-panel name="7">
<FamilyNew />
</q-tab-panel> -->
</q-tab-panels>
</q-card>
</template>
<style scoped></style>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,348 @@
<script setup lang="ts">
import { ref, watch, computed } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import { useRoute } from "vue-router";
import type { QTableProps } from "quasar";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
const $q = useQuasar();
const route = useRoute();
const {
date2Thai,
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
} = useCounterMixin();
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const modal = defineModel<boolean>("modal", { required: true });
const salaryId = defineModel<string>("salaryId", { required: true });
const baseColumns = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วัน เดือน ปี",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
},
{
name: "amount",
align: "left",
label: empType.value === "-employee" ? "ค่าตอบแทนรายเดือน" : "เงินเดือน",
sortable: true,
field: "amount",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => Number(v).toLocaleString(),
},
{
name: "positionSalaryAmount",
align: "left",
label: "เงินประจำตำแหน่ง",
sortable: true,
field: "positionSalaryAmount",
headerStyle: "font-size: 14px",
format: (v) => Number(v).toLocaleString(),
style: "font-size: 14px",
},
{
name: "mouthSalaryAmount",
align: "left",
label: "เงินค่าตอบแทนรายเดือน",
sortable: true,
field: "mouthSalaryAmount",
headerStyle: "font-size: 14px",
format: (v) => Number(v).toLocaleString(),
style: "font-size: 14px",
},
{
name: "oc",
align: "left",
label: "สังกัด",
sortable: true,
field: "oc",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "position",
align: "left",
label: "ตำแหน่ง",
sortable: true,
field: "position",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posNo",
align: "left",
label: "ตำแหน่งเลขที่",
sortable: true,
field: "posNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionLine",
align: "left",
label: "สายงาน",
sortable: true,
field: "positionLine",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionPathSide",
align: "left",
label: "ด้าน/สาขา",
sortable: true,
field: "positionPathSide",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionType",
align: "left",
label: "ตำแหน่งประเภท",
sortable: true,
field: "positionType",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionLevel",
align: "left",
label: "ระดับ",
sortable: true,
field: "positionLevel",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionExecutive",
align: "left",
label: "ตำแหน่งทางการบริหาร",
sortable: true,
field: "positionExecutive",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "positionExecutiveSide",
align: "left",
label: "ด้านทางการบริหาร",
sortable: true,
field: "positionExecutiveSide",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "salaryClass",
align: "left",
label: "ตำแหน่ง (รายละเอียด)",
sortable: true,
field: "salaryClass",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "templateDoc",
align: "left",
label: "เอกสารอ้างอิง",
sortable: true,
field: "templateDoc",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
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",
format: (v) => date2Thai(v),
},
]);
const visibleColumns = ref<string[]>([
"date",
"amount",
"positionSalaryAmount",
"mouthSalaryAmount",
"oc",
"position",
"posNo",
"positionLine",
"positionPathSide",
"positionType",
"positionLevel",
"positionExecutive",
"positionExecutiveSide",
"salaryClass",
"templateDoc",
"refCommandNo",
"lastUpdateFullName",
"lastUpdatedAt",
]);
const columns = computed(() => {
if (empType.value === "-employee") {
if (baseColumns.value) {
return baseColumns.value.filter(
(column) =>
column.name !== "positionSalaryAmount" &&
column.name !== "mouthSalaryAmount"
);
}
}
return baseColumns.value;
});
const rows = ref<any[]>([]);
const pagination = ref({
page: 1,
rowsPerPage: 10,
});
const keyword = ref<string>("");
function closeDialog() {
modal.value = false;
}
function fetchListHistory() {
showLoader();
http
.get(config.API.profileListSalaryHistoryNew(salaryId.value, empType.value))
.then((res) => {
rows.value = res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
watch(
() => modal.value,
() => {
modal.value && fetchListHistory();
}
);
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="min-width: 80%">
<DialogHeader
:tittle="'ประวัติแก้ไขตำแหน่ง/เงินเดือน'"
:close="closeDialog"
/>
<q-separator color="grey-4" />
<q-card-section style="max-height: 60vh" class="scroll">
<div class="row q-gutter-sm q-mb-sm">
<q-space />
<q-input
dense
outlined
v-model="keyword"
label="ค้นหา"
class="q-mr-sm"
>
<template v-slot:append>
<q-icon v-if="keyword === ''" name="search" />
<q-icon
v-else
name="clear"
class="cursor-pointer"
@click="keyword = ''"
/>
</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>
<d-table
ref="table"
row-key="id"
flat
bordered
dense
:filter="keyword"
:columns="columns"
:rows="rows"
:paging="true"
v-model:pagination="pagination"
: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-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols">
<div class="table_ellipsis">
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card-section>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -0,0 +1,581 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { useQuasar } from "quasar";
import { useRoute } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import DialogHeader from "@/components/DialogHeader.vue";
import DialogHisotory from "@/modules/04_registryPerson/components/detail/Salary/02_NotReceiveSalaryHistory.vue";
import type { QTableProps } from "quasar";
import type { RowList } from "@/modules/04_registryPerson/interface/index/salary";
import type { RequestNoPaidObject } from "@/modules/04_registryPerson/interface/request/Salary";
const $q = useQuasar();
const route = useRoute();
const {
date2Thai,
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
dialogRemove,
} = useCounterMixin();
const id = ref<string>("");
const profileId = ref<string>(
route.params.id ? route.params.id.toString() : ""
);
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const modelView = ref<string>("table");
const modalDialog = ref<boolean>(false);
const modalHistory = ref<boolean>(false);
const isStatusEdit = ref<boolean>(false);
const rows = ref<RowList[]>([]);
const columns = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วัน เดือน ปี",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reference",
align: "left",
label: "เอกสารอ้างอิง",
sortable: true,
field: "reference",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumns = ref<string[]>([
"date",
"detail",
"reference",
"refCommandNo",
"refCommandDate",
]);
const formData = reactive<RequestNoPaidObject>({
date: null,
reference: "",
detail: "",
refCommandNo: "",
refCommandDate: null,
});
const formFilter = reactive({
page: 1,
pageSize: 10,
keyword: "",
});
const maxPage = ref<number>(1);
const pagination = ref({
page: 1,
rowsPerPage: 10,
});
function onSubmit() {
dialogConfirm($q, () => {
isStatusEdit.value ? editData() : saveData();
onClickCloseDialog();
});
}
function onClickOpenDialog(StatusEdit: boolean = false, data: any = []) {
isStatusEdit.value = StatusEdit;
id.value = StatusEdit ? data.id : "";
formData.date = StatusEdit ? data.date : null;
formData.reference = StatusEdit ? data.reference : "";
formData.detail = StatusEdit ? data.detail : "";
formData.refCommandNo = StatusEdit ? data.refCommandNo : "";
formData.refCommandDate = StatusEdit ? data.refCommandDate : null;
modalDialog.value = true;
}
function onClickCloseDialog() {
modalDialog.value = false;
isStatusEdit.value = false;
}
async function getData() {
showLoader();
await http
.get(config.API.profileNewNoPaidByProfileId(profileId.value, empType.value))
.then((res) => {
rows.value = res.data.result;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
async function saveData() {
showLoader();
await http
.post(config.API.profileNewNoPaid(empType.value), {
...formData,
profileId: empType.value === "" ? profileId.value : undefined,
profileEmployeeId: empType.value !== "" ? profileId.value : undefined,
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
getData();
onClickCloseDialog();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
async function editData() {
showLoader();
await http
.patch(config.API.profileNewNoPaidById(id.value, empType.value), {
...formData,
profileId: undefined,
})
.then((res) => {
success($q, "บันทึกข้อมูลสำเร็จ");
getData();
onClickCloseDialog();
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
// async function deleteData(id: string) {
// showLoader();
// await http
// .delete(config.API.profileNewNoPaidById(id))
// .then((res) => {
// success($q, "");
// getData();
// })
// .catch((e) => {
// messageError($q, e);
// })
// .finally(() => {
// hideLoader();
// });
// }
function onClickHistory(rowId: string) {
id.value = rowId;
modalHistory.value = true;
}
onMounted(() => {
getData();
});
</script>
<template>
<div class="row items-center q-gutter-x-sm q-pb-sm">
<q-btn
dense
color="teal-5"
icon="add"
flat
round
@click="onClickOpenDialog()"
><q-tooltip>เพมขอม</q-tooltip></q-btn
>
<q-space />
<q-input
standout
dense
v-model="formFilter.keyword"
ref="filterRef"
outlined
placeholder="ค้นหา"
debounce="300"
>
<template v-slot:append>
<q-icon
v-if="formFilter.keyword == ''"
name="search"
@click.stop.prevent="formFilter.keyword = ''"
class="cursor-pointer"
/>
<q-icon
v-if="formFilter.keyword"
name="cancel"
@click.stop.prevent="formFilter.keyword = ''"
class="cursor-pointer"
/>
</template>
</q-input>
<q-select
v-if="modelView == 'table'"
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-btn-toggle
v-model="modelView"
dense
class="no-shadow toggle-borderd"
toggle-color="grey-4"
:options="[
{ value: 'table', slot: 'table' },
{ value: 'card', slot: 'card' },
]"
>
<template v-slot:table>
<q-icon
name="format_list_bulleted"
size="24px"
:style="{
color: modelView === 'table' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
<template v-slot:card>
<q-icon
name="mdi-view-grid-outline"
size="24px"
:style="{
color: modelView === 'card' ? '#787B7C' : '#C9D3DB',
}"
/>
</template>
</q-btn-toggle>
</div>
<d-table
ref="table"
row-key="id"
flat
bordered
dense
:columns="columns"
:rows="rows"
:paging="true"
:filter="formFilter.keyword"
v-model:pagination="pagination"
:rows-per-page-options="[20, 50, 100]"
:visible-columns="visibleColumns"
:grid="modelView === 'card'"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width />
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props" v-if="modelView === 'table'">
<q-tr :props="props" class="cursor-pointer">
<q-td auto-width>
<q-btn
flat
dense
round
class="q-mr-xs"
size="14px"
color="primary"
icon="mdi-pencil-outline"
@click="onClickOpenDialog(true, props.row)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="info"
flat
dense
round
size="14px"
icon="mdi-history"
@click.stop.prevent="onClickHistory(props.row.id)"
>
<q-tooltip>ประวนทกวนทไมไดบเงนเดอนฯ</q-tooltip>
</q-btn>
<!-- <q-btn
flat
dense
round
size="14px"
color="red"
icon="mdi-delete"
@click="
dialogRemove($q, async () => await deleteData(props.row.id))
"
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn> -->
</q-td>
<q-td v-for="col in props.cols" :key="col.id">
<div>{{ col.value ? col.value : "-" }}</div>
</q-td>
</q-tr>
</template>
<template v-slot:item="props" v-else>
<div
class="q-pa-xs col-xs-12 col-sm-6 col-md-6 col-lg-3 grid-style-transition"
>
<q-card bordered>
<q-card-actions class="bg-grey-3" align="right">
<q-btn
flat
round
color="primary"
icon="mdi-pencil-outline"
@click="onClickOpenDialog(true, props.row)"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
flat
round
color="info"
icon="history"
@click="onClickHistory(props.row.id)"
>
<q-tooltip>ประวนทกวนทไมไดบเงนเดอนฯ</q-tooltip>
</q-btn>
</q-card-actions>
<q-separator />
<q-list>
<q-item
v-for="(col, index) in props.cols.filter(
(col) => col.name !== 'desc'
)"
:key="col.name"
:class="index % 2 !== 0 ? 'bg-grey-1' : ''"
>
<q-item-section class="text-grey-6">
<q-item-label>{{ col.label }}</q-item-label>
</q-item-section>
<q-item-section class="text-dark">
<q-item-label>{{ col.value ? col.value : "-" }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-card>
</div>
</template>
</d-table>
<q-dialog v-model="modalDialog" persistent>
<q-card>
<q-form @submit.prevent greedy @validation-success="onSubmit">
<DialogHeader
:tittle="
isStatusEdit
? 'แก้ไขบันทึกวันที่ไม่ได้รับเงินเดือนฯ'
: 'เพิ่มบันทึกวันที่ไม่ได้รับเงินเดือนฯ'
"
:close="onClickCloseDialog"
/>
<q-separator />
<q-card-section>
<div class="row col-12 q-col-gutter-sm">
<div class="col-xs-12 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
v-model="formData.date"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
ref="dateRef"
outlined
dense
borderless
class="inputgreen"
:model-value="date2Thai(formData.date)"
:rules="[(val) => !!val || `${'กรุณาเลือก วัน/เดือน/ปี'}`]"
hide-bottom-space
:label="`${'วัน/เดือน/ปี'}`"
>
<template v-slot:prepend>
<q-icon
name="event"
class="cursor-pointer"
style="color: var(--q-primary)"
>
</q-icon>
</template>
</q-input>
</template>
</datepicker>
</div>
<div class="col-xs-12 col-sm-6 col-md-6">
<q-input
ref="referenceRef"
outlined
dense
autogrow
lazy-rules
borderless
v-model="formData.reference"
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกเอกสารอ้างอิง'}`]"
hide-bottom-space
:label="`${'เอกสารอ้างอิง'}`"
/>
</div>
<div class="col-12">
<q-input
ref="detailRef"
outlined
dense
autogrow
lazy-rules
borderless
v-model="formData.detail"
class="inputgreen"
:rules="[(val) => !!val || `${'กรุณากรอกรายละเอียด'}`]"
hide-bottom-space
:label="`${'รายละเอียด'}`"
/>
</div>
<div class="col-xs-12 col-sm-6 col-md-6">
<q-input
outlined
dense
lazy-rules
borderless
v-model="formData.refCommandNo"
class="inputgreen"
hide-bottom-space
:label="`${'เลขที่คำสั่ง'}`"
>
<template v-slot:append>
<q-icon name="mdi-file" class="cursor-pointer" />
</template>
</q-input>
</div>
<div class="col-xs-12 col-sm-6 col-md-6">
<datepicker
menu-class-name="modalfix"
v-model="formData.refCommandDate"
:locale="'th'"
autoApply
:enableTimePicker="false"
week-start="0"
>
<template #year="{ year }">{{ year + 543 }}</template>
<template #year-overlay-value="{ value }">{{
parseInt(value + 543)
}}</template>
<template #trigger>
<q-input
clearable
outlined
dense
borderless
class="inputgreen"
:model-value="
formData.refCommandDate == null ? null : date2Thai(formData.refCommandDate as Date)
"
hide-bottom-space
:label="`${'เอกสารอ้างอิง (ลงวันที่)'}`"
@clear="formData.refCommandDate = null"
>
<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-separator />
<q-card-actions align="right">
<q-btn label="บันทึก" id="onSubmit" type="submit" color="public">
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
<DialogHisotory v-model:modal="modalHistory" v-model:id="id" />
</template>
<style scoped></style>

View file

@ -0,0 +1,226 @@
<script setup lang="ts">
import { ref, watch } from "vue";
import { useQuasar } from "quasar";
import { useCounterMixin } from "@/stores/mixin";
import { useRoute } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
import type { QTableProps } from "quasar";
import type { RowList } from "@/modules/04_registryPerson/interface/index/salary";
import DialogHeader from "@/components/DialogHeader.vue";
const route = useRoute();
const $q = useQuasar();
const mixin = useCounterMixin();
const { showLoader, hideLoader, messageError, date2Thai } = mixin;
const empType = ref<string>(
route.name === "registryNewByid" ? "" : "-employee"
);
const modal = defineModel<boolean>("modal", { required: true });
const id = defineModel<string>("id", { required: true });
const filter = ref<string>("");
const rows = ref<RowList[]>([]);
const columns = ref<QTableProps["columns"]>([
{
name: "date",
align: "left",
label: "วัน เดือน ปี",
sortable: true,
field: "date",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "reference",
align: "left",
label: "เอกสารอ้างอิง",
sortable: true,
field: "reference",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandNo",
align: "left",
label: "เลขที่คำสั่ง",
sortable: true,
field: "refCommandNo",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "refCommandDate",
align: "left",
label: "เอกสารอ้างอิง (ลงวันที่)",
sortable: true,
field: "refCommandDate",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
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: "lastUpdatedAt",
align: "left",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
format: (v) => date2Thai(v),
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
]);
const visibleColumns = ref<string[]>([
"date",
"reference",
"detail",
"refCommandNo",
"refCommandDate",
"lastUpdateFullName",
"lastUpdatedAt",
]);
const pagination = ref({
page: 1,
rowsPerPage: 10,
});
function getHistory() {
showLoader();
http
.get(config.API.profileNewNoPaidHisById(id.value, empType.value))
.then((res) => {
rows.value = res.data.result;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
function closeDialog() {
modal.value = false;
}
watch(modal, (status) => {
if (status == true) {
getHistory();
filter.value = "";
} else {
filter.value = "";
}
});
</script>
<template>
<q-dialog v-model="modal" persistent full-width>
<q-card style="min-width: 80%">
<DialogHeader
:tittle="'ประวัติแก้ไขบันทึกวันที่ไม่ได้รับเงินเดือนฯ'"
:close="closeDialog"
/>
<q-separator color="grey-4" />
<q-card-section style="max-height: 60vh" class="scroll">
<div class="row q-gutter-sm q-mb-sm">
<q-space />
<q-input
dense
outlined
v-model="filter"
label="ค้นหา"
class="q-mr-sm"
>
<template v-slot:append>
<q-icon name="search" />
</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>
<d-table
ref="table"
row-key="id"
flat
bordered
dense
:filter="filter"
:columns="columns"
:rows="rows"
:paging="true"
v-model:pagination="pagination"
:rows-per-page-options="[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-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div class="table_ellipsis">
{{ col.value ? col.value : "-" }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card-section>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -0,0 +1,44 @@
<script setup lang="ts">
import { ref } from "vue";
/** importComponents*/
import PositionSalary from "@/modules/04_registryPerson/components/detail/Salary/01_PositionSalary.vue";
import NotReceiveSalary from "@/modules/04_registryPerson/components/detail/Salary/02_NotReceiveSalary.vue";
const tab = ref<string>("1");
</script>
<template>
<div class="row items-center q-my-md">
<div class="text-dark row items-center q-px-md">
<q-icon name="mdi-account" class="q-mr-md" size="22px" />
<div class="text-subtitle1 text-weight-bold">อมลเงนเดอน/าจาง</div>
</div>
</div>
<q-separator />
<q-tabs
v-model="tab"
active-color="blue-8"
align="left"
bordered
narrow-indicator
indicator-color="transparent"
dense
class="text-grey q-pl-sm"
>
<q-tab name="1" label="ตำแหน่ง/เงินเดือน" />
<q-tab name="2" label="บันทึกวันที่ไม่ได้รับเงินเดือนฯ" />
</q-tabs>
<q-separator />
<q-tab-panels v-model="tab" animated>
<q-tab-panel name="1">
<PositionSalary />
</q-tab-panel>
<q-tab-panel name="2">
<NotReceiveSalary />
</q-tab-panel>
</q-tab-panels>
</template>
<style scoped></style>

View file

@ -0,0 +1,124 @@
<script setup lang="ts">
import { ref } from "vue";
import { useRoute } from "vue-router";
import { useRegistryDetailNewDataStore } from "@/modules/04_registryPerson/stores/DetailMain";
import PersonalInformationMain from "@/modules/04_registryPerson/components/detail/PersonalInformation/Main.vue";
import GovernmentInformationMain from "@/modules/04_registryPerson/components/detail/GovernmentInformation/Main.vue";
import salaryMain from "@/modules/04_registryPerson/components/detail/Salary/Main.vue";
import AchievementMain from "@/modules/04_registryPerson/components/detail/Achievement/Main.vue";
import OtherMaim from "@/modules/04_registryPerson/components/detail/Other/Main.vue";
import EmployeeMain from "@/modules/04_registryPerson/components/detail/Employee/Main.vue";
const route = useRoute();
const store = useRegistryDetailNewDataStore();
const props = defineProps({
fetchDataPersonal: { type: Function, require: true },
});
const itemsTab = ref<any>([
{
name: "1",
icon: "mdi-account",
label: "ข้อมูลส่วนตัว",
},
{
name: "2",
icon: "mdi-account-tie",
label:
route.name === "registry-employeeId"
? "ข้อมูลลูกจ้างชั่วคราว"
: "ข้อมูลราชการ",
},
{
name: "3",
icon: "mdi-cash",
label: "เงินเดือน/ค่าจ้าง",
},
{
name: "4",
icon: "mdi-medal",
label: "ข้อมูลผลงาน",
},
{
name: "5",
icon: "mdi-bookmark",
label: "ข้อมูลอื่นๆ",
},
]);
const splitterModel = ref<number>(12);
</script>
<template>
<q-splitter v-model="splitterModel" disable class="split">
<template v-slot:before>
<div class="">
<!-- bg-tab-regi -->
<q-tabs
v-model="store.tabMain"
vertical
class="text-grey-6 text-weight-light"
active-class="bg-white text-blue-8 text-weight-bold bg-blue-1"
><!-- indicator-color="transparent" -->
<q-tab
class="q-py-sm"
v-for="(tab, index) in itemsTab"
:key="index"
:name="tab.name"
:icon="tab.icon"
:label="tab.label"
/><!-- hover-tab -->
</q-tabs>
</div>
</template>
<template v-slot:after>
<q-tab-panels
v-model="store.tabMain"
animated
swipeable
vertical
transition-prev="jump-up"
transition-next="jump-up"
class="q-pa-none"
><!-- split -->
<q-tab-panel
v-for="(tab, index) in itemsTab"
:key="index"
:name="tab.name"
class="q-pa-none"
>
<PersonalInformationMain
v-if="store.tabMain === '1'"
:fetchDataPersonal="props.fetchDataPersonal"
/>
<EmployeeMain
v-if="store.tabMain === '2' && route.name === 'registry-employeeId'"
/>
<GovernmentInformationMain v-else-if="store.tabMain === '2'" />
<salaryMain v-if="store.tabMain === '3'" />
<AchievementMain v-if="store.tabMain === '4'" />
<OtherMaim v-if="store.tabMain === '5'" />
</q-tab-panel>
</q-tab-panels>
</template>
</q-splitter>
</template>
<style scoped>
.hover-tab:hover {
background-color: #0793f1;
color: white !important;
opacity: 1 !important;
}
.bg-tab-regi {
background-color: #273238;
}
.split {
border-radius: 10px !important;
}
</style>

View file

@ -0,0 +1,184 @@
<script setup lang="ts">
import { reactive, ref, watch } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import type { DataOption } from "@/modules/04_registryPerson/interface/index/Main";
/**
* importComponents
*/
import DialogHeader from "@/components/DialogHeader.vue";
/**
* importStore
*/
import { useRequestEditStore } from "@/modules/04_registryPerson/stores/RequestEdit";
import { useCounterMixin } from "@/stores/mixin";
/**
* use
*/
const $q = useQuasar();
const store = useRequestEditStore();
const { dialogConfirm, showLoader, hideLoader, messageError, success } =
useCounterMixin();
/**
* props
*/
const modal = defineModel<boolean>("modal", { required: true });
const requestId = defineModel<string>("requestId", { required: true });
const props = defineProps({
fetchData: { type: Function, requied: true },
});
const isReadOnly = ref<boolean>(false);
const formData = reactive({
status: "",
remark: "",
});
const statusOptionMain = ref<DataOption[]>(
store.optionStatus.filter((e) => e.id !== "")
);
const statusOption = ref<DataOption[]>(statusOptionMain.value);
function onSubmit() {
dialogConfirm($q, () => {
showLoader();
http
.patch(config.API.requestEdit + `${requestId.value}`, {
status: formData.status,
remark: formData.remark,
})
.then(async () => {
await props.fetchData?.();
closeDialog();
await success($q, "บันทึกข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
hideLoader();
});
});
}
function closeDialog() {
modal.value = false;
formData.status = "";
formData.remark = "";
}
function classInput(val: boolean) {
return {
"full-width cursor-pointer ": val,
"full-width cursor-pointer inputgreen": !val,
};
}
function filterOption(val: string, update: Function) {
update(() => {
statusOption.value = statusOptionMain.value.filter(
(v: any) => v.name.indexOf(val) > -1
);
});
}
function fetchDataRequest() {
showLoader();
http
.get(config.API.requestEdit + `${requestId.value}`)
.then((res) => {
const data = res.data.result;
formData.status = data.status;
formData.remark = data.remark;
if (data.status !== "PENDING") {
isReadOnly.value = true;
} else {
isReadOnly.value = false;
}
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
watch(
() => modal.value,
() => {
modal.value && fetchDataRequest();
}
);
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="width: 700px; max-width: 80vw">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader tittle="แก้ไขสถานะคำร้อง" :close="closeDialog" />
<q-separator />
<q-card-section>
<div class="row q-col-gutter-sm">
<div class="col-xs-12 col-md-12">
<q-select
:class="classInput(isReadOnly)"
:readonly="isReadOnly"
v-model="formData.status"
label="สถานะ"
dense
outlined
emit-value
map-options
:options="statusOption"
:rules="[(val:string) => !!val || `${'กรุณาเลือกสถานะ'}`]"
lazy-rules
hide-bottom-space
use-input
option-label="name"
option-value="id"
@filter="(inputValue:string,
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-xs-12 col-md-12">
<q-input
:class="classInput(isReadOnly)"
:readonly="isReadOnly"
v-model="formData.remark"
label="หมายเหตุ"
dense
outlined
type="textarea"
hide-bottom-space
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right" v-if="!isReadOnly">
<q-btn label="บันทึก" color="secondary" type="submit"
><q-tooltip>นท</q-tooltip></q-btn
>
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -0,0 +1,58 @@
interface Pagination {
rowsPerPage: number;
}
interface DataOption {
id: string;
name: string;
}
interface DataOption2 {
id: number;
name: string;
}
interface DataOptionInsignia {
id: string;
name: string;
typeName: string;
}
interface zipCodeOption {
id: string;
name: string;
zipCode: string;
}
interface InformationOps {
prefixOps: DataOption[];
rankOps: DataOption[];
genderOps: DataOption[];
bloodOps: DataOption[];
statusOps: DataOption[];
religionOps: DataOption[];
employeeClassOps: DataOption[];
employeeTypeOps: DataOption[];
}
interface AddressOps {
provinceOps: DataOption[];
districtOps: DataOption[];
districtCOps: DataOption[];
subdistrictOps: zipCodeOption[];
subdistrictCOps: zipCodeOption[];
}
interface InsigniaOps {
insigniaOptions: DataOptionInsignia[];
}
export type {
Pagination,
DataOption,
DataOption2,
DataOptionInsignia,
zipCodeOption,
InformationOps,
AddressOps,
InsigniaOps,
};

View file

@ -0,0 +1,41 @@
interface RequestItemsObject {
profileId: string,
id?: string;
level: string;
detail: string;
unStigma: string;
refCommandNo: string;
refCommandDate: Date | null;
date: Date | null;
}
interface FormFilter {
page: number;
pageSize: number;
keyword: string;
type: string;
posType: string;
posLevel: string;
retireYear: string | null;
rangeYear: { min: number; max: number };
isShowRetire: boolean;
isProbation: boolean;
}
interface DataOption {
id: string;
name: string;
}
interface DisciplineOps {
levelOptions: DataOption[];
}
interface MyObjectRef {
date: object | null;
detail: object | null;
refCommandNo: object | null;
[key: string]: any;
}
export type { RequestItemsObject,FormFilter,DataOption,DisciplineOps,MyObjectRef };

View file

@ -0,0 +1,9 @@
interface ArrayFileList {
id:string
pathName:string
fileName:string
}
export type {
ArrayFileList
}

View file

@ -0,0 +1,30 @@
interface FormPerson {
isLive: null | number | boolean | string;
citizenId: string;
prefix: string;
firstName: string;
lastName: string;
job: string;
lastNameOld?: string;
statusMarital?: string;
}
interface FormChildren {
id: string;
createdAt: string;
createdUserId: string;
lastUpdatedAt: string;
lastUpdateUserId: string;
createdFullName: string;
lastUpdateFullName: string;
childrenCareer: string;
childrenFirstName: string;
childrenLastName: string;
childrenPrefix: string;
childrenLive: boolean | number | null;
childrenCitizenId: string;
profileId: string | null;
profileEmployeeId: string | null;
}
export type { FormPerson, FormChildren };

View file

@ -0,0 +1,19 @@
interface FormData {
type: string,
citizenId: string,
fullName: string,
retireYear: number | null,
year: number | null,
posPath: string,
posLevel: string,
posOc: string,
isShowRetire: boolean,
isProbation: boolean,
}
interface YearRange {
min:number,
max:number
}
export type { FormData , YearRange}

View file

@ -0,0 +1,50 @@
interface FormMain {
ocId: string;
positionId: string;
positionLine: string;
positionLevel: string;
numberId: string;
positionExecutive: string;
positionExecutiveSide: string;
positionType: string;
positionPathSide: string;
containDate: any;
workDate: any;
reasonSameDate: string;
retireDate: any;
ageAll: GovAgeForm;
absent: number;
age: number;
[key: string]: any;
}
interface GovAgeForm {
year: number;
month: number;
day: number;
}
interface RequestItemsHistoryObject {
lastUpdatedAt: Date;
lastUpdateFullName: string;
oc: string | null;
position: string | null;
positionPathSide: string | null;
posNo: string | null;
positionLine: string | null;
positionType: string | null;
positionLevel: string | null;
positionExecutive: string | null;
positionExecutiveSide: string | null;
dateAppoint: Date;
dateStart: Date;
dateRetire: string | null;
dateRetireLaw: string | null;
govAge: string | null;
govAgeAbsent: string | null;
govAgePlus: string | null;
reasonSameDate: string | null;
createdFullName: string | null;
createdAt: Date;
}
export type { RequestItemsHistoryObject, FormMain };

View file

@ -0,0 +1,55 @@
interface DetailData {
id: string;
typeLeave: string;
dateStartLeave: Date | null;
dateEndLeave: Date | null;
numLeave: number;
status: string;
reason: string;
typeLeaveId: string;
code: string;
}
interface FormFilter {
page: number;
pageSize: number;
keyword: string;
type: string;
posType: string;
posLevel: string;
retireYear: string | null;
rangeYear: { min: number; max: number };
isShowRetire: boolean;
isProbation: boolean;
}
interface DataOptionLeave {
id: string;
name: string;
totalLeave: number;
}
interface DataOption {
id: string;
name: string;
disable?: boolean;
}
interface ResponseTotalObject {
typeLeaveId: string;
typeLeave: string;
totalLeave: number;
limitLeave: string;
remainLeave: string;
}
interface MyObjectRef {
typeLeave: object | null;
dateRange: object | null;
numLeave: object | null;
statLeave: object | null;
reason: object | null;
[key: string]: any;
}
export type { DetailData, FormFilter, DataOptionLeave, DataOption, ResponseTotalObject, MyObjectRef };

View file

@ -0,0 +1,31 @@
interface RowList {
id: string;
date: Date | null;
detail: string;
lastUpdateFullName: string;
lastUpdatedAt: Date;
}
interface FormFilter {
page: number;
pageSize: number;
keyword: string;
type: string;
posType: string;
posLevel: string;
retireYear: string | null;
rangeYear: { min: number; max: number };
isShowRetire: boolean;
isProbation: boolean;
}
interface MyObjectRef {
date: object | null;
detail: object | null;
[key: string]: any
}
export type {
RowList,
FormFilter,
MyObjectRef,
}

View file

@ -0,0 +1,48 @@
interface FormFilter {
page: number;
pageSize: number;
keyword: string;
type: string;
posType: string;
posLevel: string;
retireYear: string | null;
rangeYear: { min: number; max: number };
isShowRetire: boolean;
isProbation: boolean;
}
//ข้อมูล
interface RequestItemsObject {
profileId: string,
id?: string,
dateStart: Date | null,
dateEnd: Date | null,
detail: string,
reference: string,
refCommandNo: string,
refCommandDate: Date | null,
}
interface MyObjectRef {
dateStart:object|null
dateEnd:object|null
detail:object|null
reference:object|null
[key: string]: any;
}
interface ResponseObject {
lastUpdateFullName: string,
lastUpdatedAt: Date,
id: string;
dateStart: Date;
dateEnd: Date;
detail: string;
reference: string;
refCommandNo: string;
refCommandDate: Date | null;
createdFullName: string;
createdAt: Date;
}
export type{ FormFilter,RequestItemsObject,MyObjectRef,ResponseObject }

View file

@ -0,0 +1,6 @@
interface RangeYear {
min: number;
max: number;
}
export type { RangeYear };

View file

@ -0,0 +1,21 @@
interface RowList {
id: string;
date: Date | null;
reference: string;
detail: string;
refCommandNo: string;
refCommandDate: Date | null;
}
interface ObjectSalaryRef {
date: object | null;
posNo: object | null;
position: object | null;
typePosition: object | null;
levelPosition: object | null;
salary: object | null;
doc: object | null;
[key: string]: any;
}
export type { RowList, ObjectSalaryRef };

View file

@ -0,0 +1,16 @@
interface RequestObject {
currentZipCode: string | null;
currentSubDistrictId: string | null;
currentDistrictId: string | null;
currentProvinceId: string | null;
currentAddress: string | null;
registrationZipCode: string | null;
registrationSubDistrictId: string | null;
registrationDistrictId: string | null;
registrationProvinceId: string | null;
registrationAddress: string | null;
}
export type {
RequestObject,
};

View file

@ -0,0 +1,12 @@
interface RequestItemsObject {
profileId?: string;
profileEmployeeId?: string;
detail: string;
issueDate: Date | null;
issuer: string;
refCommandDate: Date | null;
refCommandNo: string;
isDate: boolean | string;
}
export type { RequestItemsObject };

View file

@ -0,0 +1,26 @@
interface RequestItemsObject {
profileId: string;
educationLevel: string;
institute: string;
startYear: number;
endYear: number;
finishDate: Date | null;
startDate: Date;
endDate: Date;
isEducation: boolean | null;
degree: string;
field: string;
fundName: string;
gpa: string;
country: string;
other: string;
duration: string;
durationYear: number | null | string;
note: string;
educationLevelId: string;
positionPath: string;
positionPathId: string;
isDate: boolean;
}
export type { RequestItemsObject };

View file

@ -0,0 +1,19 @@
interface FormEmployee {
positionEmployeeGroupId: string;
positionEmployeeLineId: string;
positionEmployeePositionId: string;
employeeOc: string;
employeeTypeIndividual: string;
employeeWage: string;
employeeMoneyIncrease: string;
employeeMoneyAllowance: string;
employeeMoneyEmployee: string;
employeeMoneyEmployer: string;
}
interface FormEmployment {
command: string;
date: null | Date;
}
export type { FormEmployee, FormEmployment };

View file

@ -0,0 +1,59 @@
interface DataProps {
row: RequestItemsObject;
rowIndex: number;
}
//ข้อมูล
interface RequestItemsObject {
birthDate: Date | null;
bloodGroupId: string | null;
citizenId: string | null;
firstName: string | null;
genderId: string | null;
lastName: string | null;
nationality: string | null;
prefixId: string | null;
race: string | null;
relationshipId: string | null;
religionId: string | null;
telephoneNumber: string | null;
employeeType: string | null;
employeeClass: string | null;
}
interface RequestItemsHistoryObject {
citizenId: string | null;
prefix: string | null;
firstName: string | null;
lastName: string | null;
birthDate: Date;
gender: string | null;
relationship: string | null;
bloodGroup: string | null;
nationality: string | null;
race: string | null;
religion: string | null;
telephoneNumber: string | null;
createdFullName: string | null;
createdAt: Date;
employeeType: string | null;
employeeClass: string | null;
}
//columns
interface Columns {
name: String;
align: String;
label: String;
sortable: Boolean;
field: String;
headerStyle: String;
style: String;
}
export type {
RequestItemsObject,
Columns,
DataProps,
RequestItemsHistoryObject,
};

View file

@ -0,0 +1,19 @@
interface RequestItemsObject {
profileId?: string;
year: number;
no: string;
volume: string;
section: string;
page: string;
receiveDate: Date | null;
insigniaId: string;
dateAnnounce: Date | null;
issue: string;
volumeNo: string;
refCommandDate: Date | null;
refCommandNo: string;
note: string;
profileEmployeeId?: string | null;
}
export type { RequestItemsObject };

View file

@ -0,0 +1,38 @@
interface FormFilter {
page: number;
pageSize: number;
keyword: string;
type: string;
posType: string;
posLevel: string;
retireYear: string | null;
rangeYear: { min: number; max: number };
isShowRetire: boolean | null;
isProbation: boolean | null;
}
interface FormAddPerson {
birthDate: Date | null,
rank: string,
prefix: string;
firstName: string;
lastName: string;
citizenId: string;
position: string;
posTypeId: string;
posLevelId: string;
}
interface MyObjectRef {
prefix: object | null;
firstName: object | null;
lastName: object | null;
citizenId: object | null;
position: object | null;
posTypeId: object | null;
posLevelId: object | null;
[key: string]: any;
}
export type { FormFilter, FormAddPerson, MyObjectRef };

View file

@ -0,0 +1,10 @@
interface RequestItemsObject {
certificateType: string;
issuer: string;
certificateNo: string;
issueDate: Date;
expireDate: Date | null;
profileId: string;
}
export type { RequestItemsObject };

View file

@ -0,0 +1,21 @@
interface RequestObject {
birthDate: Date | null;
bloodGroup: string | null;
citizenId: string;
// email: string | null;
ethnicity: string | null;
firstName: string;
gender: string | null;
lastName: string;
nationality: string | null;
phone: string | null;
// posLevelId: string;
// posTypeId: string;
prefix: string;
rank: string | null;
relationship: string | null;
religion: string | null;
// telephoneNumber: string | null;
}
export type { RequestObject };

View file

@ -0,0 +1,14 @@
interface RequestItemsObject {
profileId?: string;
profileEmployeeId?: string;
name: string;
date: Date | null;
point1: number;
point1Total: number;
point2: number;
point2Total: number;
pointSum: number;
pointSumTotal: number;
}
export type { RequestItemsObject };

View file

@ -0,0 +1,28 @@
interface FormSalaryNew {
date: Date | null;
posNo: string;
templatePos: string;
position: string;
positionLine: string;
positionPathSide: string;
positionType: string;
positionLevel: string;
positionExecutive: string;
salary: number | string | null;
salaryPos: number | string | null;
salaryCompensation: number | string | null;
refCommandNo: string;
templateDoc: string;
doc: string;
}
interface RequestNoPaidObject {
profileId?: string;
date: Date | null;
detail: string;
reference: string;
refCommandDate: Date | null;
refCommandNo: string;
}
export type { FormSalaryNew, RequestNoPaidObject };

View file

@ -0,0 +1,13 @@
interface RequestItemsObject {
field: string;
detail: string;
remark: string;
reference: string;
dateStart: Date | null,
dateEnd: Date | null ,
profileId: string,
}
export type { RequestItemsObject };

View file

@ -0,0 +1,19 @@
interface RequestItemsObject {
name: string,
topic: string,
yearly: number | null,
place: string,
duration: string,
department: string,
numberOrder: string,
dateOrder: Date | null,
startDate: Date,
endDate: Date,
startYear: number,
finishYear: number,
profileId: string,
isDate: boolean,
}
export type { RequestItemsObject };

View file

@ -0,0 +1,15 @@
interface ResponseObject {
registrationAddress: string | null;
registrationProvinceId: string | null;
registrationDistrictId: string | null;
registrationSubDistrictId: string | null;
registrationZipCode: string | null;
currentAddress: string | null;
currentProvinceId: string | null;
currentDistrictId: string | null;
currentSubDistrictId: string | null;
currentZipCode: string | null;
id: string;
}
export type { ResponseObject };

View file

@ -0,0 +1,19 @@
interface ResponseObject {
createdAt: Date;
createdFullName: string;
createdUserId: string;
detail: string;
id: string;
isActive: boolean;
isDate: boolean;
issueDate: Date;
issuer: string;
lastUpdateFullName: string;
lastUpdateUserId: string;
lastUpdatedAt: Date;
profileId: string;
refCommandDate: Date;
refCommandNo: string;
}
export type { ResponseObject };

View file

@ -0,0 +1,34 @@
//ข้อมูล
interface ResponseObject {
createdAt: Date
createdFullName: string
createdUserId: string
educationLevel: string;
institute: string;
startYear: number;
endYear: number;
finishDate: Date;
startDate: Date;
endDate: Date;
isEducation: boolean | null;
degree: string;
field: string;
fundName: string;
gpa: string;
country: string;
other: string;
duration: string;
durationYear: number | null;
note: string;
isActive: boolean
isDate: boolean
positionPath: string
positionPathId : string
profileId: string
lastUpdateFullName: string
lastUpdateUserID: string
lastUpdatedAt: Date
}
export type { ResponseObject };

View file

@ -0,0 +1,54 @@
interface EmployeeHistory {
createdAt: string;
createdFullName: string;
createdUserId: string;
employeeMoneyAllowance: string;
employeeMoneyEmployee: string;
employeeMoneyEmployer: string;
employeeMoneyIncrease: string;
employeeOc: string;
employeeTypeIndividual: string;
employeeWage: string;
id: string;
lastUpdateFullName: string;
lastUpdateUserId: string;
lastUpdatedAt: string;
positionEmployeeGroupId: string;
positionEmployeeLineId: string;
positionEmployeePositionId: string;
}
interface Employment {
command: string;
date: string | Date;
id: string;
}
interface EmploymentHistory {
command: string;
createdAt: string | Date;
createdFullName: string;
createdUserId: string;
date: string | Date;
id: string;
lastUpdateFullName: string;
lastUpdateUserId: string;
lastUpdatedAt: string | Date;
profileEmployeeEmploymentId: string;
}
interface ResEmployee {
id: string;
positionEmployeeGroupId: string;
positionEmployeeLineId: string;
positionEmployeePositionId: string;
employeeOc: string;
employeeTypeIndividual: string;
employeeWage: string;
employeeMoneyIncrease: string;
employeeMoneyAllowance: string;
employeeMoneyEmployee: string;
employeeMoneyEmployer: string;
}
export type { Employment, EmploymentHistory, EmployeeHistory, ResEmployee };

View file

@ -0,0 +1,23 @@
interface HistoryPos {
citizenId: string;
date: string | Date;
fullName: string;
id: string;
posNo: string;
position: string;
}
interface Position {
createdAt: string;
createdFullName: string;
createdUserId: string;
id: string;
isActive: boolean;
lastUpdateFullName: string;
lastUpdateUserId: "";
lastUpdatedAt: string;
name: string;
note: string;
}
export type { HistoryPos, Position };

View file

@ -0,0 +1,55 @@
interface ResponseObject {
id: string;
createdAt: string;
createdUserId: Date | null;
lastUpdatedAt: Date | null;
lastUpdateUserId: string;
createdFullName: string;
lastUpdateFullName: string;
profileId: string;
year: number;
no: string;
volume: string;
section: string;
page: string;
receiveDate: Date | null;
insigniaId: string;
insignia: ResponseInsigniaObject;
dateAnnounce: Date | null;
issue: string;
volumeNo: string;
refCommandDate: Date | null;
refCommandNo: string;
note: string;
}
interface ResponseInsigniaObject {
createdAt: Date;
createdFullName: string;
createdUserId: string;
id: string;
insigniaType: ResponseInsigniaType;
insigniaTypeId: string;
isActive: boolean;
lastUpdateFullName: string;
lastUpdateUserId: string;
lastUpdatedAt: Date;
level: null;
name: string;
note: string;
shortName: string;
}
interface ResponseInsigniaType {
createdAt: Date;
createdFullName: string;
createdUserId: string;
id: string;
isActive: boolean;
lastUpdateFullName: string;
lastUpdateUserId: string;
lastUpdatedAt: Date;
name: string;
}
export type { ResponseObject, ResponseInsigniaObject, ResponseInsigniaType };

View file

@ -0,0 +1,27 @@
interface DataType {
id: string;
posLevels: any;
posTypeName: string;
posTypeRank: number;
}
interface DataLevel {
id: string;
posLevelAuthority: string | null;
posLevelName: string;
posLevelRank: number;
}
interface DataPerson {
avatar?: string;
citizenId: string;
firstName: string;
id: string;
lastName: string;
posLevelId: string;
posTypeId: string;
position: string;
prefix: string;
}
export type { DataType, DataLevel, DataPerson };

View file

@ -0,0 +1,12 @@
//ข้อมูล
interface ResponseObject {
issuer: string,
certificateType: string,
certificateNo: string,
issueDate: Date | null,
isActive: boolean,
expireDate: Date | null
}
export type { ResponseObject };

View file

@ -0,0 +1,107 @@
interface ResponseObject {
avatar: null | string;
avatarName: null | string;
birthDate: Date | null;
bloodGroup: string | null;
citizenId: string;
createdAt: Date | null;
createdFullName: string;
createdUserId: string;
dateRetire: Date | null;
email: string | null;
ethnicity: string | null;
firstName: string;
gender: string | null;
id: string;
isLeave: boolean;
isProbation: boolean;
keycloak: string | null;
lastName: string;
lastUpdateFullName: string;
lastUpdateUserId: string;
lastUpdatedAt: Date | null;
nationality: string | null;
phone: string | null;
posLevel: PosLevel | null;
posLevelId: string | null;
posType: PosType | null;
posTypeId: string | null;
position: string | null;
prefix: string;
rank: string | null;
relationship: string | null;
religion: string | null;
telephoneNumber: string | null;
}
interface Religion {
id: string;
createdAt: Date;
lastUpdatedAt: Date;
createdFullName: string;
lastUpdateFullName: string;
name: string;
}
interface Gender {
id: string;
createdAt: Date;
lastUpdatedAt: Date;
createdFullName: string;
lastUpdateFullName: string;
name: string;
}
interface Relationship {
id: string;
createdAt: Date;
lastUpdatedAt: Date;
createdFullName: string;
lastUpdateFullName: string;
name: string;
}
interface BloodGroup {
id: string;
createdAt: Date;
lastUpdatedAt: Date;
createdFullName: string;
lastUpdateFullName: string;
name: string;
}
interface PosLevel {
id: string;
createdAt: Date;
createdUserId: string;
lastUpdatedAt: Date;
lastUpdateUserId: string;
createdFullName: string;
lastUpdateFullName: string;
posLevelName: string;
posLevelRank: number;
posLevelAuthority: null;
posTypeId: string;
}
interface PosType {
id: string;
createdAt: Date;
createdUserId: string;
lastUpdatedAt: Date;
lastUpdateUserId: string;
createdFullName: string;
lastUpdateFullName: string;
posTypeName: string;
posTypeRank: number;
}
export type {
ResponseObject,
Religion,
Gender,
Relationship,
BloodGroup,
PosLevel,
PosType,
};

View file

@ -0,0 +1,21 @@
interface ResponseObject {
createdAt: Date;
createdFullName: string;
createdUserId: string;
date: Date;
id: string;
isActive: boolean;
lastUpdateFullName: string;
lastUpdateUserId: string;
lastUpdatedAt: Date;
name: string;
point1: number;
point1Total: number;
point2: number;
point2Total: number;
pointSum: number;
pointSumTotal: number;
profileId: string;
}
export type { ResponseObject };

View file

@ -0,0 +1,21 @@
//ข้อมูล
interface ResponseObject {
createdAt: Date
createdFullName: string
createdUserId: string
dateStart: Date | null
dateEnd: Date | null
detail: string
field: string
id: string
isActive: boolean
lastUpdateFullName: string
lastUpdateUserId: string
lastUpdatedAt: Date
profileId: string
reference: string
remark: string
}
export type { ResponseObject };

View file

@ -0,0 +1,26 @@
//ข้อมูล
interface ResponseObject {
createdAt: Date
createdFullName: string
createdUserId: string
dateOrder: Date
dateStart: Date | null
dateEnd: Date | null
department: string
duration : string
id: string
isActive: boolean
isDate: boolean
lastUpdateFullName: string
lastUpdateUserId: string
lastUpdateAt: Date
name: string
numberOrder: string
place: string
profileId: string
topic: string
yearly: number
}
export type { ResponseObject };

View file

@ -0,0 +1,49 @@
const listPage = () => import("@/modules/04_registryPerson/views/list.vue");
const detailPage = () =>
import("@/modules/04_registryPerson/views/detailView.vue");
const requestEdit = () =>
import("@/modules/04_registryPerson/views/requestEdit.vue");
export default [
{
path: "/registry-person",
name: "registryNew",
component: listPage,
meta: {
Auth: true,
Key: "SYS_REGISTRY",
Role: "STAFF",
},
},
{
path: "/registry-person/:id",
name: "registryNewByid",
component: detailPage,
meta: {
Auth: true,
Key: "SYS_REGISTRY",
Role: "STAFF",
},
},
{
path: "/registry-employee/:id",
name: "registryNewEmployeeByid",
component: detailPage,
meta: {
Auth: true,
Key: "SYS_REGISTRY_EMP",
Role: "STAFF",
},
},
{
path: "/registry-person/request-edit",
name: "registryNewRequestEdit",
component: requestEdit,
meta: {
Auth: true,
Key: "SYS_REGISTRY",
Role: "STAFF",
},
},
];

View file

@ -0,0 +1,62 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import type { DataOption } from "@/modules/04_registryPerson/interface/index/Main";
import type {
DataType,
DataLevel,
} from "@/modules/04_registryPerson/interface/response/Main";
export const useRegistryNewDataStore = defineStore("registryNew", () => {
const searchTypeOption = ref<DataOption[]>([
{ id: "fullName", name: "ชื่อ-นามสกุล" },
{ id: "citizenId", name: "เลขประจำตัวประชาชน" },
// { id: "posNo", name: "ตำแหน่งเลขที่" },
{ id: "position", name: "ตำแหน่งในสายงาน" },
]);
const employeeClassOps = ref<DataOption[]>([
{ id: "officer", name: "ข้าราชการ กทม.สามัญ" },
{ id: "perm", name: "ลูกจ้างประจำ" },
// { id: "temp", name: "ลูกจ้างชั่วคราว" },
]);
const posTypeOps = ref<DataOption[]>([]);
const posTypeMain = ref<DataType[]>([]);
const posLevelOps = ref<DataOption[]>([]);
const yearOps = ref<DataOption[]>([]);
const mode = ref<string>("table");
function fetchType(data: DataType[]) {
posTypeMain.value = data;
const list: DataOption[] = data.map((e: DataType) => ({
id: e.id,
name: e.posTypeName,
}));
posTypeOps.value = list;
}
function fetchLevel(data: DataLevel[]) {
const list: DataOption[] = data.map((e: DataLevel) => ({
id: e.id,
name: e.posLevelName,
}));
const seen = new Set();
posLevelOps.value = list.filter((item: DataOption) => {
if (seen.has(item.name)) {
return false;
} else {
seen.add(item.name);
return true;
}
});
}
return {
fetchType,
fetchLevel,
posTypeMain,
searchTypeOption,
employeeClassOps,
posTypeOps,
posLevelOps,
yearOps,
mode,
};
});

View file

@ -0,0 +1,212 @@
import { ref } from "vue";
import { defineStore } from "pinia";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import type { RequestObject } from "@/modules/04_registryPerson/interface/request/Address";
import type { ResponseObject } from "@/modules/04_registryPerson/interface/response/Address";
import type {
DataOption,
AddressOps,
zipCodeOption,
} from "@/modules/04_registryPerson/interface/index/Main";
export const useAddressDataStore = defineStore("addess", () => {
const $q = useQuasar();
const profileIdBefore = ref<string>("");
const mixin = useCounterMixin();
const {
showLoader,
hideLoader,
date2Thai,
messageError,
convertDate,
dateToISO,
} = mixin;
const Ops = ref<AddressOps>({
provinceOps: [],
districtOps: [],
districtCOps: [],
subdistrictOps: [],
subdistrictCOps: [],
});
const OpsFilter = ref<AddressOps>({
provinceOps: [],
districtOps: [],
districtCOps: [],
subdistrictOps: [],
subdistrictCOps: [],
});
const defaultAddress: ResponseObject = {
id: "",
currentZipCode: "",
currentSubDistrictId: "",
currentDistrictId: "",
currentProvinceId: "",
currentAddress: "",
registrationZipCode: "",
registrationSubDistrictId: "",
registrationDistrictId: "",
registrationProvinceId: "",
registrationAddress: "",
};
const defaultAddressForm: RequestObject = {
currentZipCode: "",
currentSubDistrictId: "",
currentDistrictId: "",
currentProvinceId: "",
currentAddress: "",
registrationZipCode: "",
registrationSubDistrictId: "",
registrationDistrictId: "",
registrationProvinceId: "",
registrationAddress: "",
};
function findData(ops: any, id: string | null) {
if (id === null) return "";
return ops.find((r: { id: string }) => r.id === id) || {};
}
async function fetchProvince() {
showLoader();
await http
.get(config.API.profileNewProvince)
.then(async (res) => {
const data = res.data.result;
let option: DataOption[] = [];
data.map((r: any) => {
option.push({ id: r.id.toString(), name: r.name.toString() });
});
Ops.value.provinceOps = option;
OpsFilter.value.provinceOps = option;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
async function fetchDistrict(id: string | null, position: string) {
if (!id) return;
showLoader();
await http
.get(config.API.profileNewDistrictByPId(id))
.then(async (res) => {
const data = res.data.result;
let option: DataOption[] = [];
data.districts.map((r: any) => {
option.push({ id: r.id, name: r.name });
});
if (position == "1") {
Ops.value.districtOps = option;
OpsFilter.value.districtOps = option;
} else {
Ops.value.districtCOps = option;
OpsFilter.value.districtCOps = option;
}
return option;
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
async function fetchSubDistrict(id: string | null, position: string) {
if (!id) return;
showLoader();
await http
.get(config.API.profileNewSubDistrictByDId(id))
.then(async (res) => {
const data = res.data.result;
let option: zipCodeOption[] = [];
data.subDistricts.map((r: any) => {
option.push({
id: r.id,
name: r.name,
zipCode: r.zipCode,
});
});
if (position == "1") {
OpsFilter.value.subdistrictOps = option;
Ops.value.subdistrictOps = option;
} else {
OpsFilter.value.subdistrictCOps = option;
Ops.value.subdistrictCOps = option;
}
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
function filterSelector(val: any, update: Function, refData: string) {
switch (refData) {
case "provinceOps":
update(() => {
Ops.value.provinceOps = OpsFilter.value.provinceOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "districtOps":
update(() => {
Ops.value.districtOps = OpsFilter.value.districtOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "districtCOps":
update(() => {
Ops.value.districtCOps = OpsFilter.value.districtCOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "subdistrictOps":
update(() => {
Ops.value.subdistrictOps = OpsFilter.value.subdistrictOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "subdistrictCOps":
update(() => {
Ops.value.subdistrictCOps = OpsFilter.value.subdistrictCOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
default:
break;
}
}
return {
profileIdBefore,
defaultAddress,
defaultAddressForm,
Ops,
OpsFilter,
findData,
fetchProvince,
fetchDistrict,
fetchSubDistrict,
filterSelector,
};
});

View file

@ -0,0 +1,11 @@
import { defineStore } from "pinia";
import { ref } from "vue";
export const useRegistryDetailNewDataStore = defineStore(
"registryNewDetail",
() => {
const tabMain = ref<string>("1");
return { tabMain };
}
);

View file

@ -0,0 +1,36 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import type { DataOption } from "@/modules/04_registryPerson/interface/index/Main";
export const useRequestEditStore = defineStore("requestEditStore", () => {
const optionTopic = ref<string[]>([
"ขอแก้ไขคำนำหน้านาม ชื่อ นามสกุล",
"ขอแก้ไขรูปภาพประจำตัว",
"ขอแก้ไขชื่อ - นามสกุล คู่สมรส",
"ขอแก้ไขชื่อ - นามสกุล บิดา",
"ขอแก้ไขชื่อ - นามสกุล มารดา",
"ขอแก้ไขข้อมูลการได้รับพระราชทานเครื่องราชอิสริยาภรณ์/เหรียญจักรพรรดิมาลา",
"ขอแก้ไขประกาศเกียรติคุณ",
"ขอแก้ไขข้อมูลประวัติการศึกษา",
]);
const optionStatus = ref<DataOption[]>([
{ id: "", name: "ทั้งหมด" },
{ id: "PENDING", name: "รอดำเนินการ" },
{ id: "COMPLETE", name: "ดำเนินการแก้ไขแล้ว" },
{ id: "REJECT", name: "ไม่อนุมัตการแก้ไข" },
]);
function convertStatus(val: string) {
switch (val) {
case "PENDING":
return "รอดำเนินการ";
case "COMPLETE":
return "ดำเนินการแก้ไขแล้ว";
case "REJECT":
return "ไม่อนุมัตการแก้ไข";
default:
return "-";
}
}
return { convertStatus, optionTopic, optionStatus };
});

View file

@ -0,0 +1,29 @@
import { ref } from "vue";
import { defineStore } from "pinia";
export const useResultsPerformDataStore = defineStore(
"resultPerformDataStore",
() => {
function textRangePoint(val: number | undefined) {
if (val == undefined) val = -1;
if (val < 60.0) return "(คะแนนต่ำกว่าร้อยละ 60.00)";
if (val >= 60.0 && val <= 69.99) return "(คะแนนร้อยละ 60.00 - 69.99)";
if (val >= 70.0 && val <= 79.99) return "(คะแนนร้อยละ 70.00 - 79.99)";
if (val >= 80.0 && val <= 89.99) return " (คะแนนร้อยละ 80.00 - 89.99)";
if (val >= 90.0) return " (คะแนนร้อยละ 90.00 ขึ้นไป)";
else return "";
}
function textPoint(val: number | undefined) {
if (val == undefined) val = -1;
if (val < 60.0) return "ต้องปรับปรุง";
if (val >= 60.0 && val <= 69.99) return "พอใช้";
if (val >= 70.0 && val <= 79.99) return "ดี";
if (val >= 80.0 && val <= 89.99) return "ดีมาก";
if (val >= 90.0) return "ดีเด่น";
else return "-";
}
return { textRangePoint, textPoint };
}
);

View file

@ -0,0 +1,21 @@
import { ref, computed } from "vue";
import { defineStore } from "pinia";
import type { DataOptionInsignia } from "@/modules/04_registryPerson/interface/index/Main";
import type { ResponseObject as Insignia } from "@/modules/07_insignia/interface/response/Main";
export const useInsigniaDataStore = defineStore("insigniaDataStore", () => {
const insigniaOption = ref<DataOptionInsignia[]>([]);
function mapInsigniaOption(resData: any) {
insigniaOption.value = [];
resData.map((r: Insignia) => {
insigniaOption.value.push({
id: r.id.toString(),
name: r.name.toString() + ` (${r.shortName})`,
typeName: r.insigniaTypeName.toString(),
});
});
}
return { insigniaOption, mapInsigniaOption };
});

View file

@ -0,0 +1,256 @@
import { ref } from "vue";
import { defineStore } from "pinia";
import { useCounterMixin } from "@/stores/mixin";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import type { RequestObject } from "@/modules/04_registryPerson/interface/request/Profile";
import type {
DataOption,
InformationOps,
} from "@/modules/04_registryPerson/interface/index/Main";
export const useProfileDataStore = defineStore("profile", () => {
const $q = useQuasar();
const mixin = useCounterMixin();
const {
showLoader,
hideLoader,
date2Thai,
messageError,
convertDate,
dateToISO,
} = mixin;
const retireDate = ref<Date>();
const defaultProfile: RequestObject = {
bloodGroup: null,
relationship: null,
gender: null,
// posTypeId: "",
// posLevelId: "",
religion: null,
citizenId: "",
// telephoneNumber: null,
nationality: null,
ethnicity: null,
birthDate: null,
phone: null,
// email: null,
lastName: "",
firstName: "",
prefix: "",
rank: null,
};
const Ops = ref<InformationOps>({
prefixOps: [],
rankOps: [],
genderOps: [],
bloodOps: [],
statusOps: [],
religionOps: [],
employeeClassOps: [
{ id: "perm", name: "ลูกจ้างประจำ" },
{ id: "temp", name: "ลูกจ้างชั่วคราว" },
],
employeeTypeOps: [
{ id: "gov", name: "งบประมาณเงินอุดหนุนรัฐบาล" },
{ id: "bkk", name: "งบประมาณกรุงเทพมหานคร" },
],
});
const OpsFilter = ref<InformationOps>({
prefixOps: [],
rankOps: [],
genderOps: [],
bloodOps: [],
statusOps: [],
religionOps: [],
employeeClassOps: [
{ id: "perm", name: "ลูกจ้างประจำ" },
{ id: "temp", name: "ลูกจ้างชั่วคราว" },
],
employeeTypeOps: [
{ id: "gov", name: "งบประมาณเงินอุดหนุนรัฐบาล" },
{ id: "bkk", name: "งบประมาณกรุงเทพมหานคร" },
],
});
const prefixOp = ref<string[]>([
"นาย",
"นาง",
"นางสาว",
"เด็กชาย",
"เด็กหญิง",
]);
function calculateAge(birthDate: Date | null) {
if (!birthDate) return null;
const birthDateTimeStamp = new Date(birthDate).getTime();
const now = new Date();
const diff = now.getTime() - birthDateTimeStamp;
const ageDate = new Date(diff);
const years = ageDate.getUTCFullYear() - 1970;
const months = ageDate.getUTCMonth();
const days = ageDate.getUTCDate() - 1;
const retire = new Date(birthDate);
retire.setFullYear(retire.getFullYear() + 60);
retireDate.value = retire;
if (years > 60) {
return "อายุเกิน 60 ปี";
}
return `${years} ปี ${months} เดือน ${days} วัน`;
}
const fetchPerson = async () => {
showLoader();
await http
.get(config.API.profileNewMetaMain)
.then((res) => {
const data = res.data.result;
let optionbloodGroups: DataOption[] = [];
data.bloodGroups.map((r: any) => {
optionbloodGroups.push({
id: r.id.toString(),
name: r.name.toString(),
});
});
Ops.value.bloodOps = optionbloodGroups;
OpsFilter.value.bloodOps = optionbloodGroups;
let optiongenders: DataOption[] = [];
data.genders.map((r: any) => {
optiongenders.push({
id: r.id.toString(),
name: r.name.toString(),
});
});
Ops.value.genderOps = optiongenders;
OpsFilter.value.genderOps = optiongenders;
let optionprefixs: DataOption[] = [];
data.prefixs.map((r: any) => {
optionprefixs.push({
id: r.id.toString(),
name: r.name.toString(),
});
});
Ops.value.prefixOps = optionprefixs;
OpsFilter.value.prefixOps = optionprefixs;
let optionrank: DataOption[] = [];
data.rank.map((r: any) => {
optionrank.push({
id: r.id.toString(),
name: r.name.toString(),
});
});
Ops.value.rankOps = optionrank;
OpsFilter.value.rankOps = optionrank;
let optionrelationships: DataOption[] = [];
data.relationships.map((r: any) => {
optionrelationships.push({
id: r.id.toString(),
name: r.name.toString(),
});
});
Ops.value.statusOps = optionrelationships;
OpsFilter.value.statusOps = optionrelationships;
let optionreligions: DataOption[] = [];
data.religions.map((r: any) => {
optionreligions.push({
id: r.id.toString(),
name: r.name.toString(),
});
});
Ops.value.religionOps = optionreligions;
OpsFilter.value.religionOps = optionreligions;
})
.catch((e: any) => {})
.finally(() => {
hideLoader();
});
};
const filterSelector = (val: any, update: Function, refData: string) => {
switch (refData) {
case "prefixOps":
update(() => {
Ops.value.prefixOps = OpsFilter.value.prefixOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "rankOps":
update(() => {
Ops.value.rankOps = OpsFilter.value.rankOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "genderOps":
update(() => {
Ops.value.genderOps = OpsFilter.value.genderOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "bloodOps":
update(() => {
Ops.value.bloodOps = OpsFilter.value.bloodOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "statusOps":
update(() => {
Ops.value.statusOps = OpsFilter.value.statusOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "religionOps":
update(() => {
Ops.value.religionOps = OpsFilter.value.religionOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "employeeClassOps":
update(() => {
Ops.value.employeeClassOps = OpsFilter.value.employeeClassOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
case "employeeTypeOps":
update(() => {
Ops.value.employeeTypeOps = OpsFilter.value.employeeTypeOps.filter(
(v: DataOption) => v.name.indexOf(val) > -1
);
});
break;
default:
break;
}
};
return {
defaultProfile,
retireDate,
Ops,
OpsFilter,
calculateAge,
fetchPerson,
filterSelector,
};
});

View file

@ -0,0 +1,20 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { useCounterMixin } from "@/stores/mixin";
// const { date2Thai } = useCounterMixin();
export const useRegistryDataStore = defineStore("RegistryData", () => {
const row = ref<[]>([]);
function save(data: any) {
const list = data.map((e: any) => ({
...e,
})) satisfies [];
row.value = list;
}
return {
save,
row,
};
});

View file

@ -0,0 +1,187 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import type { DataOption2 } from "@/modules/04_registryPerson/interface/index/Main";
export const useSalaryDataStore = defineStore("salatyDataStore", () => {
const optionTemplatePos = ref<DataOption2[]>([
{
id: 1,
name: "เลื่อนเงินเดือน",
},
{
id: 2,
name: "เลื่อนเงินเดือน (ดีเด่น)",
},
{
id: 3,
name: "เลื่อนเงินเดือน (เพิ่มเติม)",
},
{
id: 4,
name: "ปรับเงินเดือน",
},
{
id: 5,
name: "ปรับเงินเดือนเพิ่มเติมตามคุณวุฒิการศึกษา",
},
{
id: 6,
name: "ปรับเงินเดือนเพิ่มเติมตามคุณวุฒิการศึกษา (เพิ่มเติม)",
},
{
id: 7,
name: "เลื่อนเงินเดือนและให้ข้าราชการ กทม. สามัญได้รับเงินเดือนสูงกว่าขั้นสูงของตำแหน่งที่ได้รับแต่งตั้ง",
},
{
id: 8,
name: "เลื่อนเงินเดือนกรณีพิเศษให้แก่ผู้ปฏิบัติงานด้านยาเสพติด",
},
{
id: 9,
name: "{ประเภทตำแหน่ง} {ชื่อตำแหน่ง} สำนัก{ชื่อสำนัก}",
},
{
id: 10,
name: "แต่งตั้งข้าราชการ {ประเภทตำแหน่ง} {ชื่อตำแหน่ง} สำนัก{ชื่อสำนัก}",
},
{
id: 11,
name: "แก้ไขคำสั่ง {หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 12,
name: "โปรดเกล้าฯ {ชื่อตำแหน่ง} สำนัก{ชื่อสำนัก}",
},
{
id: 13,
name: "ช่วยราชการที่{หน่วยงานและรายละเอียดต่างๆ}",
},
{
id: 14,
name: "ปฏิบัติหน้าที่ในตำแหน่ง{ชื่อตำแหน่ง} สำนัก{ชื่อสำนัก}",
},
{
id: 15,
name: "รักษาการในตำแหน่ง{ชื่อตำแหน่ง} สำนัก{ชื่อสำนัก}",
},
{
id: 16,
name: "พ้นจากการทดลองปฏิบัติหน้าที่ราชการ",
},
{
id: 17,
name: "งดเลื่อนขั้นเงินเดือน",
},
{
id: 18,
name: "แก้ไขคำสั่งเลื่อนขั้นเงินเดือน {หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 19,
name: "ยกเลิกคำสั่งเลื่อนขั้นเงินเดือน {หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 20,
name: "กลับไปปฏิบัติงานทางต้นสังกัดเดิม",
},
{
id: 21,
name: "โปรดเกล้าฯ แต่งตั้งให้ดำรงตำแหน่ง{รายละเอียดของตำแหน่งและหน่วยงาน}",
},
]);
const optionTemplateDoc = ref<DataOption2[]>([
{
id: 1,
name: "บรรจุและแต่งตั้งผู้สอบแข่งขันได้ คำสั่ง {หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 2,
name: "ปรับเงินเดือนตาม{รายละเอียดของบัญชี เช่นชื่อ ฉบับที่ ปี พ.ศ.}",
},
{
id: 3,
name: "เลื่อนขั้นเงินเดือนตามคำสั่ง {หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 4,
name: "เลื่อนขั้นเงินเดือน คำสั่ง {หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 5,
name: "เลื่อนขั้นเงินเดือน (1 ขั้น) คำสั่ง {หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 6,
name: "เลื่อนขั้นเงินเดือน (1.5 ขั้น) คำสั่ง {หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 7,
name: "แต่งตั้งตามคำสั่ง {หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 8,
name: "คำสั่ง {หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 9,
name: "ปรับเงินเดือนตาม{รายละเอียดข้อมูล}",
},
{
id: 10,
name: "แก้ไขคำสั่ง {หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม} ตามคำสั่ง {หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 11,
name: "เลื่อนระดับและแต่งตั้งคำสั่ง{หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 12,
name: "แต่งตั้งดำรงตำแหน่ง{ชื่อตำแหน่ง} คำสั่ง{หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 13,
name: "แต่งตั้งคำสั่ง{หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 14,
name: "เลื่อนและแต่งตั้งโดยการสอบคัดเลือก คำสั่ง{หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 15,
name: "แต่งตั้งข้าราชการให้ดำรงตำแหน่งของ{รายละเอียดของตำแหน่งและหน่วยงาน} คำสั่ง{หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 16,
name: "ย้ายตามคำสั่ง{หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 17,
name: "แต่งตั้งข้าราชการให้ดำรงตำแหน่งตามคำสั่ง{หน่วยงาน/สำนัก} ที่ {เลขที่}/{ปีงบประมาณ} ลว. {วันที่ลงนาม}",
},
{
id: 18,
name: "ปรับอัตราเงินเดือนตามพระราชกฤษฎีกาการปรับอัตราเงินเดือนของข้าราชการ พ.ศ. (.............)",
},
]);
return {
optionTemplatePos,
optionTemplateDoc,
};
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,583 @@
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
/** importType*/
import type { DataOption } from "@/modules/04_registryPerson/interface/index/Main";
import type {
DataPerson,
DataType,
} from "@/modules/04_registryPerson/interface/response/Main";
import type { FormFilter } from "@/modules/04_registryPerson/interface/request/Main";
/** importComponents*/
import TableView from "@/modules/04_registryPerson/components/TableView.vue";
import avatar from "@/assets/avatar_user.jpg";
/** importStore*/
import { useRegistryNewDataStore } from "@/modules/04_registryPerson/store";
import { useCounterMixin } from "@/stores/mixin";
const $q = useQuasar();
const store = useRegistryNewDataStore();
const { showLoader, hideLoader, messageError } = useCounterMixin();
const mode = ref<"table" | "card">("table");
const isShowFilter = ref<boolean>(false);
const isShowBtnFilter = ref<boolean>(true);
const empType = ref<string>("officer"); // .//
const labelOption = reactive({
type: "ข้าราชการ กทม.สามัญ",
posType: "ทั้งหมด",
posLevel: "ทั้งหมด",
retireYear: "",
});
const searchType = ref<string>("fullName");
const formFilter = reactive<FormFilter>({
page: 1,
pageSize: 12,
keyword: "",
type: "",
posType: "",
posLevel: "",
retireYear: "",
rangeYear: { min: 0, max: 60 },
isShowRetire: false,
isProbation: false,
});
const maxPage = ref<number>(1);
const total = ref<number>(0);
const dataPersonMain = ref<DataPerson[]>([]);
const conditionTotal = computed(() => {
let num: string = "";
if (formFilter.isProbation && formFilter.isShowRetire) {
num = "(2)";
} else if (formFilter.isProbation || formFilter.isShowRetire) {
num = "(1)";
} else "";
return num;
});
/**
* function เรยกขอมลตำแหนงประเภท
*/
function fetchType() {
http
.get(config.API.orgPosType)
.then((res) => {
store.fetchType(res.data.result);
})
.catch((err) => {
messageError($q, err);
});
}
/**
* function เรยกขอมลกลมงาน
*/
function fetchOptionGroup() {
http
.get(config.API.orgEmployeeType)
.then((res) => {
store.fetchType(res.data.result);
})
.catch((err) => {
messageError($q, err);
});
}
function fetchYearOption() {
if (store.yearOps.length === 0) {
const options = [];
const year = new Date().getFullYear();
for (let i = year - 40; i <= year + 60; i++) {
options.push({ id: i.toString(), name: (i + 543).toString() });
}
if (options) {
store.yearOps.push(...options);
}
}
}
/**
* function fetch รายชอขอมลทะเบยนประว
*/
function fetchDataPerson() {
showLoader();
let queryParams: any = {
page: formFilter.page,
pageSize: formFilter.pageSize,
};
if (formFilter.keyword) {
queryParams = Object.assign({}, queryParams, {
searchField: searchType.value,
searchKeyword: formFilter.keyword,
});
}
if (labelOption.posLevel != "ทั้งหมด") {
queryParams = Object.assign({}, queryParams, {
posLevel: labelOption.posLevel,
});
}
if (labelOption.posType != "ทั้งหมด") {
queryParams = Object.assign({}, queryParams, {
posType: labelOption.posType,
});
}
if (formFilter.isProbation != null) {
queryParams.isProbation = formFilter.isProbation;
}
if (formFilter.isShowRetire != null) {
queryParams.isRetire = formFilter.isShowRetire;
}
if (empType.value !== "officer") {
queryParams.type = empType.value;
}
http
.get(
config.API.registryNew(empType.value !== "officer" ? "-employee" : ""),
{ params: queryParams }
)
.then((res) => {
maxPage.value = Math.ceil(res.data.result.total / formFilter.pageSize);
dataPersonMain.value = res.data.result.data;
total.value = res.data.result.total;
insertAvatar(res.data.result.data);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* function fetch ปโปรไฟล
* @param items อมลคน
*/
function insertAvatar(items: DataPerson[]) {
items.map((x: any, index: number) => {
if (x.avatarName != null) {
http
.get(
config.API.fileByFile("ทะเบียนประวัติ", "โปรไฟล์", x.id, x.avatarName)
)
.then((img) => {
dataPersonMain.value[index] = {
...x,
avatar: img.data.downloadUrl,
};
})
.catch(() => {
dataPersonMain.value[index] = {
...x,
avatar: avatar,
};
});
} else {
dataPersonMain.value[index] = {
...x,
avatar: avatar,
};
}
});
}
/**
* funciotn แสดงตวเลอกเพมเต
*/
function onClickShowFilter() {
isShowFilter.value = !isShowFilter.value;
isShowBtnFilter.value = false;
if (isShowFilter.value) {
fetchType();
// fetchLevel();
fetchYearOption();
}
}
/**
* funciotn นหาขอม
*/
function onclickSearch() {
isShowFilter.value = true;
isShowBtnFilter.value = false;
formFilter.page = 1;
if (isShowFilter.value) {
fetchType();
// fetchLevel();
fetchYearOption();
}
formFilter.keyword = formFilter.keyword === null ? "" : formFilter.keyword;
fetchDataPerson();
}
/**
* function เลอกประเภทขาราชการ
* @param item ประเภทขาราชการ
*/
function selectType(item: DataOption) {
labelOption.type = item.name;
empType.value = item.id;
formFilter.page = 1;
labelOption.posType = "ทั้งหมด";
labelOption.posLevel = "ทั้งหมด";
if (item.id !== "officer") {
formFilter.isShowRetire = null;
formFilter.isProbation = null;
fetchOptionGroup();
} else {
fetchType();
}
fetchDataPerson();
}
/**
* function เลอกประเภทตำแหน
* @param item ประเภทตำแหน
*/
function selectPosType(item: DataOption) {
const dataType = store.posTypeMain.find((e: DataType) => e.id === item.id);
store.fetchLevel(dataType?.posLevels);
labelOption.posType = item.name;
labelOption.posLevel = "ทั้งหมด";
formFilter.page = 1;
fetchDataPerson();
}
/**
* function เลอกประเภทตำแหน
* @param item ประเภทระด
*/
function selectPosLevel(item: DataOption) {
labelOption.posLevel = item.name;
formFilter.page = 1;
fetchDataPerson();
}
/**
* function เคลวเลอก
* @param t ประเภทตวเลอก
*/
function clearSelect(t: string) {
if (t === "posType") {
labelOption.posType = "ทั้งหมด";
labelOption.posLevel = "ทั้งหมด";
} else if (t === "posLevel") {
labelOption.posLevel = "ทั้งหมด";
} else if (t === "retireYear") {
labelOption.retireYear = "";
} else if (t === "rangeYear") {
formFilter.rangeYear.min = 0;
formFilter.rangeYear.max = 60;
}
formFilter.page = 1;
fetchDataPerson();
}
onMounted(async () => {
fetchDataPerson();
});
</script>
<template>
<q-card class="q-mt-md">
<q-card-section class="card-img q-pb-lg">
<div class="text-h5 text-center q-py-md text-weight-medium">
นหาขอมลทะเบยนประว
</div>
<div class="row justify-center">
<div
class="col-xs-12 col-sm-12 col-md-11 q-pa-md bg-Search rounded-borders"
>
<q-form @submit="onclickSearch">
<div class="bg-white row col-12 q-pa-none rounded-borders">
<div class="row col-11">
<div class="col-3 row wrap">
<q-select
borderless
bg-color="white"
v-model="searchType"
:options="store.searchTypeOption"
emit-value
dense
emit-option
option-label="name"
option-value="id"
map-options
class="selectS col-11 q-px-md"
color="deep-orange"
dropdown-icon="mdi-chevron-down"
/>
<q-separator vertical />
</div>
<q-input
borderless
dense
bg-color="white"
v-model="formFilter.keyword"
clearable
placeholder="ค้นหา"
class="col-9 q-pr-md"
@clear="fetchDataPerson"
>
<template v-slot:before>
<q-icon name="search" color="deep-orange" />
</template>
</q-input>
</div>
<div class="row col-1">
<q-btn
class="fit btnSearch"
unelevated
color="deep-orange"
label="ค้นหา"
type="submit"
/>
</div>
</div>
</q-form>
<div v-if="isShowBtnFilter" class="col-12 row q-mt-sm">
<q-space />
<q-btn
flat
label="ตัวเลือกเพิ่มเติม"
icon-right="mdi-tune"
@click="onClickShowFilter"
dense
class="q-px-sm"
></q-btn>
</div>
<div
class="row q-mt-sm q-gutter-sm justify-center"
v-if="isShowFilter"
>
<q-btn-dropdown
flat
rounded
dense
label-color="white"
dropdown-icon="mdi-chevron-down"
class="q-px-sm"
>
<template v-slot:label>
{{ `${labelOption.type}` }}
</template>
<q-list>
<q-item
v-for="(item, index) in store.employeeClassOps"
:key="index"
clickable
v-close-popup
@click="selectType(item)"
>
<q-item-section>
<q-item-label>{{ item.name }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
<q-separator inset vertical class="lineFil" />
<q-btn-dropdown
rounded
flat
dense
label-color="white"
dropdown-icon="mdi-chevron-down"
class="q-px-sm"
>
<template v-slot:label>
{{
labelOption.posType !== "ทั้งหมด"
? labelOption.posType
: empType === "officer"
? `ตำแหน่งประเภท${labelOption.posType}`
: `กลุ่มงาน${labelOption.posType}`
}}
<q-btn
size="10px"
flat
round
color="white"
icon="close"
v-if="labelOption.posType !== 'ทั้งหมด'"
@click.stop.prevent="clearSelect('posType')"
/>
</template>
<q-list>
<q-item
v-for="(item, index) in store.posTypeOps"
:key="index"
clickable
v-close-popup
@click="selectPosType(item)"
>
<q-item-section>
<q-item-label>{{ item.name }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
<q-separator inset vertical class="lineFil" />
<q-btn-dropdown
flat
dense
rounded
label-color="white"
dropdown-icon="mdi-chevron-down"
class="q-px-sm"
:disable="labelOption.posType === 'ทั้งหมด' ? true : false"
>
<template v-slot:label>
{{
labelOption.posLevel !== "ทั้งหมด"
? labelOption.posLevel
: empType === "officer"
? `ระดับ${labelOption.posLevel}`
: `ระดับชั้นงาน${labelOption.posLevel}`
}}
<q-btn
size="10px"
flat
round
color="white"
icon="close"
v-if="labelOption.posLevel !== 'ทั้งหมด'"
@click.stop.prevent="clearSelect('posLevel')"
/>
</template>
<q-list
:style="store.posLevelOps.length > 9 ? 'height: 450px' : ''"
>
<q-item
v-for="(item, index) in store.posLevelOps"
:key="index"
clickable
v-close-popup
@click="selectPosLevel(item)"
>
<q-item-section>
<q-item-label>{{ item.name }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
<q-separator
inset
vertical
class="lineFil"
v-if="empType === 'officer'"
/>
<q-btn-dropdown
v-if="empType === 'officer'"
flat
dense
rounded
label-color="white"
dropdown-icon="mdi-chevron-down"
:label="`เงื่อนไขอื่นๆ ${conditionTotal}`"
class="q-px-sm"
>
<q-list>
<q-item clickable v-close-popup>
<q-item-section>
<q-toggle
v-model="formFilter.isProbation"
color="primary"
label="ทดลองปฏิบัติหน้าที่ราชการ"
@update:model-value="fetchDataPerson"
/>
</q-item-section>
</q-item>
<q-item clickable v-close-popup>
<q-item-section>
<q-toggle
v-model="formFilter.isShowRetire"
color="primary"
label="แสดงข้อมูลผู้พ้นจากราชการ"
@update:model-value="fetchDataPerson"
/>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-section>
<TableView
v-model:mode="mode"
:rows="dataPersonMain"
v-model:formFilter="formFilter"
v-model:maxPage="maxPage"
:fetchData="fetchDataPerson"
:fetchType="fetchType"
:total="total"
:empType="empType"
/>
</q-card-section>
</q-card>
</template>
<style scoped>
.card-img {
background: url("@/assets/registry-banner.png");
background-size: cover;
color: white;
}
:deep(.custom-select.q-field--outlined .q-field__control) {
color: white;
background-color: #36969f;
}
:deep(.custom-select.q-field--outlined .q-field__control::before) {
border: 1px solid #fff;
}
:deep(.custom-btn.q-btn--outline::before) {
background-color: #36969f;
}
.btnSearch {
border-radius: 0px 4px 4px 0px;
}
.bg-Search {
background: #00000015;
}
.lineFil {
transform: rotate(30deg);
height: 20px;
margin-top: 15px;
background: #ffffff7b !important;
}
.selectS .q-field__control .q-field__append .q-icon {
color: #ff5722 !important;
}
</style>

View file

@ -0,0 +1,405 @@
<script setup lang="ts">
import { ref, onMounted, watch } from "vue";
import { useRouter } from "vue-router";
import { useQuasar } from "quasar";
import config from "@/app.config";
import http from "@/plugins/http";
/**
* importType
*/
import type { QTableProps } from "quasar";
import type {
DataOption,
Pagination,
} from "@/modules/04_registryPerson/interface/index/Main";
/**
* importComponents
*/
import DialogStatus from "@/modules/04_registryPerson/components/requestEdit/DialogStatus.vue";
/**
* importStore
*/
import { useRequestEditStore } from "@/modules/04_registryPerson/stores/RequestEdit";
import { useCounterMixin } from "@/stores/mixin";
/**
* use
*/
const $q = useQuasar();
const router = useRouter();
const store = useRequestEditStore();
const { showLoader, hideLoader, messageError } = useCounterMixin();
/**
* Table
*/
const rows = ref<any[]>([]);
const page = ref<number>(1);
const pageSize = ref<number>(10);
const rowsTotal = ref<number>(0);
const maxPage = ref<number>(0);
const columns = ref<QTableProps["columns"]>([
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: false,
field: (row) => rows.value.indexOf(row) + 1,
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "fullname",
align: "left",
label: "ชื่อ-นามสกุล",
sortable: false,
field: "fullname",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "topic",
align: "left",
label: "ชื่อเรื่อง",
sortable: true,
field: "topic",
format: (v) => (v ? v : "-"),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "detail",
align: "left",
label: "รายละเอียด",
sortable: true,
field: "detail",
format: (v) => (v ? v : "-"),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "document",
align: "left",
label: "หลักฐานอ้างอิง",
sortable: true,
field: "document",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "status",
align: "left",
label: "สถานะคำร้อง",
sortable: true,
field: "status",
format: (v) => store.convertStatus(v),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "remark",
align: "left",
label: "หมายเหตุ ",
sortable: true,
field: "remark",
format: (v) => (v ? v : "-"),
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
const visibleColumns = ref<string[]>([
"no",
"fullname",
"topic",
"detail",
"document",
"status",
"remark",
]);
/**
* วแปร
*/
const status = ref<string>("");
const keyword = ref<string>("");
const statusOption = ref<DataOption[]>(store.optionStatus);
const modalStatus = ref<boolean>(false);
const requestId = ref<string>("");
function fetchListRequset() {
showLoader();
http
.get(config.API.requestEdit + `admin`, {
params: {
page: page.value,
pageSize: pageSize.value,
status: status.value,
keyword: keyword.value,
},
})
.then((res) => {
const data = res.data.result;
maxPage.value = Math.ceil(data.total / pageSize.value);
rowsTotal.value = data.total;
rows.value = data.data;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function updateStatusValue() {
page.value = 1;
fetchListRequset();
}
function clearStatus() {
status.value = "";
statusOption.value = store.optionStatus;
}
function filterOption(val: string, update: Function) {
update(() => {
status.value = val ? "" : status.value;
statusOption.value = store.optionStatus.filter(
(v: any) => v.name.indexOf(val) > -1
);
});
}
function onclickEdit(id: string) {
modalStatus.value = true;
requestId.value = id;
}
function updatePageSizePagination(newPagination: Pagination) {
page.value = 1;
pageSize.value = newPagination.rowsPerPage;
}
/**
* function หาชอไฟล
* @param id รายการยนคำรองขอแกไขขอม
*/
function onDownloadFile(id: string) {
showLoader();
http
.get(
config.API.file(
"ระบบทะเบียนประวัติ",
"เอกสารหลักฐานคำร้องขอแก้ไขข้อมูล",
id
)
)
.then((res) => {
if (res.data.length !== 0) {
downloadUrl(id, res.data[0].fileName);
} else {
hideLoader();
}
})
.catch((e) => {
messageError($q, e);
hideLoader();
});
}
/**
* function โหลดไฟล
* @param id รายการยนคำรองขอแกไขขอม
* @param fileName อไฟล
*/
function downloadUrl(id: string, fileName: string) {
http
.get(
config.API.fileByFile(
"ระบบทะเบียนประวัติ",
"เอกสารหลักฐานคำร้องขอแก้ไขข้อมูล",
id,
fileName
)
)
.then((res) => {
window.open(res.data.downloadUrl, "_blank");
})
.catch((e) => {
messageError($q, e);
})
.finally(() => {
hideLoader();
});
}
watch(
() => pageSize.value,
() => {
fetchListRequset();
}
);
onMounted(() => {
fetchListRequset();
});
</script>
<template>
<div class="row items-center">
<div class="toptitle text-dark row items-center q-py-xs">
<q-btn
icon="mdi-arrow-left"
unelevated
round
dense
flat
color="primary"
class="q-mr-sm"
@click="router.go(-1)"
/>
รายการคำรองขอแกไขทะเบยนประว
</div>
</div>
<q-card flat bordered class="q-pa-md">
<div class="row q-mb-sm q-col-gutter-sm">
<div class="col-xs-10 col-md-3">
<q-select
style="max-width: 250px"
v-model="status"
label="สถานะคำร้อง"
dense
outlined
emit-value
map-options
option-label="name"
option-value="id"
:options="statusOption"
@update:model-value="updateStatusValue"
:clearable="status !== ''"
@clear="clearStatus"
use-input
@filter="(inputValue:string,
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>
<q-space />
<q-input
v-model="keyword"
outlined
clearable
dense
label="ค้นหา"
style="width: 250px"
@keydown.enter="updateStatusValue()"
>
</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
class="col-xs-12 col-sm-3 col-md-2"
/>
</div>
<div class="col-12">
<d-table
:columns="columns"
:rows="rows"
row-key="id"
:rows-per-page-options="[10, 25, 50, 100]"
:paging="true"
:visible-columns="visibleColumns"
@update:pagination="updatePageSizePagination"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-medium">{{ col.label }}</span>
</q-th>
<q-th auto-width />
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.name == 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-else-if="col.name === 'document'">
<q-btn
icon="mdi-download"
round
dense
flat
color="primary"
size="12px"
@click.pervent="onDownloadFile(props.row.id)"
>
<q-tooltip>หลกฐานอางอ</q-tooltip>
</q-btn>
</div>
<div v-else class="table_ellipsis2">
{{ col.value ? col.value : "-" }}
</div>
</q-td>
<q-td>
<q-btn
icon="edit"
round
dense
flat
color="edit"
size="12px"
@click.pervent="onclickEdit(props.row.id)"
>
<q-tooltip>แกไขสถานะคำรอง</q-tooltip>
</q-btn>
</q-td>
</q-tr>
</template>
<template v-slot:pagination="scope">
งหมด {{ rowsTotal }} รายการ
<q-pagination
v-model="page"
active-color="primary"
color="dark"
:max="Number(maxPage)"
:max-pages="5"
size="sm"
boundary-links
direction-links
@update:model-value="fetchListRequset()"
></q-pagination>
</template>
</d-table>
</div>
</q-card>
<DialogStatus
v-model:modal="modalStatus"
:fetchData="fetchListRequset"
:requestId="requestId"
/>
</template>
<style scoped></style>