hrms-admin/src/modules/05_command/views/lists.vue
2025-07-18 15:50:52 +07:00

642 lines
19 KiB
Vue

<script setup lang="ts">
import { ref, onMounted, watch, computed } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import { useDataStore } from "@/modules/05_command/stores/main";
import type {
ListOrder,
Tabs,
DateOption,
} from "@/modules/05_command/interface/index/Main";
import type {
DataCommandSys,
DataTemplateDetail,
} from "@/modules/05_command/interface/response/Main";
import Header from "@/components/DialogHeader.vue";
import PageOrder from "@/modules/05_command/components/ViewPdf.vue";
import TemplateDetail from "@/modules/05_command/components/FormTemplate.vue";
const $q = useQuasar();
const mixin = useCounterMixin();
const store = useDataStore();
const { dialogConfirm, success, showLoader, hideLoader, messageError } = mixin;
const childTemplateDetailRef = ref<InstanceType<typeof TemplateDetail> | null>(
null
); //ref components ข้อความต้นแบบ
const childPageOrderRef = ref<InstanceType<typeof PageOrder> | null>(null); //ref components คำสั่ง
// Tabs
const tabs = ref<Tabs[]>([
{
value: "cover",
label: "คำสั่ง",
},
{
value: "attachment",
label: "บัญชีแนบท้าย",
},
{
value: "template",
label: "ข้อความต้นแบบ",
},
]);
/** เมื่อ แนบท้ายเป็น false ตัด tab attachment ออก */
const filteredTabs = computed(() =>
isAttachmentShow.value
? tabs.value
: tabs.value.filter((tab) => tab.value !== "attachment")
);
const isCode = ref<string>(""); // ตัวแปรเก็บ code
const isActive = ref<boolean>(true); // สถานะของรายการคำสั่ง
const isAttachment = ref<boolean>(true); // บัญชีแนบท้าย
const isSalary = ref<boolean>(true); // แก้ไขเงินเดือน
const isAttachmentShow = ref<boolean>(true); // บัญชีแนบท้าย show/off
const isUploadAttachment = ref<boolean>(false);
const dataCategory = ref<DateOption[]>([]); //ข้อมูล หมวดหมู่
const categoryOP = ref<DateOption[]>([]); // options หมวดหมู่
const commandId = ref<string>(""); // Id คำสั่ง
const activeCommandId = ref<string>(""); // Id คำสั่งที่เลือก
const name = ref<string>(""); // ชื่อคำสั่ง
const subtitle = ref<string>(""); // คำอธิบาย
const category = ref<string>(""); // หมวดหมู่
const listOrder = ref<ListOrder[]>([]); // list คำสั่ง
const status = ref<boolean>(false); // สถานะ
const isEdit = ref<boolean>(false); //เก็บ true/false เช็คแก้ไข
const dialogFormCommand = ref<boolean>(false); // model คำสั่ง
const dataTemplateDetail = ref<DataTemplateDetail>();
const keyword = ref<string>("");
const page = ref<number>(1); // หน้า
const pageSize = ref<number>(13); // จำนวนข้อมูล
const maxPage = ref<number>(0); // จำนวนหน้า
/** ฟังก์ชั่นดึงรายการประเภทคำสั่ง*/
async function fetchCommandType() {
showLoader();
await http
.get(config.API.commandType + `/admin`, {
params: {
page: page.value,
pageSize: pageSize.value,
isActive: isActive.value,
keyword: keyword.value,
},
})
.then(async (res) => {
const data = res.data.result;
listOrder.value = data.commandTypes;
maxPage.value = Math.ceil(data.total / pageSize.value);
if (activeCommandId.value) {
listOrder.value.find((e: ListOrder) => {
if (e.id === activeCommandId.value) {
selectInbox(e);
}
});
}
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** ฟังก์ชันดึงข้อมูลรายการหมวดหมู่*/
function fetchCommandSys() {
showLoader();
http
.get(config.API.commandSysList)
.then((res) => {
const data = res.data.result;
const option = data.map((e: DataCommandSys) => ({
id: e.id,
name: e.sysName,
}));
dataCategory.value = option;
categoryOP.value = option;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* ฟังก์ชั่นค้นหาคำสั่ง ตามสถานะ
* @param val สถานะ true/false
*/
function searchByStatus() {
activeCommandId.value = "";
page.value = 1;
pageSize.value = 13;
fetchCommandType();
}
/**
* เปิด dialog Edit
* @param data ข้อมูลคำสั่ง
*/
function onDialogEdit(data: ListOrder) {
commandId.value = data.id;
name.value = data.name;
status.value = data.isActive;
category.value = data.commandSysId;
subtitle.value = data.subtitle ? data.subtitle : "";
isEdit.value = true;
isAttachment.value = data.isAttachment;
isUploadAttachment.value = data.isUploadAttachment;
dialogFormCommand.value = true;
}
/**
* ปิด dialog
*/
function closeDialog() {
commandId.value = "";
isEdit.value = false;
dialogFormCommand.value = false;
subtitle.value = "";
name.value = "";
status.value = false;
category.value = "";
}
/** บันทึกข้อมูลการแก้ไขคำสั่ง*/
function onSubmit() {
dialogConfirm($q, async () => {
showLoader();
await http
.put(config.API.commandType + `/${commandId.value}`, {
name: name.value,
subtitle: subtitle.value,
commandSysId: category.value,
isActive: status.value,
isAttachment: isAttachment.value,
isSalary: isSalary.value,
isUploadAttachment: isUploadAttachment.value,
})
.then(async () => {
await fetchCommandType();
success($q, "บันทึกข้อมูลสำเร็จ");
closeDialog();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
/**
* เก็บ id list คำสั่งที่เลือก เพื่อใช้ class
*/
function selectInbox(data: ListOrder) {
isCode.value = data.code;
if (data.code === "C-PM-47") {
isAttachmentShow.value = false;
activeCommandId.value = "";
return;
}
store.currentTab =
!data.isAttachment && store.currentTab == "attachment"
? "cover"
: store.currentTab;
isAttachmentShow.value = data.isAttachment;
activeCommandId.value = data.id;
fetchDataCommandTypeId(data.id);
}
/**
* ฟังก์ชันดึงข้อมูลรายละเอียดคำสั่ง
* @param id คำสั่ง
*/
async function fetchDataCommandTypeId(id: string) {
showLoader();
await http
.get(config.API.commandType + `/${id}`)
.then(async (res) => {
const data = await res.data.result;
dataTemplateDetail.value = data;
await Promise.all([childTemplateDetailRef?.value?.fetchData(data)]);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* ฟังก์ชั่นค้นหาข้อมูลใน Select หมวดหมู่
* @param val คำค้นหา
* @param update ฟังก์ชั่นค้นหา
*/
function filterSelector(val: string, update: Function) {
update(() => {
categoryOP.value = dataCategory.value.filter(
(v: DateOption) => v.name.indexOf(val) > -1
);
});
}
/** เช็คการเปลี่ยนแปลงของค่า isActive*/
watch(
() => isActive.value,
() => {
searchByStatus();
}
);
/** เริ่มเมื่อโหลดหน้านี้*/
onMounted(async () => {
await Promise.all([fetchCommandType(), fetchCommandSys()]);
});
</script>
<template>
<div class="toptitle text-dark col-12 row items-center">
รายการคำสงและตนแบบ
</div>
<div class="row q-col-gutter-sm" style="height: 85vh; display: flex">
<div class="col-4">
<q-card bordered style="min-height: 100%">
<q-toolbar>
<q-input
borderless
dense
outlined
v-model="keyword"
placeholder="ค้นหา"
@keydown.enter.prevent="searchByStatus()"
>
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
<q-space />
<div class="row items-center no-wrap">
<q-label :class="`${isActive ? `text-primary` : `text-orange`}`">{{
`${isActive ? `ใช้งาน` : `ไม่ใช้งาน`}`
}}</q-label>
<label :class="`q-ml-lg toggle-control`">
<input type="checkbox" v-model="isActive" />
<span class="control"></span>
</label>
</div>
</q-toolbar>
<q-separator />
<q-card-section class="q-pa-sm">
<q-list v-for="(item, index) in listOrder" bordered :key="item.id">
<q-item
clickable
v-ripple
:active="activeCommandId === item.id"
active-class="my-menu_link"
@click="selectInbox(item)"
>
<q-item-section>
<q-item-label>
{{ item.name }}
<q-item-label caption>{{
item.subtitle ? item.subtitle : ""
}}</q-item-label>
</q-item-label>
</q-item-section>
<q-item-section side center @click.stop>
<q-icon
name="edit"
class="q-pa-none q-ml-xs"
color="edit"
flat
dense
@click="onDialogEdit(item)"
/>
<q-tooltip>แกไขขอม</q-tooltip>
</q-item-section>
</q-item>
</q-list>
<div
v-if="listOrder.length === 0"
class="full-width row flex-center text-accent q-gutter-sm"
>
<div class="text-grey-5">ไมพบขอม</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="around" v-if="listOrder.length > 0">
<q-pagination
class="q-pa-sm"
v-model="page"
active-color="primary"
color="dark"
:max="maxPage"
size="sm"
boundary-links
direction-links
:max-pages="5"
@update:model-value="fetchCommandType"
></q-pagination>
</q-card-actions>
</q-card>
</div>
<div class="col-8">
<q-card bordered style="height: 100%">
<div v-if="activeCommandId !== ''">
<q-tabs
dense
v-model="store.currentTab"
align="left"
indicator-color="primary"
active-color="primary bg-teal-1"
inline-label
class="text-body2 text-grey-7"
>
<q-tab
v-for="tab in filteredTabs"
:key="tab.value"
v-on:click="store.currentTab = tab.value"
:label="tab.label"
:name="tab.value"
class="q-py-xs"
/>
</q-tabs>
<q-separator />
<q-tab-panels v-model="store.currentTab" animated>
<q-tab-panel name="cover">
<PageOrder
ref="childPageOrderRef"
v-model:type="store.currentTab"
v-model:data-template-detail="dataTemplateDetail as DataTemplateDetail"
:command-id="activeCommandId"
:fetch-data-template="fetchDataCommandTypeId"
/>
</q-tab-panel>
<q-tab-panel name="attachment">
<PageOrder
ref="childPageOrderRef"
v-model:type="store.currentTab"
v-model:data-template-detail="dataTemplateDetail as DataTemplateDetail"
:command-id="activeCommandId"
:fetch-data-template="fetchDataCommandTypeId"
/>
</q-tab-panel>
<q-tab-panel name="template">
<TemplateDetail
ref="childTemplateDetailRef"
v-model:type="store.currentTab"
v-model:data-template-detail="dataTemplateDetail as DataTemplateDetail"
:command-id="activeCommandId"
:fetch-data-template="fetchDataCommandTypeId"
/>
</q-tab-panel>
</q-tab-panels>
</div>
<div
v-else-if="isCode == 'C-PM-47'"
style="height: auto; max-width: 100%; max-height: 90vh"
>
<div
class="absolute-center text-center text-grey-9"
style="width: 100%"
></div>
</div>
<div v-else style="height: auto; max-width: 100%; max-height: 90vh">
<div
class="absolute-center text-center text-grey-9"
style="width: 100%"
>
<div class="column items-center">
<q-icon
name="mdi-hand-pointing-left"
size="lg"
color="primary"
></q-icon>
<span>กรณาเลอกรายการคำส</span>
</div>
</div>
</div>
</q-card>
</div>
</div>
<q-dialog v-model="dialogFormCommand" persistent>
<q-card class="col-12" style="width: 40vw">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<Header
:tittle="isEdit ? 'แก้ไขคำสั่ง' : 'เพิ่มคำสั่ง'"
:close="closeDialog"
/>
<q-separator />
<q-card-section>
<div class="row q-col-gutter-sm">
<div class="col-12">
<q-input
outlined
dense
v-model="name"
label="ชื่อคำสั่ง"
class="inputgreen"
:rules="[(val:string) => !!val || `${'กรุณากรอกชื่อคำสั่ง'}`,]"
hide-bottom-space
></q-input>
</div>
<div class="col-12">
<q-input
outlined
dense
v-model="subtitle"
label="คำอธิบาย"
class="inputgreen"
hide-bottom-space
></q-input>
</div>
<div class="col-12">
<q-select
class="inputgreen"
v-model="category"
:label="`${'หมวดหมู่'}`"
dense
emit-value
map-options
option-label="name"
:options="categoryOP"
option-value="id"
lazy-rules
use-input
hide-selected
fill-input
hide-bottom-space
outlined
:rules="[(val:string) => !!val || `${'กรุณาเลือกหมวดหมู่'}`]"
@filter="(inputValue:string,
doneFn:Function) => filterSelector(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-12">
<div class="row items-center justify-between border_custom">
<p class="q-ma-none">สถานะการใชงาน</p>
<label :class="`toggle-control`">
<input type="checkbox" v-model="status" />
<span class="control"></span>
</label>
</div>
</div>
<div class="col-12">
<q-checkbox
v-model="isAttachment"
label="บัญชีแนบท้าย"
size="sm"
/>
<q-checkbox v-model="isSalary" label="แก้ไขเงินเดือน" size="sm" />
<q-checkbox
v-model="isUploadAttachment"
label="อัปโหลดบัญชีแนบท้าย"
size="sm"
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<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 lang="scss">
:deep(.scrollStyle .q-scrollarea__bar) {
background-color: #909090;
border-radius: 10px;
width: 5px;
opacity: 0.1;
right: 0px;
}
:deep(.scrollStyle .q-scrollarea__thumb) {
background-color: #909090;
border-radius: 10px;
width: 5px;
opacity: 0.5;
right: 0px;
}
.my-menu_link {
background: #ebf9f7 !important;
border-radius: 5px;
color: #1bb19ab8 !important;
}
.my-menu-link .q-hoverable {
border-radius: 5px;
}
.my-link {
font-size: 0.75rem;
padding: 2px;
min-height: 10px !important;
}
.my-link:hover {
border-radius: 5px;
}
.border_custom {
border-radius: 5px !important;
border: 1px solid #c8d3db;
height: 40px;
padding: 0 12px;
}
$toggle-background-color-on: #06884d;
$toggle-background-color-off: darkgray;
$toggle-control-color: white;
$toggle-width: 40px;
$toggle-height: 25px;
$toggle-gutter: 3px;
$toggle-radius: 50%;
$toggle-control-speed: 0.15s;
$toggle-control-ease: ease-in;
$toggle-radius: calc($toggle-height / 2);
$toggle-control-size: $toggle-height - ($toggle-gutter * 2);
.toggle-control {
display: block;
position: relative;
padding-left: $toggle-width;
margin-bottom: 12px;
cursor: pointer;
font-size: 22px;
user-select: none;
input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
input:checked ~ .control {
background-color: $toggle-background-color-on;
&:after {
left: $toggle-width - $toggle-control-size - $toggle-gutter;
}
}
.control {
position: absolute;
top: -7px;
left: -15px;
height: $toggle-height;
width: $toggle-width;
border-radius: $toggle-radius;
background-color: $toggle-background-color-off;
transition: background-color $toggle-control-speed $toggle-control-ease;
&:after {
content: "";
position: absolute;
left: $toggle-gutter;
top: $toggle-gutter;
width: $toggle-control-size;
height: $toggle-control-size;
border-radius: $toggle-radius;
background: $toggle-control-color;
transition: left $toggle-control-speed $toggle-control-ease;
}
}
}
</style>