444 lines
16 KiB
Vue
444 lines
16 KiB
Vue
<!-- =============================== -->
|
|
<!-- component เพิ่ม/แก้ไข ตำแหน่ง ของ โครงสร้างอัตรากำลัง -->
|
|
<!-- เป็นชุด q-select(dropdown) ที่ใช้ในการ mapping ตำแหน่งกับโครงสร้าง -->
|
|
<!-- เพื่อสร้าง โครงสร้างตำแหน่ง ใหม่ใน DB -->
|
|
<!-- Call API to fill all dropdown list -->
|
|
<!-- แก้ไขไม่ต้องมีจำนวน จะแก้ 1:1 check เรื่องการส่งค่าดีๆ -->
|
|
<!-- =============================== -->
|
|
<template>
|
|
<div v-if="isAddNew" class="row col-12 items-center q-pt-md">
|
|
<div class="bg-white q-px-sm topCard">
|
|
<span class="text-weight-medium">เพิ่มตำแหน่ง</span>
|
|
<q-btn
|
|
flat
|
|
color="primary"
|
|
size="12px"
|
|
icon="mdi-plus"
|
|
dense
|
|
class="q-mx-sm"
|
|
@click="addPositionItem()"
|
|
>
|
|
<q-tooltip>เพิ่มตำแหน่ง</q-tooltip>
|
|
</q-btn>
|
|
</div>
|
|
</div>
|
|
<!-- การ์ดเพิ่มตำแหน่ง-->
|
|
<q-form ref="myForm">
|
|
<div v-if="!isAddNew">
|
|
<div v-for="(item, index) in positions" :key="index">
|
|
<q-select
|
|
dense
|
|
outlined
|
|
v-model="item.positionMasterId"
|
|
:options="position"
|
|
label="ตำแหน่ง"
|
|
class="col-xs-9 col-sm-6 col-md-8"
|
|
use-input
|
|
input-debounce="0"
|
|
@filter="filterFn"
|
|
hide-bottom-space
|
|
option-label="positionPath"
|
|
option-value="id"
|
|
map-options
|
|
emit-value
|
|
clearable
|
|
:rules="[(val:any) => !!val || `${'กรุณาเลือกตำแหน่ง'}`]"
|
|
>
|
|
<template v-slot:option="scope">
|
|
<q-item v-bind="scope.itemProps">
|
|
<q-item-section>
|
|
<q-item-label>{{ scope.opt.positionPath }}</q-item-label>
|
|
<q-item-label caption>
|
|
<q-icon
|
|
class="q-mr-sm"
|
|
size="15px"
|
|
color="primary"
|
|
name="mdi-bookmark"
|
|
v-if="scope.opt.isDirector"
|
|
></q-icon>
|
|
{{ scope.opt.positionExecutive }}
|
|
{{ scope.opt.positionExecutiveSide }}
|
|
{{ scope.opt.positionLevel }}
|
|
{{ scope.opt.positionLine }}
|
|
{{ scope.opt.positionPathSide }}
|
|
{{ scope.opt.positionType }}
|
|
</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
</template>
|
|
<template v-slot:selected-item="scope">
|
|
<q-chip dense square class="q-my-none q-ml-xs q-mr-none">
|
|
{{ scope.opt.positionPath }}
|
|
</q-chip>
|
|
<q-item-label caption>
|
|
<q-icon
|
|
class="q-mr-sm"
|
|
size="15px"
|
|
color="primary"
|
|
name="mdi-bookmark"
|
|
v-if="scope.opt.isDirector"
|
|
></q-icon>
|
|
{{ scope.opt.positionExecutive }}
|
|
{{ scope.opt.positionExecutiveSide }}
|
|
{{ scope.opt.positionLevel }}
|
|
{{ scope.opt.positionLine }}
|
|
{{ scope.opt.positionPathSide }}
|
|
{{ scope.opt.positionType }}
|
|
</q-item-label>
|
|
</template>
|
|
<template v-slot:no-option>
|
|
<ddNoResultMsg />
|
|
</template>
|
|
</q-select>
|
|
<div class="col-xs-12 col-sm-12 col-md-12">
|
|
<q-input
|
|
:class="getClass(true)"
|
|
hide-bottom-space
|
|
:outlined="true"
|
|
dense
|
|
lazy-rules
|
|
:readonly="false"
|
|
:borderless="false"
|
|
v-model="item.positionUserNote"
|
|
:label="`${'หมายเหตุ'}`"
|
|
type="textarea"
|
|
/>
|
|
<!-- :rules="[(val) => !!val || `${'กรุณากรอกเงื่อนไขตำแหน่ง'}`]" -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<q-card v-else bordered flat class="q-py-sm">
|
|
<q-card
|
|
flat
|
|
class="bg-grey-2 q-pa-sm q-ma-sm row col-12"
|
|
v-for="(item, index) in positions"
|
|
:key="index"
|
|
>
|
|
<div class="col-12 row items-center q-col-gutter-xs">
|
|
<div>
|
|
<q-avatar
|
|
class="q-mr-sm"
|
|
size="25px"
|
|
color="grey-3"
|
|
text-color="grey-9"
|
|
>{{ index + 1 }}</q-avatar
|
|
>
|
|
</div>
|
|
<q-btn
|
|
flat
|
|
round
|
|
color="red"
|
|
size="10px"
|
|
class="q-mr-sm"
|
|
icon="mdi-trash-can-outline"
|
|
dense
|
|
@click="deletePositionItem(item)"
|
|
/>
|
|
|
|
<!-- label="ตำแหน่ง" -->
|
|
<q-select
|
|
dense
|
|
outlined
|
|
v-model="item.positionMasterId"
|
|
:options="position"
|
|
label="ตำแหน่ง"
|
|
class="col-xs-9 col-sm-6 col-md-8"
|
|
use-input
|
|
input-debounce="0"
|
|
@filter="filterFn"
|
|
hide-bottom-space
|
|
option-label="positionPath"
|
|
option-value="id"
|
|
map-options
|
|
emit-value
|
|
clearable
|
|
:rules="[(val:any) => !!val || `${'กรุณาเลือกตำแหน่ง'}`]"
|
|
>
|
|
<template v-slot:option="scope">
|
|
<q-item v-bind="scope.itemProps">
|
|
<q-item-section>
|
|
<q-item-label>{{ scope.opt.positionPath }}</q-item-label>
|
|
<q-item-label caption>
|
|
<q-icon
|
|
class="q-mr-sm"
|
|
size="15px"
|
|
color="primary"
|
|
name="mdi-bookmark"
|
|
v-if="scope.opt.isDirector"
|
|
></q-icon>
|
|
{{ scope.opt.positionExecutive }}
|
|
{{ scope.opt.positionExecutiveSide }}
|
|
{{ scope.opt.positionLevel }}
|
|
{{ scope.opt.positionLine }}
|
|
{{ scope.opt.positionPathSide }}
|
|
{{ scope.opt.positionType }}
|
|
</q-item-label>
|
|
</q-item-section>
|
|
</q-item>
|
|
</template>
|
|
<template v-slot:selected-item="scope">
|
|
<q-chip dense square class="q-my-none q-ml-xs q-mr-none">
|
|
{{ scope.opt.positionPath }}
|
|
</q-chip>
|
|
<q-item-label caption>
|
|
<q-icon
|
|
class="q-mr-sm"
|
|
size="15px"
|
|
color="primary"
|
|
name="mdi-bookmark"
|
|
v-if="scope.opt.isDirector"
|
|
></q-icon>
|
|
{{ scope.opt.positionPathSide }}
|
|
{{ scope.opt.positionExecutive }}
|
|
{{ scope.opt.positionLevel }}
|
|
{{ scope.opt.positionLine }}
|
|
|
|
<!-- {{ scope.opt.positionType }}
|
|
{{ scope.opt.positionExecutiveSide }} -->
|
|
</q-item-label>
|
|
</template>
|
|
<template v-slot:no-option>
|
|
<ddNoResultMsg />
|
|
</template>
|
|
</q-select>
|
|
<q-input
|
|
dense
|
|
outlined
|
|
v-model.number="item.count"
|
|
class="col-xs-3 col-sm-2 col-md-2"
|
|
type="number"
|
|
hide-bottom-space
|
|
label="จำนวน"
|
|
:rules="[(val:any) => val > 0 || `${'ต้องมากกว่า 0'}`]"
|
|
/>
|
|
<div class="col-xs-12 col-sm-12 col-md-12">
|
|
<q-input
|
|
:class="getClass(true)"
|
|
hide-bottom-space
|
|
:outlined="true"
|
|
dense
|
|
lazy-rules
|
|
:readonly="false"
|
|
:borderless="false"
|
|
v-model="item.positionUserNote"
|
|
:label="`${'หมายเหตุ'}`"
|
|
type="textarea"
|
|
/>
|
|
<!-- :rules="[(val) => !!val || `${'กรุณากรอกเงื่อนไขตำแหน่ง'}`]" -->
|
|
</div>
|
|
</div>
|
|
</q-card>
|
|
</q-card>
|
|
</q-form>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, defineAsyncComponent, watch, onMounted } from "vue";
|
|
import { useQuasar, QForm } from "quasar";
|
|
import type { PropType } from "vue";
|
|
import { useDataStore } from "@/stores/data";
|
|
import http from "@/plugins/http";
|
|
import config from "@/app.config";
|
|
import type { DataOption } from "../../interface/index/Main";
|
|
|
|
const ddNoResultMsg = defineAsyncComponent(
|
|
() => import("@/components/DropDownNoResultMsg.vue")
|
|
); //แสดงข้อความเมื่อ Dropdown Filter ไม่เจอข้อมูล
|
|
const dataStore = useDataStore();
|
|
const { loaderPage } = dataStore;
|
|
const $q = useQuasar(); // show dialog
|
|
const emit = defineEmits(["update:positions", "update:formprops"]);
|
|
const props = defineProps({
|
|
positions: Array,
|
|
formprops: QForm as PropType<QForm | null>,
|
|
isAddNew: {
|
|
type: Boolean,
|
|
required: true,
|
|
// default: true,
|
|
},
|
|
editObj: Object, //FE ส่งมาทั้งก้อน rowClickTree
|
|
});
|
|
|
|
const myForm = ref<QForm | null>(null);
|
|
|
|
watch(myForm, (form: QForm | null, prevForm: QForm | null) => {
|
|
if (!props.isAddNew) {
|
|
positions.value.push(JSON.parse(JSON.stringify(positionSet.value)));
|
|
if (!props.editObj != null && !props.editObj != undefined) {
|
|
// console.log("position.value", position.value);
|
|
// console.log("props.editObj", props.editObj);
|
|
positions.value[0].positionMasterId = props.editObj?.positionMasterId;
|
|
positions.value[0].positionUserNote = props.editObj?.positionUserNote;
|
|
}
|
|
}
|
|
emit("update:formprops", form);
|
|
});
|
|
|
|
const positions = ref<Array<any>>([]); //๋Array เก็บ positionSet ใช้ Array<object> แล้ว ขึ้นขีดแดงๆตรง html ข้างบน
|
|
const positionSet = ref<object>({
|
|
count: 1, // จำนวนตำแหน่งที่จะเพิ่ม
|
|
positionMasterId: "", // ชื่อตำแหน่ง จากระบบข้อมูลหลัก Table PositionMaster เช่น นักจัดการงานทั่วไป,พยาบาลชำนาญการ
|
|
positionUserNote: "",
|
|
}); //เก็บข้อมูลที่จะเลือกใน Drop Down แต่ละตัวจะต้องมี key ของมัน เพื่อส่งไปให้ API
|
|
emit("update:positions", positions.value);
|
|
|
|
const positionFilter = ref<Array<any>>([]); //for DropDown
|
|
const position = ref<Array<any>>([]); //for DropDown
|
|
|
|
onMounted(async () => {
|
|
loaderPage(false);
|
|
await fetchPositionMaster();
|
|
});
|
|
|
|
const fetchPositionMaster = async () => {
|
|
loaderPage(true);
|
|
await http
|
|
// .get(config.API.getPostionMasterDraft(false))
|
|
.get(config.API.getPostionMaster(false))
|
|
.then((res) => {
|
|
// console.log("psMaster:", res.data.result);
|
|
res.data.result.map((e: any) => {
|
|
positionFilter.value.push({
|
|
id: e.id,
|
|
isActive: e.isActive,
|
|
positionType: e.positionType,
|
|
positionLine: e.positionLine,
|
|
positionPath: e.positionPath,
|
|
positionPathSide: e.positionPathSide,
|
|
positionExecutive: e.positionExecutive,
|
|
positionExecutiveSide: e.positionExecutiveSide,
|
|
positionLevel: e.positionLevel.toString(),
|
|
positionStatus: e.positionStatus,
|
|
positionTypeId: e.positionTypeId,
|
|
positionMasterId: e.positionMasterId,
|
|
positionLineId: e.positionLineId,
|
|
positionPathId: e.positionPathId,
|
|
positionPathSideId: e.positionPathSideId,
|
|
positionExecutiveId: e.positionExecutiveId,
|
|
positionExecutiveSideId: e.positionExecutiveSideId,
|
|
positionLevelId: e.positionLevelId,
|
|
positionStatusId: e.positionStatusId,
|
|
positionCondition: e.positionCondition,
|
|
positionMasterUserNote: e.positionMasterUserNote,
|
|
isDirector: e.isDirector,
|
|
});
|
|
position.value = positionFilter.value;
|
|
});
|
|
})
|
|
.catch((e) => {
|
|
console.log(e);
|
|
})
|
|
.finally(() => {
|
|
loaderPage(false);
|
|
});
|
|
};
|
|
|
|
/**Fuction Filter DropDown
|
|
* q-select ส่งค่ามาให้เองจาก @filter="filterFn"
|
|
* @param val ค่าตัวพิมพ์ค้นหา
|
|
* @param update ทุกครั้งที่พิมพ์ค่า
|
|
*/
|
|
const filterFn = (val: string, update: any) => {
|
|
if (val === "") {
|
|
update(() => {
|
|
position.value = positionFilter.value;
|
|
//ไม่มีไม่แตกต่าง ไม่แน่ใจต้องใช้ไหม
|
|
// here you have access to "ref" which
|
|
// is the Vue reference of the QSelect
|
|
});
|
|
return;
|
|
}
|
|
|
|
update(() => {
|
|
position.value = positionFilter.value.filter(
|
|
(v: any) => v.positionPath != null && v.positionPath.indexOf(val) > -1
|
|
);
|
|
});
|
|
// console.log(positions.value);
|
|
};
|
|
|
|
/**Add new positionSet item into positions
|
|
*กรอกข้อมูลสำคัญครบถึงเพิ่ม ตำแหน่ง ใหม่ได้
|
|
*ข้อมูลไม่ครบแสดง Alert เตือนให้รู้เฉยๆ แล้วไม่ให้เพิ่ม
|
|
*/
|
|
const addPositionItem = () => {
|
|
// console.log(positions.value);
|
|
myForm.value!.validate().then((result) => {
|
|
if (result) {
|
|
positions.value.push(JSON.parse(JSON.stringify(positionSet.value)));
|
|
//ช้า คลิกเลือกแล้วไม่แสดงผล ทั้งๆที่ดูแล้วไม่น่าเกี่ยว positions.value.push(ref<any>(positionSet.value));
|
|
emit("update:positions", positions.value);
|
|
} else {
|
|
//เก็บไว้เผี่อแสดง pop-up ให้ user เห็น
|
|
console.log("validation fail");
|
|
}
|
|
});
|
|
};
|
|
|
|
/**Delete positionSet item from positions
|
|
* ลบตำแหน่งตัวที่กดลบ
|
|
* @param val data ใน item ที่จะลบ
|
|
*/
|
|
const deletePositionItem = (val: object) => {
|
|
//Check if val is completed before delete
|
|
//จะใช้ filter ต้องเป็น array เลยต้องจับใส่ [val]
|
|
if (!isEmptyPosition([val])) {
|
|
$q.dialog({
|
|
title: "ยืนยันการลบข้อมูล",
|
|
message: "มีข้อมูลอยู่ หากต้องการลบกด ตกลง",
|
|
cancel: true,
|
|
persistent: true,
|
|
})
|
|
.onOk(() => {
|
|
// console.log(">>>> OK");
|
|
positions.value = positions.value.filter((x: object) => x !== val); //คำสั่งลบ
|
|
// console.log(positions.value);
|
|
})
|
|
.onCancel(() => {
|
|
// console.log(">>>> Cancel");
|
|
})
|
|
.onDismiss(() => {
|
|
// console.log("I am triggered on both OK and Cancel");
|
|
});
|
|
} else {
|
|
positions.value = positions.value.filter((x: object) => x !== val);
|
|
}
|
|
emit("update:positions", positions.value);
|
|
};
|
|
|
|
/** Check if position is Empty
|
|
* -- return true/false
|
|
* @param items Array of position
|
|
*/
|
|
const isEmptyPosition = (items: any[]) => {
|
|
console.log("items", items);
|
|
const isEmpty = items.map((f: any) => !f.positionMasterId);
|
|
// ถ้าว่างเป็น true
|
|
// ถ้ามี data false
|
|
console.log("isEmpty", isEmpty[0]);
|
|
return isEmpty[0];
|
|
};
|
|
|
|
/**class จัดรูปแบบแสดงระหว่างข้อมูลที่แก้ไขหรือแสดงเฉยๆ
|
|
* copy from AddMappingPositions
|
|
* @param val ข้อมูล input สำหรับแก้ไขหรือไม่
|
|
*/
|
|
const getClass = (val: boolean) => {
|
|
return {
|
|
"full-width inputgreen cursor-pointer": val,
|
|
"full-width cursor-pointer": !val,
|
|
};
|
|
};
|
|
</script>
|
|
<style>
|
|
.text1 {
|
|
color: gray;
|
|
font-weight: 400;
|
|
padding-right: 2px;
|
|
font-size: 13px;
|
|
}
|
|
.text2 {
|
|
font-weight: 400;
|
|
padding-right: 10px;
|
|
}
|
|
</style>
|