hrms-mgt/src/modules/23_persons/components/TablePersons.vue
DESKTOP-1R2VSQH\Lenovo ThinkPad E490 985f1bcf23 feat(persons): add router for persons feature
2026-05-18 13:44:38 +07:00

306 lines
8.1 KiB
Vue

<script setup lang="ts">
import { onMounted, reactive, ref } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import { usePersonsStore } from "@/modules/23_persons/stores/PersonsStore";
import { usePagination } from "@/composables/usePagination";
import type { QTableProps } from "quasar";
import type {
DataOptions,
PosTypes,
PosLevels,
Person,
} from "@/modules/23_persons/interface/Main";
const $q = useQuasar();
const { showLoader, hideLoader, messageError } = useCounterMixin();
const { pagination, params, onRequest } = usePagination("", fetchData);
const store = usePersonsStore();
const props = defineProps<{ type: "current" | "draft" }>();
const options = reactive({
posType: [] as DataOptions[],
posLevel: [] as DataOptions[],
});
const filters = reactive({
keyword: "",
position: "",
posTypeId: "",
posLevelId: "",
});
const rows = ref<Person[]>([]);
const visibleColumns = ref<string[]>([
"citizenId",
"fullName",
"position",
"posType",
"posLevel",
]);
const columns = ref<QTableProps["columns"]>([
{
name: "citizenId",
align: "left",
label: "เลขบัตรประชาชน",
sortable: false,
field: "citizenId",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "fullName",
align: "left",
label: "ชื่อ-นามสกุล",
sortable: false,
field: "fullName",
format(val, row) {
return `${row.prefix || ""}${row.firstName || ""} ${
row.lastName || ""
}`.trim();
},
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "position",
align: "left",
label: "ตำแหน่งในสายงาน",
sortable: false,
field: "position",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posType",
align: "left",
label: "ประเภทตำแหน่ง",
sortable: false,
field: "posType",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posLevel",
align: "left",
label: "ระดับตำแหน่ง",
sortable: false,
field: "posLevel",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
/** ฟังก์ชันดึงข้อมูลรายชื่อขรก. ที่ไม่อยู่ในโครงสร้าง */
async function fetchData() {
try {
showLoader();
const payload = {
...params.value,
posTypeId: filters.posTypeId,
posLevelId: filters.posLevelId,
position: filters.position.trim(),
keyword: filters.keyword.trim(),
};
const endpoint =
props.type === "current"
? config.API.orgSearchCurrentProfile
: config.API.orgSearchProfile;
const res = await http.post(endpoint, payload);
const result = res.data.result;
pagination.value.rowsNumber = result?.total || 0;
rows.value = result?.data || [];
} catch (error) {
messageError($q, error);
} finally {
hideLoader();
}
}
/** ฟังก์ชันดึงข้อมูลประเภทตำแหน่ง */
async function getPosType() {
try {
options.posType = await store.fetchPosType();
} catch (error) {
messageError($q, error);
}
}
/**
* ฟังก์ชันอัปเดตตัวเลือกระดับตำแหน่งเมื่อเลือกประเภทตำแหน่ง
* @param val ID เภทตำแหน่ง
*/
function updateSelectType(val: string) {
const listLevel: PosTypes | undefined = store.optionsData.dataType.find(
(e: PosTypes) => e.id === val
);
store.optionsData.posLevel =
listLevel?.posLevels?.map((e: PosLevels) => ({
id: e.id,
name: e.posLevelName,
})) || [];
options.posLevel = store.optionsData.posLevel;
filters.posLevelId = "";
searchData();
}
/** ฟังก์ชันค้นหาข้อมูล*/
function searchData() {
pagination.value.page = 1;
fetchData();
}
/**
* ฟังก์ชันเปิดหน้าทะเบียนขรก. ในแท็บใหม่
* @param personId ID ของขรก.
*/
function handleRedirect(personId: string) {
window.open(`/registry-officer/${personId}`, "_blank");
}
onMounted(() => {
fetchData();
getPosType();
});
</script>
<template>
<div class="col-12">
<!-- Filter Section -->
<div class="row col-12 q-col-gutter-sm q-mb-sm">
<div class="col-xs-12 col-sm-6 col-md-2">
<q-input
v-model="filters.position"
dense
outlined
label="ตำแหน่งในสายงาน"
@keydown.enter.prevent="searchData"
/>
</div>
<div class="col-xs-12 col-sm-6 col-md-2">
<q-select
label="ตำแหน่งประเภท"
v-model="filters.posTypeId"
:options="options.posType"
emit-value
dense
map-options
outlined
option-label="name"
option-value="id"
clearable
@clear="filters.posTypeId = ''"
@update:model-value="updateSelectType"
/>
</div>
<div class="col-xs-12 col-sm-6 col-md-2">
<q-select
label="ระดับตำแหน่ง"
v-model="filters.posLevelId"
:disable="!filters.posTypeId"
:options="options.posLevel"
emit-value
dense
map-options
outlined
option-label="name"
option-value="id"
clearable
@clear="filters.posLevelId = ''"
@update:model-value="searchData"
/>
</div>
<q-space />
<div class="col-xs-12 col-sm-6 col-md-2">
<q-input
v-model="filters.keyword"
dense
outlined
label="ค้นหาจากชื่อ-นามสกุล หรือเลขประจำตัวประชาชน"
@keydown.enter.prevent="searchData"
>
<template v-slot:append>
<q-icon name="search" class="cursor-pointer" @click="searchData" />
</template>
</q-input>
</div>
<div class="col-xs-12 col-sm-6 col-md-2">
<q-select
for="visibleColumns"
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
/>
</div>
</div>
<!-- Table Section -->
<div class="col-12">
<p-table
ref="table"
:columns="columns"
:rows="rows"
row-key="id"
flat
bordered
dense
class="custom-header-table"
:visible-columns="visibleColumns"
:rows-per-page-options="[1, 10, 25, 50, 100]"
v-model:pagination="pagination"
@request="onRequest"
>
<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">
<q-tr :props="props">
<q-td auto-width>
<q-btn
flat
dense
round
color="info"
icon="mdi-eye"
@click.prevent="handleRedirect(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>
</p-table>
</div>
</div>
</template>
<style scoped></style>