Merge branch 'develop' into setthawut

This commit is contained in:
STW_TTTY\stwtt 2024-09-06 16:43:33 +07:00
commit 7a07a12e26
39 changed files with 2366 additions and 1783 deletions

View file

@ -91,4 +91,8 @@ export default {
orgDeceasedProfile: `${orgPos}/profile/search`,
orgProfileListKeycloak: () => `${orgProfile}/search-personal-no-keycloak`,
/** กำหนดสิทธิ์จัดการโครงสร้าง */
permissionOrg: `${organization}/permission-org`,
permissionOrgProfile: `${organization}/permission-org/profile`, // คนที่มีสิทธิ์จัดการโครงสร้าง
};

View file

@ -108,6 +108,11 @@ const menuList = readonly<any[]>([
label: "กำหนดสิทธิ์ (Permissions)",
path: "managePermission",
},
{
key: 2.0,
label: "กำหนดสิทธิ์จัดการโครงสร้าง",
path: "roleOrganization",
},
],
},
{

View file

@ -1,31 +1,22 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
/** importType*/
import type { QTableProps } from "quasar";
import type {
ObjectPosRef,
FormQuery,
} from "@/modules/01_metadata/interface/index/positionEmployee";
import type {
DataOption,
RowDetailPositions,
} from "@/modules/01_metadata/interface/request/position/index";
import type { FormQuery } from "@/modules/01_metadata/interface/index/positionEmployee";
import type { DataOption } from "@/modules/01_metadata/interface/request/position/index";
import type {
ResGroup,
ResLevel,
ResPossition,
} from "@/modules/01_metadata/interface/response/positionEmployee/Main";
/**importComponets*/
import DialogHeader from "@/components/DialogHeader.vue";
/**importStore*/
import { useCounterMixin } from "@/stores/mixin";
/**use*/
const $q = useQuasar();
const mixin = useCounterMixin();
@ -38,6 +29,7 @@ const {
dialogRemove,
} = mixin;
//table
const rows = ref<ResPossition[]>([]);
const columns = ref<QTableProps["columns"]>([
{
@ -83,47 +75,80 @@ const visibleColumns = ref<string[]>([
"posTypeName",
"posLevelName",
]);
const formQuery = reactive<FormQuery>({
type: "positionName",
keyword: "",
});
//
const optionFilter = ref<DataOption[]>([
{ id: "positionName", name: "ชื่อตำแหน่ง" },
{ id: "positionType", name: "กลุ่มงาน" },
{ id: "positionLevel", name: "ระดับชั้นงาน" },
]);
const formQuery = reactive<FormQuery>({
type: "positionName",
keyword: "",
});
const modalDialog = ref<boolean>(false);
const isStatusEdit = ref<boolean>(false);
const posId = ref<string>("");
const formDataPos = reactive({
posName: "",
posTypeName: "",
posLevelName: "",
});
const posNameRef = ref<object | null>(null);
const posTypeNameRef = ref<object | null>(null);
const posLevelNameRef = ref<object | null>(null);
const objectRef: ObjectPosRef = {
posName: posNameRef,
posTypeName: posTypeNameRef,
posLevelName: posLevelNameRef,
};
const posTypeMain = ref<ResGroup[]>([]);
const posTypeOp = ref<DataOption[]>([]);
const posLevelOp = ref<DataOption[]>([]);
function deletePos(id: string) {
dialogRemove($q, () => {
/**
* งกนดงขอมลรายการตำแหนงจาก API
* @param statusType สถานะการสงประเภทคนหา (true = ไมงประเภทคนหา)
*
* เกบขอมลรายการตำแหนงจากไวใน rows.value
*/
async function fetchData(statusType: boolean = false) {
showLoader();
await http
.get(
config.API.orgEmployeePos +
`?keyword=${formQuery.keyword}&type=${statusType ? "" : formQuery.type}`
)
.then(async (res) => {
rows.value = await res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* งกนยนยนการบนทกขอมลตำแหน
*
* เม isStatusEdit เป false จะทำการเพมขอมลรายการขอมลตำแหน าไมจะทำการแกไขขอม
* และเมอเพมขอมลเสรจจำทำการดงขอมลรายการตำแหนงใหม
*/
function onSubmit() {
dialogConfirm($q, async () => {
const body = {
posDictName: formDataPos.posName,
posTypeId: formDataPos.posTypeName,
posLevelId: formDataPos.posLevelName,
};
showLoader();
http
.delete(config.API.orgEmployeePosById(id))
.then(() => {
success($q, "ลบข้อมูลสำเร็จ");
fetchData();
// Phat API
const url = !isStatusEdit.value
? config.API.orgEmployeePos
: config.API.orgEmployeePosById(posId.value);
await http[!isStatusEdit.value ? "post" : "put"](url, body)
.then(async () => {
await fetchData();
success($q, "บันทีกข้อมูลสำเร็จ");
closeDialog();
})
.catch((err) => {
messageError($q, err);
@ -134,41 +159,61 @@ function deletePos(id: string) {
});
}
async function onClickOpenDialog(typeEdit: boolean = false, data: any = []) {
modalDialog.value = true;
/**
* งกนยนยนการลบรายการขอมลตำแหน
*
* @param id รายการขอมลตำแหนงทจะลบ
* เมอลบขอมลเสรจจำทำการดงขอมลรายการตำแหนงใหม
*/
function deletePos(id: string) {
dialogRemove($q, async () => {
showLoader();
await http
.delete(config.API.orgEmployeePosById(id))
.then(async () => {
await fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
/**
* งกนเป popup เพอแกไขหรอคดลอกขอมลตำแหน
* @param typeEdit สถานะการแกไข (true = แกไข, false = เพมใหมหรอคดลอก)
* @param data อมลตำแหนงทองการแกไขหรอคดลอก
*/
async function onClickOpenDialog(
typeEdit: boolean = false,
data: ResPossition = {} as ResPossition
) {
isStatusEdit.value = typeEdit;
modalDialog.value = true;
//
await fetchType();
updatePosTypeName(data.posTypeId);
isStatusEdit.value = typeEdit;
modalDialog.value = true;
// id
await updatePosTypeName(data.posTypeId);
if (data) {
posId.value = data.id;
setTimeout(() => {
formDataPos.posName = data.posDictName;
formDataPos.posTypeName = data.posTypeId;
formDataPos.posLevelName = data.posLevelId;
}, 200);
formDataPos.posName = data.posDictName;
formDataPos.posTypeName = data.posTypeId;
formDataPos.posLevelName = data.posLevelId;
}
}
async function fetchData(statusFetch: boolean = false) {
showLoader();
await http
.get(
config.API.orgEmployeePos +
`?keyword=${formQuery.keyword}&type=${
statusFetch ? "" : formQuery.type
}`
)
.then((res) => {
rows.value = res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* งกนดงขอมลกลมงาน
*/
async function fetchType() {
if (posTypeMain.value.length === 0) {
showLoader();
@ -191,47 +236,11 @@ async function fetchType() {
}
}
async function onClickSubmit() {
const hasError = [];
for (const key in objectRef) {
if (Object.prototype.hasOwnProperty.call(objectRef, key)) {
const property = objectRef[key];
if (property.value && typeof property.value.validate === "function") {
const isValid = property.value.validate();
hasError.push(isValid);
}
}
}
if (hasError.every((result) => result === true)) {
dialogConfirm($q, () => {
submit();
});
}
}
async function submit() {
const body = {
posDictName: formDataPos.posName,
posTypeId: formDataPos.posTypeName,
posLevelId: formDataPos.posLevelName,
};
showLoader();
try {
const url = !isStatusEdit.value
? config.API.orgEmployeePos
: config.API.orgEmployeePosById(posId.value);
await http[!isStatusEdit.value ? "post" : "put"](url, body);
success($q, "บันทีกข้อมูลสำเร็จ");
fetchData();
} catch (err) {
messageError($q, err);
} finally {
hideLoader();
closeDialog();
}
}
function updatePosTypeName(id: string) {
/**
* งกนหาตวเลอกระดบชนงานตาม ID กลมงาน
* @param id กลมงาน
*/
async function updatePosTypeName(id: string) {
const posLevel = posTypeMain.value.find((e: ResGroup) => e.id === id);
posLevelOp.value =
posLevel?.posLevels.map((e: ResLevel) => ({
@ -241,6 +250,10 @@ function updatePosTypeName(id: string) {
formDataPos.posLevelName = "";
}
/**
* งกนป Popup แกไขหรอคดลอกขอมลตำแหน
* และกำหนด formDataPos ไปเปนคาวาง
*/
function closeDialog() {
modalDialog.value = false;
formDataPos.posName = "";
@ -248,8 +261,11 @@ function closeDialog() {
formDataPos.posLevelName = "";
}
onMounted(() => {
fetchData(true);
/**
* hook ทำงานเม Components กเรยกใชงาน
*/
onMounted(async () => {
await fetchData(true);
});
</script>
<template>
@ -403,7 +419,7 @@ onMounted(() => {
<q-dialog v-model="modalDialog" class="dialog" persistent>
<q-card style="width: 350px">
<form @submit.prevent="onClickSubmit">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader
:tittle="`${
isStatusEdit
@ -426,7 +442,7 @@ onMounted(() => {
label="ชื่อตำแหน่ง"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกชื่อตำแหน่ง'}`]"
:rules="[(val:string) => !!val || `${'กรุณากรอกชื่อตำแหน่ง'}`]"
class="inputgreen"
/>
</div>
@ -445,7 +461,7 @@ onMounted(() => {
option-value="id"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณาเลือกกลุ่มงาน'}`]"
:rules="[(val:string) => !!val || `${'กรุณาเลือกกลุ่มงาน'}`]"
@update:model-value="updatePosTypeName"
class="inputgreen"
/>
@ -466,7 +482,7 @@ onMounted(() => {
option-value="id"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณาเลือกระดับชั้นงาน'}`]"
:rules="[(val:string) => !!val || `${'กรุณาเลือกระดับชั้นงาน'}`]"
class="inputgreen"
/>
</div>
@ -476,7 +492,7 @@ onMounted(() => {
<q-card-actions align="right">
<q-btn type="submit" :label="`บันทึก`" color="public" />
</q-card-actions>
</form>
</q-form>
</q-card>
</q-dialog>
</template>

View file

@ -1,28 +1,22 @@
<script setup lang="ts">
import { ref, onMounted, reactive } from "vue";
import { useRouter } from "vue-router";
import { useQuasar } from "quasar";
import { useRouter } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
/** importType*/
import type { QTableProps } from "quasar";
import type { ObjectGroupRef } from "@/modules/01_metadata/interface/index/positionEmployee";
import type { ResGroup } from "@/modules/01_metadata/interface/response/positionEmployee/Main";
import type { FrmDataGroup } from "@/modules/01_metadata/interface/request/positionEmployee";
/** importComponents*/
import dialogHeader from "@/components/DialogHeader.vue";
/** importStore*/
import { usePositionEmployeeDataStore } from "@/modules/01_metadata/stores/positionEmployeeStore";
import { useCounterMixin } from "@/stores/mixin";
/** use*/
const $q = useQuasar();
const store = usePositionEmployeeDataStore();
const router = useRouter();
const mixin = useCounterMixin();
const {
dialogRemove,
dialogConfirm,
@ -30,8 +24,11 @@ const {
messageError,
showLoader,
hideLoader,
} = mixin;
} = useCounterMixin();
//Table
const rows = ref<ResGroup[]>([]); //
const filterKeyword = ref<string>(""); //
const columns = ref<QTableProps["columns"]>([
{
name: "posTypeName",
@ -67,35 +64,28 @@ const visibleColumns = ref<string[]>([
"posTypeRank",
]);
/** from เพิ่มข้อมูลกลุ่มงาน */
//
const formDataGroup = reactive<FrmDataGroup>({
posTypeName: "",
posTypeShortName: "",
posTypeRank: null,
});
/** formRef*/
const posTypeNameRef = ref<Object | null>(null);
const posTypeShortNameRef = ref<Object | null>(null);
const posTypeRankRef = ref<Object | null>(null);
const objectGroupRef: ObjectGroupRef = {
posTypeName: posTypeNameRef,
posTypeShortName: posTypeShortNameRef,
posTypeRank: posTypeRankRef,
};
const editId = ref<string>("");
const filterKeyword = ref<string>("");
const dialog = ref<boolean>(false);
const dialogStatus = ref<string>("");
const rows = ref<ResGroup[]>([]);
/**
* งกนดงขอมลรายการกลมงาน API
*
* เกบขอมลรรายการกลมงานไวใน rows.value
*/
async function fetchData() {
showLoader();
await http
.get(config.API.orgEmployeeType)
.then(async (res) => {
rows.value = res.data.result;
rows.value = await res.data.result;
})
.catch((err) => {
messageError($q, err);
@ -105,54 +95,47 @@ async function fetchData() {
});
}
async function onClickSubmit() {
const hasError = [];
for (const key in objectGroupRef) {
if (Object.prototype.hasOwnProperty.call(objectGroupRef, key)) {
const property = objectGroupRef[key];
if (property.value && typeof property.value.validate === "function") {
const isValid = property.value.validate();
hasError.push(isValid);
}
}
}
if (hasError.every((result) => result === true)) {
dialogConfirm($q, () => {
submit();
});
}
/**
* งกนเป popup แกไขขอมลกลมงาน
* @param data อมลกลมงานทจะแกไข
*
* กำหนด dialogStatus เป edit และกำหนดให ฟอรมขอมลกลมงาน เป อมลทจะแกไข
*
*/
function onClickOpenDialogEdit(data: ResGroup) {
dialogStatus.value = "edit";
dialog.value = true;
editId.value = data.id;
formDataGroup.posTypeName = data.posTypeName;
formDataGroup.posTypeShortName = data.posTypeShortName;
formDataGroup.posTypeRank = data.posTypeRank;
}
async function submit() {
const body: FrmDataGroup = {
posTypeName: formDataGroup.posTypeName,
posTypeShortName: formDataGroup.posTypeShortName,
posTypeRank: Number(formDataGroup.posTypeRank),
};
showLoader();
try {
/**
* นยนการบนทกขอมลรายการกลมงาน
*
* dialogStatus เป 'create' จะทำการเพมขอมลรายการกลมงาน าไมจะทำการแกไขขอม
* เมอบนทกขอมลเสรจจะเรยก function fetchData() เพอดงขอมลรายการกลมงานใหม
*
*/
function onSubmit() {
dialogConfirm($q, async () => {
showLoader();
const body: FrmDataGroup = {
posTypeName: formDataGroup.posTypeName,
posTypeShortName: formDataGroup.posTypeShortName,
posTypeRank: Number(formDataGroup.posTypeRank),
};
// Path API
const url =
dialogStatus.value === "create"
? config.API.orgEmployeeType
: config.API.orgEmployeeTypeById(editId.value);
await http[dialogStatus.value === "create" ? "post" : "put"](url, body);
success($q, "บันทีกข้อมูลสำเร็จ");
fetchData();
} catch (err) {
messageError($q, err);
} finally {
hideLoader();
closeDialog();
}
}
function onClickDelete(id: string) {
dialogRemove($q, () => {
showLoader();
http
.delete(config.API.orgEmployeeTypeById(id))
.then(() => {
fetchData();
await http[dialogStatus.value === "create" ? "post" : "put"](url, body)
.then(async () => {
await fetchData();
success($q, "บันทีกข้อมูลสำเร็จ");
closeDialog();
})
.catch((err) => {
messageError($q, err);
@ -163,32 +146,31 @@ function onClickDelete(id: string) {
});
}
function onClickOpenDialogEdit(data: ResGroup) {
dialogStatus.value = "edit";
dialog.value = true;
editId.value = data.id;
formDataGroup.posTypeName = data.posTypeName;
formDataGroup.posTypeShortName = data.posTypeShortName;
formDataGroup.posTypeRank = data.posTypeRank;
}
/**
* งกนไปหนารายการระดบชนงาน
* @param id กลมงาน
*/
function onClickDetail(id: string) {
router.push(`/master-data/position-employee/level/${id}`);
}
/**
* งกนป popup แกไขหรอเพมขอมลกลมงาน
*
* และกำหนดให ฟอรมขอมลกลมงาน เปนคาวาง
*/
function closeDialog() {
dialog.value = false;
clearFormData();
}
function clearFormData() {
formDataGroup.posTypeName = "";
formDataGroup.posTypeShortName = "";
formDataGroup.posTypeRank = null;
}
onMounted(() => {
fetchData();
/**
* hook ทำงานเม Components กเรยกใชงาน
*/
onMounted(async () => {
await fetchData();
});
</script>
@ -268,17 +250,6 @@ onMounted(() => {
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<!-- <q-btn
color="red"
flat
dense
round
icon="mdi-delete"
clickable
@click.stop="onClickDelete(props.row.id)"
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn> -->
</q-td>
<q-td v-for="col in props.cols" :key="col.id">
{{ col.value ? col.value : "-" }}
@ -289,7 +260,7 @@ onMounted(() => {
<q-dialog v-model="dialog" class="dialog" persistent>
<q-card style="width: 350px">
<form @submit.prevent="onClickSubmit">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<q-card-section class="flex justify-between" style="padding: 0">
<dialog-header
:tittle="
@ -313,7 +284,7 @@ onMounted(() => {
lazy-rules
borderless
bg-color="white"
:rules="[(val) => val.length > 0 || 'กรุณากรอกชื่อกลุ่มงาน']"
:rules="[(val:string) => val.length > 0 || 'กรุณากรอกชื่อกลุ่มงาน']"
hide-bottom-space
class="inputgreen"
/>
@ -329,7 +300,7 @@ onMounted(() => {
label="อักษรย่อกลุ่มงาน"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกอักษรย่อกลุ่มงาน'}`]"
:rules="[(val:string) => !!val || `${'กรุณากรอกอักษรย่อกลุ่มงาน'}`]"
class="inputgreen"
/>
</div>
@ -345,7 +316,7 @@ onMounted(() => {
borderless
min="1"
bg-color="white"
:rules="[(val) => val != undefined || 'กรุณากรอกระดับกลุ่มงาน']"
:rules="[(val:string) => val != undefined || 'กรุณากรอกระดับกลุ่มงาน']"
hide-bottom-space
mask="############"
class="inputgreen"
@ -354,19 +325,11 @@ onMounted(() => {
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
id="onSubmit"
type="submit"
dense
unelevated
label="บันทึก"
color="public"
class="q-px-md"
>
<q-btn id="onSubmit" type="submit" label="บันทึก" color="public">
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</form>
</q-form>
</q-card>
</q-dialog>
</template>

View file

@ -11,7 +11,7 @@ import type {
ResGroup,
ResLevel,
} from "@/modules/01_metadata/interface/response/positionEmployee/Main";
import type { ObjectLevelRef } from "@/modules/01_metadata/interface/index/positionEmployee";
import type { DataGroup } from "@/modules/01_metadata/interface/index/positionEmployee";
import type { FormDataLevel } from "@/modules/01_metadata/interface/request/positionEmployee";
/** importComponts*/
@ -23,21 +23,15 @@ import { useCounterMixin } from "@/stores/mixin";
/**use*/
const $q = useQuasar();
const storeOption = useMainOptionDataStore();
const route = useRoute();
const router = useRouter();
const posName = ref<string>("");
const posTypeId = ref<string>(route.params.id.toString());
const {
dialogRemove,
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
} = useCounterMixin();
const storeOption = useMainOptionDataStore();
const { dialogConfirm, showLoader, hideLoader, messageError, success } =
useCounterMixin();
// Table
const rows = ref<DataGroup[]>([]); //
const filter = ref<string>(""); //
const columns = ref<QTableProps["columns"]>([
{
name: "no",
@ -60,7 +54,7 @@ const columns = ref<QTableProps["columns"]>([
{
name: "posTypeName",
align: "left",
label: "กลุ่มงาน",
label: "ระดับชั้นงาน",
sortable: true,
field: "posTypeName",
headerStyle: "font-size: 14px",
@ -77,42 +71,38 @@ const columns = ref<QTableProps["columns"]>([
style: "font-size: 14px",
},
]);
const rows = ref<any>([]);
const visibleColumns = ref<string[]>([
"no",
"posLevelName",
"posTypeName",
"posLevelAuthority",
]);
const filter = ref<string>("");
const levelId = ref<string>("");
const titleName = ref<string | null>("");
const posTypeId = ref<string>(route.params.id.toString()); // id
const titleName = ref<string | null>(""); //
const levelId = ref<string>(""); // id
const isStatusEdit = ref<boolean>(false); //
const modalDialog = ref<boolean>(false); // popup
//
const formDataLevel = reactive<FormDataLevel>({
posLevelName: null,
posTypeName: "",
posLevelAuthority: "",
});
/** formRef*/
const posLevelNameRef = ref<Object | null>(null);
const commanderRef = ref<Object | null>(null);
const objectLevelRef: ObjectLevelRef = {
posLevelName: posLevelNameRef,
posLevelAuthority: commanderRef,
};
const id = ref<string>(route.params.id.toString());
function fetchData() {
/**
* งกนดงขอมลรายการระดบชนงาน API
*
* เกบขอมลรรายการระดบชนงานไวใน rows.value
*/
async function fetchData() {
showLoader();
http
.get(config.API.orgEmployeeTypeById(id.value))
.then((res) => {
await http
.get(config.API.orgEmployeeTypeById(posTypeId.value))
.then(async (res) => {
titleName.value = res.data.result.posTypeName ?? null;
formDataLevel.posTypeName = res.data.result.posTypeName;
rows.value = res.data.result.posLevels.map((x: any) => ({
rows.value = await res.data.result.posLevels.map((x: ResLevel) => ({
...x,
posTypeName: res.data.result.posTypeName,
}));
@ -125,9 +115,17 @@ function fetchData() {
});
}
const isStatusEdit = ref<boolean>(false);
const modalDialog = ref<boolean>(false);
function onClickOpenDialog(statusEdit: boolean = false, data: any = []) {
/**
* งกนเป popup แกไขขอมลระดบชนงาน
* @param data อมลระดบชนงานทจะแกไข
*
* กำหนด isStatusEdit เป true และกำหนดให ฟอรมขอมลระดบชนงาน เป อมลทจะแกไข
*
*/
function onClickOpenDialog(
statusEdit: boolean = false,
data: DataGroup = {} as DataGroup
) {
isStatusEdit.value = statusEdit;
modalDialog.value = true;
@ -141,6 +139,11 @@ function onClickOpenDialog(statusEdit: boolean = false, data: any = []) {
}
}
/**
* งกนป popup แกไขหรอเพมขอมลระดบชนงาน
*
* และกำหนดให ฟอรมขอมลระดบชนงาน เปนคาวาง
*/
function onClickCloseDialog() {
modalDialog.value = false;
formDataLevel.posLevelName = null;
@ -148,66 +151,57 @@ function onClickCloseDialog() {
formDataLevel.posLevelAuthority = "";
}
function onClickSubmit() {
const hasError = [];
for (const key in objectLevelRef) {
if (Object.prototype.hasOwnProperty.call(objectLevelRef, key)) {
const property = objectLevelRef[key];
if (property.value && typeof property.value.validate === "function") {
const isValid = property.value.validate();
hasError.push(isValid);
}
}
}
if (hasError.every((result) => result === true)) {
dialogConfirm($q, () => {
submit();
});
}
}
async function submit() {
const body = {
posLevelName: Number(formDataLevel.posLevelName),
posTypeId: posTypeId.value,
posLevelRank: Number(formDataLevel.posLevelName),
posLevelAuthority: formDataLevel.posLevelAuthority,
};
showLoader();
try {
/**
* นยนการบนทกขอมลรายการระดบชนงาน
*
* dialogStatus เป 'false' จะทำการเพมขอมลรายการระดบชนงาน าไมจะทำการแกไขขอม
* เมอบนทกขอมลเสรจจะเรยก function fetchData() เพอดงขอมลรายการระดบชนงานใหม
*
*/
function onSubmit() {
dialogConfirm($q, async () => {
showLoader();
const body = {
posLevelName: Number(formDataLevel.posLevelName),
posTypeId: posTypeId.value,
posLevelRank: Number(formDataLevel.posLevelName),
posLevelAuthority: formDataLevel.posLevelAuthority,
};
// Phat APi
const url = !isStatusEdit.value
? config.API.orgEmployeelevel
: config.API.orgEmployeelevelById(levelId.value);
await http[!isStatusEdit.value ? "post" : "put"](url, body);
success($q, "บันทีกข้อมูลสำเร็จ");
fetchData();
onClickCloseDialog();
} catch (err) {
messageError($q, err);
} finally {
hideLoader();
}
}
function onClickDelete(id: string) {
dialogRemove($q, () => {
http
.delete(config.API.orgEmployeelevelById(id))
.then(() => {
success($q, "ลบข้อมูลสำเร็จ");
fetchData();
await http[!isStatusEdit.value ? "post" : "put"](url, body)
.then(async () => {
await fetchData();
success($q, "บันทีกข้อมูลสำเร็จ");
onClickCloseDialog();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
/**
* งกนแปลงตำแหนงผอำนาจ
* @param val าของผอำนาจ
* @returns ตำแหนงผอำนาจ
*/
function convertPosLevelAuthority(val: string) {
const result = storeOption.posLevelAuthorityOption.find((e) => e.id === val);
return result?.label;
}
/**
* hook ทำงานเม Components กเรยกใชงาน
*
* าม posTypeId จะดงขอมลรายการระดบชนงาน
*/
onMounted(() => {
posTypeId.value && fetchData();
});
@ -295,16 +289,6 @@ onMounted(() => {
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<!-- <q-btn
color="red"
flat
dense
round
icon="mdi-delete"
@click="onClickDelete(props.row.id)"
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn> -->
</q-td>
<q-td v-for="(col, index) in props.cols" :key="col.id">
<div v-if="col.name == 'no'">
@ -325,7 +309,7 @@ onMounted(() => {
<q-dialog v-model="modalDialog" class="dialog" persistent>
<q-card style="width: 350px">
<form @submit.prevent="onClickSubmit">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<q-card-section class="flex justify-between" style="padding: 0">
<DialogHeader
:tittle="isStatusEdit ? 'แก้ไขข้อมูล' : 'เพิ่มข้อมูล'"
@ -333,7 +317,7 @@ onMounted(() => {
/>
</q-card-section>
<q-separator color="grey-4" />
<q-separator />
<q-card-section class="row q-gutter-y-md">
<div class="col-12">
<q-input
@ -346,8 +330,10 @@ onMounted(() => {
borderless
bg-color="white"
hide-bottom-space
mask="#######################################"
:rules="[(val) => !!val || 'กรุณากรอกระดับชั้นงาน']"
mask="#"
reverse-fill-mask
:rules="[(val:string) => !!val || 'กรุณากรอกระดับชั้นงาน']"
class="inputgreen"
/>
</div>
@ -366,7 +352,8 @@ onMounted(() => {
borderless
bg-color="white"
hide-bottom-space
:rules="[(val) => !!val || 'กรุณาเลือกผู้มีอำนาจสั่งบรรจุ']"
:rules="[(val:string) => !!val || 'กรุณาเลือกผู้มีอำนาจสั่งบรรจุ']"
class="inputgreen"
/>
</div>
@ -374,7 +361,7 @@ onMounted(() => {
<q-select
ref="posTypeIdRef"
v-model="formDataLevel.posTypeName"
label="กลุ่มงาน"
label="ระดับชั้นงาน"
outlined
dense
bg-color="white"
@ -384,21 +371,13 @@ onMounted(() => {
/>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
id="onSubmit"
type="submit"
dense
unelevated
label="บันทึก"
color="public"
class="q-px-md"
>
<q-btn id="onSubmit" type="submit" label="บันทึก" color="public">
<q-tooltip>นทกขอม</q-tooltip>
</q-btn>
</q-card-actions>
</form>
</q-form>
</q-card>
</q-dialog>
</template>

View file

@ -1,8 +1,10 @@
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import type { QTableProps } from "quasar";
import type {
@ -11,17 +13,14 @@ import type {
RowDetailPositions,
} from "@/modules/01_metadata/interface/request/position/index";
import DialogAddPosition from "@/modules/01_metadata/components/position/DialogAddPosition.vue";
import { useCounterMixin } from "@/stores/mixin";
import DialogFormPosition from "@/modules/01_metadata/components/position/DialogFormPosition.vue";
const $q = useQuasar();
const rowsPositionSelect = ref<RowDetailPositions[]>([]);
const mixin = useCounterMixin();
const { showLoader, hideLoader, messageError, success, dialogRemove } = mixin;
const { showLoader, hideLoader, messageError, success, dialogRemove } =
useCounterMixin();
/** Table*/
const rows = ref<RowDetailPositions[]>([]);
const rowsPositionSelect = ref<RowDetailPositions[]>([]); //
const columns = ref<QTableProps["columns"]>([
{
name: "no",
@ -107,6 +106,7 @@ const visibleColumns = ref<string[]>([
"positionArea",
]);
//
const formPositionSelect = reactive<FormPositionSelect>({
positionId: "",
posTypeId: "",
@ -119,17 +119,15 @@ const formPositionSelect = reactive<FormPositionSelect>({
positionArea: "",
isSpecial: false,
});
const editPosition = ref<boolean>(false);
const copyPosition = ref<boolean>(false);
const modalFormPosition = ref<boolean>(false); // DialogFormPosition
const statusEdit = ref<boolean>(false); //
const copyPosition = ref<boolean>(false); //
const modalAddPosition = ref<boolean>(false);
/** input ค้นหา */
const searchRef = ref<any>(null);
const search = ref<string>("");
const type = ref<string>("positionName");
const search = ref<string>(""); //
const type = ref<string>("ALL"); //
const isReadonly = ref<boolean>(false); //
const optionFilter = ref<DataOption[]>([
{ id: "ALL", name: "ทั้งหมด" },
{ id: "positionName", name: "ตำแหน่งในสายงาน" },
{ id: "positionField", name: "สายงาน" },
{ id: "positionType", name: "ประเภทตำแหน่ง" },
@ -140,8 +138,39 @@ const optionFilter = ref<DataOption[]>([
]);
/**
* ดลอกขอม
* @param data อมลตำแหน
* งขอมลรายการขอมลตำแหนงขาราชการ
*/
async function fetchData() {
showLoader();
await http
.get(
config.API.orgPosPosition + `?keyword=${search.value}&type=${type.value}`
)
.then(async (res) => {
rowsPositionSelect.value = await res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* เป DialogFormPosition สำหลบเพมขอมลตำแหน
*/
function popupAdd() {
modalFormPosition.value = true;
}
/**
* ดลอกขอมลตำแหนงไปยงแบบฟอร
*
* @param data อมลตำแหนงทองการคดลอก
* - ดลอกขอมลจาก data ไปย formPositionSelect เพอแสดงในแบบฟอร
* - เป DialogFormPosition สำหลบคดลอกขอม
* - copyPosition` เป็น true
*/
function copyDetiail(data: RowDetailPositions) {
formPositionSelect.positionId = data.id;
@ -153,12 +182,17 @@ function copyDetiail(data: RowDetailPositions) {
formPositionSelect.positionExecutive = data.posExecutiveId;
formPositionSelect.positionExecutiveField = data.positionExecutiveField;
formPositionSelect.positionArea = data.positionArea;
modalAddPosition.value = true;
modalFormPosition.value = true;
copyPosition.value = true;
}
/**
* แกไขขอม
* @param data อมลตำแหน
*
* @param data อมลตำแหนงทองการแกไข
* - ดลอกขอมลจาก data ไปย formPositionSelect เพอแสดงในแบบฟอร
* - เป DialogFormPosition สำหลบแกไขขอม
* - statusEdit = true
*/
function editDetiail(data: RowDetailPositions) {
formPositionSelect.positionId = data.id;
@ -171,8 +205,32 @@ function editDetiail(data: RowDetailPositions) {
formPositionSelect.positionExecutiveField = data.positionExecutiveField;
formPositionSelect.positionArea = data.positionArea;
formPositionSelect.isSpecial = data.isSpecial;
modalAddPosition.value = true;
editPosition.value = true;
modalFormPosition.value = true;
statusEdit.value = true;
}
/**
* นยนการลบรายการขอมลตำแหนงขาราชการ
*
* @param id อมลตำแหนงขาราชการ
* - เมอการลบสำเร จะดงขอมลใหมเพอแสดงผล และแสดงขอความสำเร
*/
function deletePos(id: string) {
dialogRemove($q, () => {
showLoader();
http
.delete(config.API.orgPosPositionById(id))
.then(async () => {
await fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
/**
@ -186,137 +244,81 @@ function inputEdit(val: boolean) {
};
}
async function searchInput() {
showLoader();
await http
.get(
config.API.orgPosPosition + `?keyword=${search.value}&type=${type.value}`
)
.then((res) => {
rowsPositionSelect.value = res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
function addPosition(data: RowDetailPositions) {
const isIdExist = rows.value.some((item: any) => item.id === data.id);
if (!isIdExist) {
rows.value = [data, ...rows.value];
}
}
function deletePos(id: string) {
dialogRemove($q, () => {
showLoader();
http
.delete(config.API.orgPosPositionById(id))
.then(() => {
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
searchInput();
hideLoader();
});
});
}
function popupAdd() {
modalAddPosition.value = true;
}
async function fetchData() {
search.value = "";
showLoader();
await http
.get(config.API.orgPosPosition + `?keyword=&type=ALL`)
.then((res) => {
rowsPositionSelect.value = res.data.result;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
onMounted(() => {
fetchData();
/**
* hook ทำงานเม Components กเรยกใชงาน
*/
onMounted(async () => {
await fetchData();
});
</script>
<template>
<div class="row col-12 q-mb-sm">
<div class="col-md-2">
<q-btn
id="addComplaints"
for="addComplaints"
size="12px"
flat
round
color="primary"
icon="mdi-plus"
@click="popupAdd()"
><q-tooltip>เพมตำเเหน </q-tooltip></q-btn
>
</div>
<div class="row col-md-10 q-col-gutter-sm">
<div class="col-md-4">
<q-select
label="ค้นหาจาก"
v-model="type"
:options="optionFilter"
emit-value
dense
map-options
outlined
option-label="name"
option-value="id"
/>
</div>
<div class="col-md-6">
<q-input
ref="searchRef"
:class="inputEdit(isReadonly)"
v-model="search"
outlined
dense
lazy-rules
label="คำค้น"
hide-bottom-space
>
<template v-slot:append>
<q-icon
v-if="search"
name="cancel"
@click="fetchData()"
class="cursor-pointer"
></q-icon>
</template>
</q-input>
</div>
<div class="row col-md-2">
<q-form @submit.prevent="fetchData()">
<div class="row col-12 q-mb-sm">
<div class="col-md-2">
<q-btn
id="addComplaints"
for="addComplaints"
size="12px"
flat
round
color="primary"
icon="search"
label="ค้นหา"
class="full-width"
@click="searchInput()"
/>
icon="mdi-plus"
@click="popupAdd()"
><q-tooltip>เพมตำเเหน </q-tooltip></q-btn
>
</div>
<div class="row col-md-10 q-col-gutter-sm">
<div class="col-md-4">
<q-select
label="ค้นหาจาก"
v-model="type"
:options="optionFilter"
emit-value
dense
map-options
outlined
option-label="name"
option-value="id"
:clearable="type !== 'ALL'"
@clear="type = 'ALL'"
/>
</div>
<div class="col-md-6">
<q-input
:class="inputEdit(isReadonly)"
v-model="search"
outlined
dense
lazy-rules
label="คำค้น"
hide-bottom-space
>
<template v-slot:append>
<q-icon
v-if="search"
name="cancel"
@click="search = ''"
class="cursor-pointer"
></q-icon>
</template>
</q-input>
</div>
<div class="row col-md-2">
<q-btn
color="primary"
icon="search"
label="ค้นหา"
class="full-width"
type="submit"
/>
</div>
</div>
</div>
</div>
</q-form>
<div class="full-width q-mt-sm">
<d-table
@ -333,7 +335,7 @@ onMounted(() => {
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width></q-th>
<q-th auto-width />
<q-th
v-for="col in props.cols"
:key="col.name"
@ -388,12 +390,7 @@ onMounted(() => {
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
<q-td
v-for="col in props.cols"
:key="col.name"
:props="props"
@click="addPosition(props.row)"
>
<q-td v-for="col in props.cols" :key="col.name" :props="props">
<div v-if="col.name == 'no'">
{{ props.rowIndex + 1 }}
</div>
@ -415,11 +412,11 @@ onMounted(() => {
</d-table>
</div>
<DialogAddPosition
v-model:add-position="modalAddPosition"
<DialogFormPosition
v-model:add-position="modalFormPosition"
v-model:form-data="formPositionSelect"
v-model:edit-check="editPosition"
v-model:edit-check="statusEdit"
v-model:copy-check="copyPosition"
:get-data="searchInput"
:get-data="fetchData"
/>
</template>

View file

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

View file

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

View file

@ -1,50 +1,22 @@
<script setup lang="ts">
import { onMounted, ref } from "vue";
import type { QTableProps } from "quasar";
import { useQuasar } from "quasar";
import type {
RowListForm,
ListMenu,
} from "@/modules/01_metadata/interface/request/position";
import http from "@/plugins/http";
import config from "@/app.config";
import DialogAdd from "@/modules/01_metadata/components/position/DialogAddExecutive.vue";
import { useCounterMixin } from "@/stores/mixin";
import type { QTableProps } from "quasar";
import type { RowListForm } from "@/modules/01_metadata/interface/request/position";
import DialogAdd from "@/modules/01_metadata/components/position/DialogFormExecutive.vue";
const $q = useQuasar();
const mixin = useCounterMixin();
const {
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
dialogRemove,
} = mixin;
const dataEdit = ref<any>();
const isEdit = ref<boolean>(false);
const modalAdd = ref<boolean>(false);
const filterKeyword = ref<string>("");
const rows = ref<RowListForm[]>([]);
const listMenu = ref<ListMenu[]>([
{
label: "แก้ไข",
icon: "mdi-pencil",
type: "copy",
color: "teal-6",
},
{
label: "ลบ",
icon: "delete",
type: "remove",
color: "red",
},
]);
const { showLoader, hideLoader, messageError } = useCounterMixin();
// Table
const rows = ref<RowListForm[]>([]); //
const filterKeyword = ref<string>(""); //
const visibleColumns = ref<string[]>([
"no",
"posExecutiveName",
@ -80,13 +52,22 @@ const columns = ref<QTableProps["columns"]>([
},
]);
function getData() {
const dataEdit = ref<RowListForm>(); //
const isEdit = ref<boolean>(false); //
const modalPosExecutive = ref<boolean>(false); // popup
/**
* งรายการขอมลตำแหนงทางการบรหาร
*
* เกบขอมลรายการตำแหนงทางการบรหารใน rows.value
*/
async function getData() {
showLoader();
http
.get(config.API.orgPosExecutive)
.then((res) => {
const dataList = res.data.result;
rows.value = dataList;
.then(async (res) => {
const data = await res.data.result;
rows.value = data;
})
.catch((e) => {
messageError($q, e);
@ -95,36 +76,29 @@ function getData() {
hideLoader();
});
}
/**
* เป popup เพอเพมขอมลรายากรตำแหนงทางการบรหาร
*/
function popUpAdd() {
modalAdd.value = true;
modalPosExecutive.value = true;
}
function editPopUp(data: RowListForm[]) {
/**
* เป popup เพอแกไขขอมลรายากรตำแหนงทางการบรหาร
* @param data อมลรายการตำแหนงทางการบรหารทองการแกไข
*/
function editPopUp(data: RowListForm) {
modalPosExecutive.value = true;
dataEdit.value = data;
modalAdd.value = true;
isEdit.value = true;
}
function deletePos(id: string) {
dialogRemove($q, () => {
showLoader();
http
.delete(config.API.orgPosExecutiveById(id))
.then(() => {
success($q, "ลบข้อมูลสำเร็จ");
getData();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
onMounted(() => {
getData();
/**
* hook ทำงานเม Components กเรยกใชงาน
*/
onMounted(async () => {
await getData();
});
</script>
@ -232,7 +206,7 @@ onMounted(() => {
</div>
<DialogAdd
v-model:executive="modalAdd"
v-model:executive="modalPosExecutive"
v-model:form-data="dataEdit"
v-model:edit="isEdit"
:get-data="getData"

View file

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

View file

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

View file

@ -0,0 +1,175 @@
<script setup lang="ts">
import { ref, reactive, watch } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import type { RowListForm } from "@/modules/01_metadata/interface/request/position/index";
import DialogHeader from "@/components/DialogHeader.vue";
const $q = useQuasar();
const { dialogConfirm, showLoader, success, hideLoader, messageError } =
useCounterMixin();
/**
* props
*/
const modal = defineModel<boolean>("executive", { required: true }); // popup
const formData = defineModel<any>("formData", { required: true }); //
const isEdit = defineModel<any>("edit", { required: true }); //
const props = defineProps({
getData: Function,
});
const isReadonly = ref<boolean>(false); //
//
const formExecutive = reactive<RowListForm>({
id: "",
posExecutiveName: "",
posExecutivePriority: null,
});
/**
* นยนการบนทกขอมลรายการตำแหนงทางการบรหาร
*
* isEdit เป false จะทำการเพมขอมลรายการตำแหนงทางการบรหาร าไมจะทำการแกไขขอม
* เมอบนทกขอมลเสรจจะเรยก function props.getData() เพอดงขอมลรายการตำแหนงทางการบรหาร
*
*/
function onSubmit() {
dialogConfirm($q, async () => {
showLoader();
const body = {
posExecutiveName: formExecutive.posExecutiveName,
posExecutivePriority: formExecutive.posExecutivePriority, //
};
// Path API
const path = isEdit.value
? config.API.orgPosExecutive + `/${formExecutive.id}`
: config.API.orgPosExecutive;
// method
const method = isEdit.value ? "put" : "post";
await http[method](path, body)
.then(async () => {
await props.getData?.();
success($q, "บันทึกข้อมูลสำเร็จ");
close();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
/**
* popup ฟอรมขอมลรายการตำแหนงทางการบรหาร
*
* และเคลยร formExecutive อมลรายการตำแหนงทางการบรหารใหเปนคาวาง
*/
function close() {
modal.value = false;
formExecutive.id = "";
formExecutive.posExecutiveName = "";
formExecutive.posExecutivePriority = null;
isEdit.value = false;
}
/**
* งค css ออกไปตามเงอนไข
* @param val true/false
*/
function inputEdit(val: boolean) {
return {
"full-width cursor-pointer inputgreen ": val,
"full-width cursor-pointer inputgreen": !val,
};
}
/**
* เมอค modal เป true จะทำการรเซตคาของฟอร formExecutive
*
* และเม isEdit เป true จะกำหนดขอมลลงในฟอร formExecutive
*/
watch(
() => modal.value,
async () => {
if (modal.value) {
const dataList = await formData.value;
formExecutive.id = "";
formExecutive.posExecutiveName = "";
formExecutive.posExecutivePriority = null;
if (isEdit.value == true) {
formExecutive.id = dataList.id;
formExecutive.posExecutiveName = dataList.posExecutiveName;
formExecutive.posExecutivePriority = dataList.posExecutivePriority;
}
}
}
);
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="min-width: 350px">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<DialogHeader
:tittle="
isEdit ? `แก้ไขตำแหน่งทางการบริหาร` : 'เพิ่มตำแหน่งทางการบริหาร'
"
:close="close"
/>
<q-separator />
<q-card-section>
<div class="row q-col-gutter-sm col-12">
<div class="col-12">
<q-input
ref="posExecutiveNameRef"
v-model="formExecutive.posExecutiveName"
:class="inputEdit(isReadonly)"
dense
outlined
for="#posExecutiveName"
label="ชื่อตำแหน่งทางการบริหาร"
lazy-rules
hide-bottom-space
:rules="[
(val:string) => !!val || `${'กรุณากรอกชื่อตำแหน่งทางการบริหาร'}`,
]"
/>
</div>
<div class="col-12">
<q-input
ref="posExecutivePriorityRef"
v-model="formExecutive.posExecutivePriority"
:class="inputEdit(isReadonly)"
dense
outlined
type="number"
for="#posExecutivePriority"
label="ลำดับความสำคัญ"
lazy-rules
hide-bottom-space
:rules="[(val:string) => !!val || `${'กรุณากรอกลำดับความสำคัญ'}`]"
/>
</div>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn type="submit" :label="`บันทึก`" color="public" />
</q-card-actions>
</q-form>
</q-card>
</q-dialog>
</template>

View file

@ -1,8 +1,10 @@
<script setup lang="ts">
import { ref, reactive, watch, defineProps } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import type {
DataOption,
@ -10,136 +12,152 @@ import type {
OptionType,
OptionLevel,
OptionExecutive,
FormPositionSelect,
} from "@/modules/01_metadata/interface/request/position/index";
import type { ResPosType } from "@/modules/01_metadata/interface/response/position/Main";
import DialogHeader from "@/components/DialogHeader.vue";
import { useCounterMixin } from "@/stores/mixin";
const executiveOpsMain = ref<DataOption[]>([]);
const levelOps = ref<DataOption[]>([]);
const levelOpsMain = ref<DataOption[]>([]);
const typeOps = ref<DataOption[]>([]);
const typeOpsMain = ref<DataOption[]>([]);
const dataLevel = ref<any>();
const executiveOps = ref<DataOption[]>([]);
const formPositionSelect = reactive<FormPositionSelectDialog>({
positionId: "",
positionName: "",
positionField: "",
positionType: "",
positionLevel: "",
positionExecutive: "",
positionExecutiveField: "",
positionArea: "",
});
const $q = useQuasar();
const isSpecial = ref<boolean>(false);
const props = defineProps({
getData: Function,
});
const { dialogConfirm, showLoader, success, hideLoader, messageError } =
useCounterMixin();
const isReadonly = ref<boolean>(false); //
/**
* props
*/
const modal = defineModel<boolean>("addPosition", { required: true });
const formData = defineModel<any>("formData", { required: true });
const formData = defineModel<FormPositionSelect>("formData", {
required: true,
});
const editCheck = defineModel<boolean>("editCheck", { required: true });
const copyCheck = defineModel<boolean>("copyCheck", { required: true });
const props = defineProps({
getData: Function,
});
/** ฟังก์ชั่นตรวจสอบความถูกต้องของข้อมูลในฟอร์ม */
function validateFormPositionEdit() {
// const hasError = [];
// for (const key in objectPositionSelectRef) {
// if (Object.prototype.hasOwnProperty.call(objectPositionSelectRef, key)) {
// const property = objectPositionSelectRef[key];
// if (property.value && typeof property.value.validate === "function") {
// const isValid = property.value.validate();
// hasError.push(isValid);
// }
// }
// }
// if (hasError.every((result) => result === true)) {
if (editCheck.value == true) {
saveSelectEdit();
} else {
onSubmitSelectEdit();
}
// }
//
const executiveOpsMain = ref<DataOption[]>([]); //
const executiveOps = ref<DataOption[]>([]); //
const typeOpsMain = ref<DataOption[]>([]); //
const typeOps = ref<DataOption[]>([]); //
const levelOpsMain = ref<DataOption[]>([]); //
const levelOps = ref<DataOption[]>([]); //
const isReadonly = ref<boolean>(false); //
const dataType = ref<ResPosType[]>([]); //
//
const formPositionSelect = reactive<FormPositionSelectDialog>({
positionId: "",
positionName: "", //
positionField: "", //
positionType: "", //*
positionLevel: "", //
positionExecutive: "", //
positionExecutiveField: "", //
positionArea: "", ///
});
const isSpecial = ref<boolean>(false); //
/**
* เรยกรายการประเภทตำแหน
*
* งกนนงขอมลประเภทตำแหนงจาก API และเซตคาใหบตวเลอกประเภทตำแหน
*/
async function fetchType() {
showLoader();
await http
.get(config.API.orgPosType)
.then(async (res) => {
const data = await res.data.result;
dataType.value = data; // dataType
typeOpsMain.value = data.map((e: OptionType) => ({
id: e.id,
name: e.posTypeName,
}));
typeOps.value = typeOpsMain.value;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/** ฟังชั่น บันทึก */
function saveSelectEdit() {
dialogConfirm(
$q,
async () => {
showLoader();
const body = {
posDictName: formPositionSelect.positionName,
posDictField: formPositionSelect.positionField, //
posTypeId: formPositionSelect.positionType, //*
posLevelId: formPositionSelect.positionLevel, //*
posExecutiveId: formPositionSelect.positionExecutive, //
posDictExecutiveField: formPositionSelect.positionExecutiveField, //
posDictArea: formPositionSelect.positionArea, ///
isSpecial: isSpecial.value,
};
await http
.put(config.API.orgPosPositionById(formPositionSelect.positionId), body)
.then(() => {
success($q, "เพิ่มข้อมูลสำเร็จ");
clearFormPositionSelect();
modal.value = false;
editCheck.value = false;
props.getData?.();
/**
* เรยกรายการตำแหนงทางการบรหาร
*
* งกนนงขอมลตำแหนงทางการบรหารจาก API และเซตคาใหบตวเลอกตำแหนงทางการบรหาร
*/
async function fetchExecutive() {
showLoader();
await http
.get(config.API.orgPosExecutive)
.then(async (res) => {
executiveOpsMain.value = await res.data.result.map(
(e: OptionExecutive) => ({
id: e.id,
name: e.posExecutiveName,
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
},
"ยืนยันการเพิ่มตำแหน่ง",
"ต้องการยืนยันการเพิ่มตำแหน่งนี้ใช่หรือไม่?"
);
}
/** ฟังชั่น บันทึก */
function onSubmitSelectEdit() {
dialogConfirm(
$q,
async () => {
showLoader();
const body = {
posDictName: formPositionSelect.positionName,
posDictField: formPositionSelect.positionField, //
posTypeId: formPositionSelect.positionType, //*
posLevelId: formPositionSelect.positionLevel, //*
posExecutiveId: formPositionSelect.positionExecutive,
posDictExecutiveField: formPositionSelect.positionExecutiveField, //
posDictArea: formPositionSelect.positionArea, ///
isSpecial: isSpecial.value,
};
await http
.post(config.API.orgPosPosition, body)
.then(() => {
success($q, "เพิ่มข้อมูลสำเร็จ");
clearFormPositionSelect();
modal.value = false;
editCheck.value = false;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
},
"ยืนยันการเพิ่มตำแหน่ง",
"ต้องการยืนยันการเพิ่มตำแหน่งนี้ใช่หรือไม่?"
);
);
executiveOps.value = executiveOpsMain.value;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* นทกขอมลตำแหน
*
* editCheck เป true จำทำการแกไขขอม าไมจะเปนการ เพมขอม
* นทกขอมลเสร จะทำการโหลด อมลรายการตำแหนงขาราชการ ใหม
*/
function onSubmit() {
dialogConfirm($q, async () => {
showLoader();
const body = {
posDictName: formPositionSelect.positionName,
posDictField: formPositionSelect.positionField, //
posTypeId: formPositionSelect.positionType, //*
posLevelId: formPositionSelect.positionLevel, //*
posExecutiveId: formPositionSelect.positionExecutive, //
posDictExecutiveField: formPositionSelect.positionExecutiveField, //
posDictArea: formPositionSelect.positionArea, ///
isSpecial: isSpecial.value,
};
// path API
const path = editCheck.value
? config.API.orgPosPositionById(formPositionSelect.positionId)
: config.API.orgPosPosition;
// method
const method = editCheck.value ? http.put : http.post;
await method(path, body)
.then(async () => {
await props.getData?.();
success($q, "บันทึกข้อมูลสำเร็จ");
modal.value = false;
editCheck.value = false;
clearFormPositionSelect();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
/**
* เคลยรอมลในฟอรมการเลอกตำแหน
*
* เซตคาทกฟลดใน formPositionSelect ใหเปนค default
*/
async function clearFormPositionSelect() {
formPositionSelect.positionId = "";
formPositionSelect.positionName = "";
@ -151,8 +169,39 @@ async function clearFormPositionSelect() {
formPositionSelect.positionArea = "";
}
/**
* ปเดตตวเลอกประเภทตำแหนงและระดบตำแหนงทสอดคลองกบประเภททเลอก
* @param val id ของประเภทตำแหนงทเลอก
*/
async function updateSelectType(val: string) {
//
const listLevel = dataType.value.find((e: ResPosType) => e.id === val);
//
if (listLevel) {
levelOpsMain.value = listLevel.posLevels.map((e: OptionLevel) => ({
id: e.id,
name: e.posLevelName,
}));
levelOps.value = levelOpsMain.value;
}
}
/**
* popup ฟอรมขอมลตำแหน
*
* และทำการเรยก function clearFormPositionSelect() เพอเคลยรอมลในฟอรมการเลอกตำแหน
*/
function close() {
modal.value = false;
editCheck.value = false;
copyCheck.value = false;
clearFormPositionSelect();
}
/**
* งค css ออกไปตามเงอนไข
*
* @param val true/false
*/
function inputEdit(val: boolean) {
@ -162,63 +211,24 @@ function inputEdit(val: boolean) {
};
}
/** function เรียกรายการประเภทตำแหน่ง */
async function fetchType() {
showLoader();
await http
.get(config.API.orgPosType)
.then((res) => {
const data = res.data.result;
dataLevel.value = data;
typeOpsMain.value = data.map((e: OptionType) => ({
id: e.id,
name: e.posTypeName,
}));
typeOps.value = typeOpsMain.value;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
setTimeout(() => {
hideLoader();
}, 1000);
});
}
/** function เรียกรายการตำแหน่งทางการบริหาร */
async function fetchExecutive() {
showLoader();
await http
.get(config.API.orgPosExecutive)
.then((res) => {
executiveOpsMain.value = res.data.result.map((e: OptionExecutive) => ({
id: e.id,
name: e.posExecutiveName,
}));
executiveOps.value = executiveOpsMain.value;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
setTimeout(() => {
hideLoader();
}, 1000);
});
}
/**
* watch าของ modal popup
*/
watch(
() => modal.value,
() => {
async () => {
// modal
if (modal.value == true) {
fetchType();
fetchExecutive();
// fetchType fetchExecutive
await Promise.all([fetchType(), fetchExecutive()]);
//
if (editCheck.value || copyCheck.value) {
const dataList = formData.value;
setTimeout(() => {
updateSelectType(dataList.posTypeId);
}, 1000);
const dataList = formData.value; // formData
//
await updateSelectType(dataList.posTypeId);
formPositionSelect.positionId = dataList.positionId;
formPositionSelect.positionName = dataList.positionName;
formPositionSelect.positionField = dataList.positionField;
@ -233,28 +243,12 @@ watch(
}
}
);
function updateSelectType(val: string) {
const listLevel = dataLevel.value.find((e: any) => e.id === val);
levelOpsMain.value = listLevel.posLevels.map((e: OptionLevel) => ({
id: e.id,
name: e.posLevelName,
}));
levelOps.value = levelOpsMain.value;
}
function close() {
modal.value = false;
editCheck.value = false;
copyCheck.value = false;
clearFormPositionSelect();
}
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="min-width: 50vw">
<q-form greedy @submit.prevent="validateFormPositionEdit">
<q-form greedy @submit.prevent="onSubmit">
<DialogHeader
:tittle="`${editCheck ? 'แก้ไขข้อมูลตำแหน่ง' : 'เพิ่มข้อมูลตำแหน่ง'}`"
:close="close"
@ -274,7 +268,7 @@ function close() {
label="ตำแหน่งในสายงาน"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกตำแหน่งในสายงาน'}`]"
:rules="[(val:string) => !!val || `${'กรุณากรอกตำแหน่งในสายงาน'}`]"
/>
</div>
<div class="col-6">
@ -301,7 +295,7 @@ function close() {
label="สายงาน"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณากรอกสายงาน'}`]"
:rules="[(val:string) => !!val || `${'กรุณากรอกสายงาน'}`]"
/>
</div>
<div class="col-6">
@ -320,7 +314,7 @@ function close() {
option-value="id"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณาเลือกประเภทตำแหน่ง'}`]"
:rules="[(val:string) => !!val || `${'กรุณาเลือกประเภทตำแหน่ง'}`]"
/>
</div>
<div class="col-6">
@ -339,7 +333,7 @@ function close() {
option-value="id"
lazy-rules
hide-bottom-space
:rules="[(val) => !!val || `${'กรุณาเลือกระดับตำแหน่ง'}`]"
:rules="[(val:string) => !!val || `${'กรุณาเลือกระดับตำแหน่ง'}`]"
/>
</div>
<div class="col-6">

View file

@ -0,0 +1,485 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { useQuasar } from "quasar";
import { useRouter, useRoute } from "vue-router";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import { usePositionDataStore } from "@/modules/01_metadata/stores/positionListStore";
import { useMainOptionDataStore } from "@/modules/01_metadata/stores/main";
import type { QInput, QTableProps } from "quasar";
import dialogHeader from "@/components/DialogHeader.vue";
const $q = useQuasar();
const route = useRoute();
const router = useRouter();
const store = usePositionDataStore();
const storeOption = useMainOptionDataStore();
const {
dialogRemove,
dialogConfirm,
showLoader,
hideLoader,
messageError,
success,
} = useCounterMixin();
//table
const filterKeyword = ref<string>(""); //
const columns = [
{
name: "no",
align: "left",
label: "ลำดับ",
sortable: false,
field: "no",
headerStyle: "font-size: 14px; width:0px",
style: "font-size: 14px",
},
{
name: "posLevelName",
align: "left",
label: "ชื่อระดับตำแหน่ง",
sortable: true,
field: "posLevelName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posTypeName",
align: "left",
label: "ประเภทตำแหน่ง",
sortable: true,
field: "posTypeName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posLevelRank",
align: "left",
label: "ระดับของระดับตำแหน่ง",
sortable: true,
field: "posLevelRank",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "posLevelAuthority",
align: "left",
label: "ผู้มีอำนาจสั่งบรรจุ",
sortable: true,
field: "posLevelAuthority",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "createdAt",
align: "center",
label: "วันที่สร้าง",
sortable: true,
field: "createdAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdatedAt",
align: "center",
label: "วันที่แก้ไข",
sortable: true,
field: "lastUpdatedAt",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
{
name: "lastUpdateFullName",
align: "left",
label: "ผู้ดำเนินการ",
sortable: true,
field: "lastUpdateFullName",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
sort: (a: string, b: string) =>
a.localeCompare(b, undefined, { numeric: true, sensitivity: "base" }),
},
] as const satisfies QTableProps["columns"];
const visibleColumns = ref<string[]>([
"no",
"posTypeName",
"posLevelName",
"posLevelRank",
"posLevelAuthority",
]);
const id = ref<string>(route.params.id.toString()); //
const posName = ref<string>(""); //
const dialog = ref<boolean>(false); // modal popu ,
const dialogStatus = ref<string>(""); //
const editId = ref<string>(""); // id
const posLevelName = ref<string>(""); //
const posLevelRank = ref<number>(); //
const posLevelAuthority = ref<string>(""); //
/**
* งขอมลรายการระดบประเภทตำแหนงตาม id ประเภทตำแหน
*
* กำหนดชอของ ประเภทตำแหน ในตวแปร posName.velue
* นทกขอมลรายการระดบประเภทตำแหนงใน Store positionTypeStore
*/
async function fetchData() {
showLoader();
await http
.get(config.API.orgPosTypeId(id.value))
.then(async (res) => {
posName.value = res.data.result.posTypeName;
store.save(res.data.result.posLevels);
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* นยนการบนทกขอมลรายการระดบของประเภทตำแหน
*
* dialogStatus เป 'create' จะทำการเพมขอมลรายการระดบของประเภทตำแหน าไมจะทำการแกไขขอม
* เมอบนทกขอมลเสรจจะเรยก function fetchData() เพอดงขอมลระดบของประเภทตำแหนงใหม
*
*/
async function onSubmit() {
if (
posLevelName.value.length > 0 &&
posLevelAuthority.value.length > 0 &&
posLevelRank.value !== undefined &&
posLevelRank.value > 0
) {
dialogConfirm($q, async () => {
showLoader();
// Path API
const path =
dialogStatus.value === "create"
? config.API.orgPosLevel //
: config.API.orgPosLevelId(editId.value); //
// method
const method = dialogStatus.value === "create" ? "post" : "put";
await http[method](path, {
posLevelName: posLevelName.value,
posLevelRank: posLevelRank.value,
posLevelAuthority:
posLevelAuthority.value == "" ? "" : posLevelAuthority.value,
posTypeId: id.value,
})
.then(async () => {
await fetchData();
success($q, "บันทึกข้อมูลสำเร็จ");
closeDialog();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
}
/**
* นยนการลบขอมลรายการระดบของประเภทตำแหน
* @param id รายการระดบของประเภทตำแหน
*/
async function deleteData(id: string) {
await http
.delete(config.API.orgPosLevelId(id))
.then(async () => {
await fetchData();
success($q, "ลบข้อมูลสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* popup ฟอรมขอมลระดบของประเภทตำแหน
*/
function closeDialog() {
dialog.value = false;
}
/**
* hook ทำงานเม Components กเรยกใชงาน
*/
onMounted(async () => {
await fetchData();
});
</script>
<template>
<div class="toptitle text-dark col-12 row items-center">
<q-btn
icon="mdi-arrow-left"
unelevated
round
dense
flat
color="primary"
class="q-mr-sm"
@click="router.go(-1)"
/>
รายการระดบของประเภทตำแหน{{ posName }}
</div>
<q-card flat bordered>
<q-card-section>
<q-toolbar style="padding: 0">
<q-btn
flat
round
color="primary"
icon="add"
@click.stop="
() => {
dialogStatus = 'create';
dialog = true;
posLevelName = '';
posLevelRank = undefined;
posLevelAuthority = '';
}
"
>
<q-tooltip> เพมขอม </q-tooltip>
</q-btn>
<q-space />
<div class="row q-gutter-sm">
<q-input
outlined
dense
v-model="filterKeyword"
label="ค้นหา"
></q-input>
<q-select
v-model="visibleColumns"
multiple
outlined
dense
options-dense
:display-value="$q.lang.table.columns"
emit-value
map-options
:options="columns"
option-value="name"
options-cover
style="min-width: 150px"
/>
</div>
</q-toolbar>
<d-table
ref="table"
:columns="columns"
:rows="store.row"
:filter="filterKeyword"
row-key="name"
flat
bordered
:paging="true"
dense
class="custom-header-table"
:visible-columns="visibleColumns"
>
<template v-slot:header="props">
<q-tr :props="props">
<q-th auto-width />
<q-th v-for="col in props.cols" :key="col.name" :props="props">
<span class="text-weight-bold">{{ col.label }}</span>
</q-th>
</q-tr>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td auto-width>
<q-btn
color="edit"
flat
dense
round
class="q-mr-xs"
icon="edit"
clickable
@click.stop="
() => {
dialogStatus = 'edit';
editId = props.row.id;
posLevelName = props.row.posLevelName;
posLevelRank = props.row.posLevelRank;
if (props.row.posLevelAuthority === 'หัวหน้าหน่วยงาน') {
posLevelAuthority = 'HEAD';
} else if (props.row.posLevelAuthority === 'ปลัด') {
posLevelAuthority = 'DEPUTY';
} else {
posLevelAuthority = 'GOVERNOR';
}
dialog = true;
}
"
>
<q-tooltip>แกไขขอม</q-tooltip>
</q-btn>
<q-btn
color="red"
flat
dense
round
icon="mdi-delete"
clickable
@click.stop="
dialogRemove($q, async () => await deleteData(props.row.id))
"
v-close-popup
>
<q-tooltip>ลบขอม</q-tooltip>
</q-btn>
</q-td>
<q-td v-for="(col, index) in props.cols" :key="col.id">
<div v-if="col.name === 'no'">
{{ props.rowIndex + 1 }}
</div>
<div v-if="col.name === 'posTypeName'">
{{ posName }}
</div>
<div v-else>
{{ col.value }}
</div>
</q-td>
</q-tr>
</template>
</d-table>
</q-card-section>
<!-- <ListLevelDetail v-model:posName="posName" /> -->
</q-card>
<q-dialog v-model="dialog" class="dialog" persistent>
<q-card style="width: 350px">
<q-form greedy @submit.prevent @validation-success="onSubmit">
<dialog-header
:tittle="dialogStatus == 'edit' ? 'แก้ไขข้อมูล' : 'เพิ่มข้อมูล'"
:close="closeDialog"
/>
<q-separator />
<q-card-section class="row q-gutter-y-md">
<div class="col-12">
<q-input
outlined
ref="posLevelNameRef"
v-model="posLevelName"
label="ชื่อระดับตำแหน่ง"
dense
lazy-rules
borderless
bg-color="white"
hide-bottom-space
:rules="[(val:string) => val.length > 0 || 'กรุณากรอกระดับตำแหน่ง']"
class="inputgreen"
/>
</div>
<div class="col-12">
<q-input
ref="posLevelRankRef"
outlined
v-model="posLevelRank"
label="ระดับ"
dense
lazy-rules
borderless
min="1"
bg-color="white"
:rules="[(val:number) => val > 0 || 'กรุณากรอกระดับ']"
hide-bottom-space
mask="############"
class="inputgreen"
/>
</div>
<div class="col-12">
<q-select
ref="posLevelAuthorityRef"
outlined
v-model="posLevelAuthority"
emit-value
map-options
:options="storeOption.posLevelAuthorityOption"
option-value="id"
label="ผู้มีอำนาจสั่งบรรจุ"
dense
lazy-rules
:rules="[(val:string) => !!val || 'กรุณาเลือกผู้มีอำนาจสั่งบรรจุ']"
borderless
bg-color="white"
hide-bottom-space
class="inputgreen"
/>
</div>
<div class="col-12">
<q-select
ref="posTypeIdRef"
v-model="posName"
label="ประเภทตำแหน่ง"
outlined
dense
bg-color="white"
options-cover
hide-bottom-space
readonly
/>
</div>
</q-card-section>
<q-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

@ -25,4 +25,18 @@ interface FormQuery {
keyword: string;
}
export type { ObjectGroupRef, ObjectLevelRef, ObjectPosRef, FormQuery };
interface DataGroup {
id: string;
posLevelAuthority: string;
posLevelName: number;
posLevelRank: number;
posTypeName: string;
}
export type {
ObjectGroupRef,
ObjectLevelRef,
ObjectPosRef,
FormQuery,
DataGroup,
};

View file

@ -0,0 +1,15 @@
interface ResPosType {
id: string;
posTypeName: string;
posTypeRank: number;
posLevels: PosLevels[];
}
interface PosLevels {
id: string;
posLevelAuthority: string;
posLevelName: string;
posLevelRank: number;
}
export type { ResPosType };

View file

@ -21,6 +21,7 @@ interface ResPossition {
posLevelName: number;
posTypeId: string;
posTypeName: string;
posTypeShortName: string;
}
export type { ResGroup, ResLevel, ResPossition };

View file

@ -16,7 +16,7 @@ const personalSubDistrict = () =>
const positionPage = () =>
import("@/modules/01_metadata/views/02_position.vue"); //ข้อมูลตำแหน่งข้าราชการ ฯ
const positionLevelPage = () =>
import("@/modules/01_metadata/components/position/03ListLevel.vue"); //รายการระดับของประเภทตำแหน่งทั่วไป
import("@/modules/01_metadata/components/position/TypeDetail.vue"); //รายการระดับของประเภทตำแหน่งทั่วไป
/**
*
@ -24,7 +24,7 @@ const positionLevelPage = () =>
const positionEmployeePage = () =>
import("@/modules/01_metadata/views/03_positionEmployee.vue"); //ข้อมูลตำแหน่งลูกจ้างประจำ
const positionEmployeeLevelPage = () =>
import("@/modules/01_metadata/components/position-employee/03ListLevel.vue"); //รายการระดับชั้นงานบริการพื้นฐาน
import("@/modules/01_metadata/components/position-employee/GroupDetail.vue"); //รายการระดับชั้นงานบริการพื้นฐาน
/**
*

View file

@ -1,17 +1,23 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import { useCounterMixin } from "@/stores/mixin";
import type {
DataResponse,
DataRow,
} from "../interface/response/position/ListType";
import { useCounterMixin } from "@/stores/mixin";
const { date2Thai } = useCounterMixin();
export const usePositionTypeDataStore = defineStore("PositionTypeData", () => {
const row = ref<DataRow[]>([]);
function save(data: DataResponse[]) {
/**
* row.value
* @param data API
*/
async function save(data: DataResponse[]) {
const list = data.map((e) => ({
...e,
posTypes: undefined,

View file

@ -5,14 +5,14 @@ import { usePersonalDataStore } from "@/modules/01_metadata/stores/personalStore
import type { MainTabs } from "@/modules/01_metadata/interface/index/Main";
import ListPrefix from "@/modules/01_metadata/components/personal/01ListPrefix.vue"; //
import ListRank from "@/modules/01_metadata/components/personal/02ListRank.vue"; //
import ListGender from "@/modules/01_metadata/components/personal/03ListGender.vue"; //
import ListRelationship from "@/modules/01_metadata/components/personal/04ListRelationship.vue"; //
import ListBloodGroup from "@/modules/01_metadata/components/personal/05ListBloodGroup.vue"; //
import ListReligion from "@/modules/01_metadata/components/personal/06ListReligion.vue"; //
import ListProvince from "@/modules/01_metadata/components/personal/07ListProvince.vue"; //
import ListEducation from "@/modules/01_metadata/components/personal/08ListEducationLevel.vue"; //
import ListPrefix from "@/modules/01_metadata/components/personal/01_Prefix.vue"; //
import ListRank from "@/modules/01_metadata/components/personal/02_Rank.vue"; //
import ListGender from "@/modules/01_metadata/components/personal/03_Gender.vue"; //
import ListRelationship from "@/modules/01_metadata/components/personal/04_Relationship.vue"; //
import ListBloodGroup from "@/modules/01_metadata/components/personal/05_BloodGroup.vue"; //
import ListReligion from "@/modules/01_metadata/components/personal/06_Religion.vue"; //
import ListProvince from "@/modules/01_metadata/components/personal/07_Province.vue"; //
import ListEducation from "@/modules/01_metadata/components/personal/08_EducationLevel.vue"; //
const store = usePersonalDataStore();

View file

@ -1,15 +1,21 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import ListPosition from "@/modules/01_metadata/components/position/01ListPosition.vue";
import ListType from "@/modules/01_metadata/components/position/02ListType.vue";
import ListExecutive from "@/modules/01_metadata/components/position/04ListExecutive.vue";
import { usePositionDataStore } from "../stores/positionListStore";
const tabs = ref<Array<any>>([]);
import type { MainTabs } from "@/modules/01_metadata/interface/index/Main";
import ListPosition from "@/modules/01_metadata/components/position/01_Position.vue"; //
import ListType from "@/modules/01_metadata/components/position/02_Type.vue"; //
import ListExecutive from "@/modules/01_metadata/components/position/03_Executive.vue"; //
const store = usePositionDataStore();
const tabs = ref<Array<MainTabs>>([]); // Tab
/**
* hook ทำงานเม Components กเรยกใชงาน
*/
onMounted(() => {
const tabsPerson = [
{ label: "ตำแหน่ง", value: "list_position" },

View file

@ -1,13 +1,20 @@
div
<script setup lang="ts">
import { ref, onMounted } from "vue";
import ListPosition from "@/modules/01_metadata/components/position-employee/01ListPosition.vue";
import ListType from "@/modules/01_metadata/components/position-employee/02ListType.vue";
import { usePositionEmployeeDataStore } from "../stores/positionEmployeeStore";
// const store.pathLocation = ref<string>("list_position");
const tabs = ref<Array<any>>([]);
import type { MainTabs } from "@/modules/01_metadata/interface/index/Main";
import ListPosition from "@/modules/01_metadata/components/position-employee/01_PositionMain.vue"; //
import ListType from "@/modules/01_metadata/components/position-employee/02_GroupMain.vue"; //
const store = usePositionEmployeeDataStore();
const tabs = ref<Array<MainTabs>>([]); // Tab
/**
* hook ทำงานเม Components กเรยกใชงาน
*/
onMounted(() => {
const tabsPerson = [
{ label: "ตำแหน่ง", value: "list_position" },
@ -51,10 +58,6 @@ onMounted(() => {
<q-tab-panel name="list_type">
<ListType v-if="store.pathLocation == 'list_type'" />
</q-tab-panel>
<q-tab-panel name="list_executive">
<ListExecutive v-if="store.pathLocation == 'list_executive'" />
</q-tab-panel>
</q-tab-panels>
</q-card>
</template>

View file

@ -0,0 +1,294 @@
<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 { useCounterMixin } from "@/stores/mixin";
import { useDataStoreUser } from "@/modules/02_users/stores/main";
import type { QTableProps } from "quasar";
import type {
DataProfile,
Pagination,
} from "@/modules/02_users/interface/index/Main";
import DialogHeader from "@/components/DialogHeader.vue";
const $q = useQuasar();
const store = useDataStoreUser();
const { showLoader, hideLoader, messageError, success, dialogConfirm } =
useCounterMixin();
const modal = defineModel<boolean>("modal", { required: true });
const orgId = defineModel<string>("orgId", { required: true });
const props = defineProps({
fetchData: {
type: Function,
required: true,
},
});
const qurey = reactive({
searchKeyword: "", //
searchField: "fullName", // field
page: 1, //
pageSize: 10, //
});
const selected = ref<DataProfile[]>([]); //
const rows = ref<DataProfile[]>([]); //
const total = ref<number>(0); //
const maxPage = ref<number>(0); //
const columns = ref<QTableProps["columns"]>([
{
name: "fullName",
align: "left",
label: "ชื่อ-นามสกุล",
sortable: true,
field: (row) => `${row.prefix}${row.firstName} ${row.lastName}`,
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: "position",
align: "left",
label: "ตำแหน่งในสายงาน",
sortable: true,
field: "position",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posType",
align: "left",
label: "ประเภทตำแหน่ง",
sortable: true,
field: (row) =>
`${row.posType ? row.posType : "-"} ${
row.posLevel ? `(${row.posLevel})` : ``
} `,
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
/**
* งกนดงขอมลรายช
* @param newPage โหลดหนาแรก าเป true โหลดหนาแรก false ใหโหลดหนาปจจ
*
* เกบมลรายชอไวใน rows.value
*/
async function onSearchListPerson(newPage: boolean = false) {
qurey.page = newPage ? 1 : qurey.page;
selected.value = [];
showLoader();
await http
.get(config.API.permissionOrgProfile, {
params: qurey,
})
.then((res) => {
const data = res.data.result;
maxPage.value = Math.ceil(data.total / qurey.pageSize);
total.value = data.total;
rows.value = data.data;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* งกนยนยนการเพมราช
*
* เมอเพมเสรจจะดงขอมลรายชอคนททธดการโครงสรางตามหนวยงาน
*/
function onSubmitPerson() {
dialogConfirm(
$q,
async () => {
showLoader();
const person = selected.value[0];
const body = {
nodeId: orgId.value,
personId: person.id,
};
await http
.post(config.API.permissionOrg, body)
.then(async () => {
await props.fetchData?.(false);
success($q, "เพิ่มราชชื่อสำเร็จ");
onClose();
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
},
"ยืนยันการเพิ่มรายชื่อ",
"ต้องการยืนยันการเพิ่มรายชื่อนี้หรือไม่ ?"
);
}
/**
* function updatePagination
* @param newPagination อม Pagination ใหม
*/
function updatePagination(newPagination: Pagination) {
qurey.pageSize = newPagination.rowsPerPage;
}
/**
* งกนป popup ราชช
*
* และกำหนดคาของ qurey ไปเปนค defult rows.value และ selected.value ไปเปนคาวาง
*/
function onClose() {
modal.value = false;
qurey.page = 1;
qurey.pageSize = 10;
qurey.searchField = "fullName";
qurey.searchKeyword = "";
rows.value = [];
selected.value = [];
}
/**
* การเปลยนแปลงของ pageSize ใน queryBody
*
* เม pageSize การเปลยนแปลงใหโหลดขอมลหนาแรก
*/
watch(
() => qurey.pageSize,
() => {
onSearchListPerson(true);
}
);
</script>
<template>
<q-dialog v-model="modal" persistent>
<q-card style="min-width: 60%">
<DialogHeader :tittle="'รายชื่อ'" :close="onClose" />
<q-separator />
<q-card-section style="max-height: 50vh" class="scroll">
<div class="col-12">
<q-toolbar style="padding: 0">
<q-select
outlined
dense
v-model="qurey.searchField"
:options="store.searchFieldOption"
label="เลือกที่ต้องการค้นหา"
emit-value
map-options
option-label="name"
option-value="id"
style="width: 250px"
/>
<q-toolbar-title>
<q-input
outlined
dense
v-model="qurey.searchKeyword"
label="คำค้นหา"
clearable
>
<template v-slot:append v-if="!qurey.searchKeyword">
<q-icon name="search" color="grey-5" />
</template>
</q-input>
</q-toolbar-title>
<q-btn
label="ค้นหา"
color="primary"
@click="onSearchListPerson(true)"
/>
</q-toolbar>
</div>
<div class="col-12">
<d-table
:columns="columns"
:rows="rows"
:paging="true"
row-key="id"
flat
bordered
dense
:rows-per-page-options="[10, 25, 50, 100]"
selection="single"
v-model:selected="selected"
@update:pagination="updatePagination"
>
<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">
<q-tr :props="props">
<td auto-width>
<q-checkbox
keep-color
color="primary"
dense
v-model="props.selected"
/>
</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>
<template v-slot:pagination="scope">
งหมด {{ total }} รายการ
<q-pagination
v-model="qurey.page"
active-color="primary"
color="dark"
:max="maxPage"
:max-pages="5"
size="sm"
boundary-links
direction-links
@update:model-value="onSearchListPerson(false)"
></q-pagination>
</template>
</d-table>
</div>
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn
label="เพิ่ม"
color="public"
:disable="selected.length === 0"
@click="onSubmitPerson"
/>
</q-card-actions>
</q-card>
</q-dialog>
</template>
<style scoped></style>

View file

@ -52,4 +52,51 @@ interface DataSystem {
parentNode: string;
}
export type { Pagination, ItemsMenu, DataOption, SystemList, DataSystem };
interface DataTree {
ancestorDNA: string;
createdAt: string;
createdFullName: string;
createdUserId: "94ba986d-f871-46a2-be92-46c0cbf0bc56";
id: string;
lastUpdateFullName: string;
lastUpdateUserId: string;
lastUpdatedAt: string;
orgRevisionId: string;
orgRootCode: string;
orgRootFax: string;
orgRootName: string;
orgRootOrder: number;
orgRootPhoneEx: string;
orgRootPhoneIn: string;
orgRootRank: string;
orgRootRankSub: string;
orgRootShortName: string;
responsibility: string;
}
interface DataProfile {
avatar: string;
avatarName: string;
firstName: string;
id: string;
lastName: string;
org: string;
orgNew?: string;
orgRootId?: string;
posLevel: string;
posNo: string;
posType: string;
position: string;
prefix: string;
rank: string;
}
export type {
Pagination,
ItemsMenu,
DataOption,
SystemList,
DataSystem,
DataTree,
DataProfile,
};

View file

@ -27,4 +27,12 @@ interface FilterReqMaster {
keyword: string;
revisionId: string;
}
export type { FormUser, FormRole, Roles, FilterReqMaster };
interface QueryProfile {
searchKeyword: string;
searchField: string;
page: number;
pageSize: number;
id: string | null;
}
export type { FormUser, FormRole, Roles, FilterReqMaster, QueryProfile };

View file

@ -12,6 +12,10 @@ const ListsPage2Role = () =>
const PermissionPage = () =>
import("@/modules/02_users/views/permissionsView.vue");
/** roleOrganization */
const roleOrgview = () =>
import("@/modules/02_users/views/roleOrganization.vue");
export default [
{
path: "/users",
@ -55,4 +59,13 @@ export default [
Role: ["SUPER_ADMIN", "ADMIN"],
},
},
{
path: "/roles-organization",
name: "roleOrganization",
component: roleOrgview,
meta: {
Role: ["SUPER_ADMIN", "ADMIN"],
},
},
];

View file

@ -1,5 +1,33 @@
import { defineStore } from "pinia";
import { ref } from "vue";
export const useDataStore = defineStore("storeData", () => {
return {};
import type { DataOption } from "@/modules/02_users/interface/index/Main";
export const useDataStoreUser = defineStore("storeDataUser", () => {
const searchFieldOption = ref<DataOption[]>([
{
name: "ชื่อ-นามสกุล",
id: "fullName",
},
{
name: "เลขที่ตำแห่นง",
id: "posNo",
},
{
name: "ตำแหน่งในสายงาน",
id: "position",
},
{
name: "ประเภทตำแหน่ง",
id: "postype",
},
{
name: "ระดับตำแหน่ง",
id: "poslevel",
},
]);
return {
searchFieldOption,
};
});

View file

@ -0,0 +1,451 @@
<script setup lang="ts">
import { ref, onMounted, reactive, watch } from "vue";
import { useQuasar } from "quasar";
import http from "@/plugins/http";
import config from "@/app.config";
import { useCounterMixin } from "@/stores/mixin";
import { useDataStoreUser } from "@/modules/02_users/stores/main";
import type { QTableProps } from "quasar";
import type {
DataTree,
DataProfile,
Pagination,
} from "@/modules/02_users/interface/index/Main";
import type { QueryProfile } from "@/modules/02_users/interface/request/Main";
import DialogAddPerson from "@/modules/02_users/components/RoleOrganization/DialogAddPerson.vue";
const $q = useQuasar();
const store = useDataStoreUser();
const { showLoader, hideLoader, messageError, success, dialogRemove } =
useCounterMixin();
/******* โครงสร้าง *******/
const filter = ref<string>(""); //
const nodeTree = ref<DataTree[]>([]); //
const expanded = ref<Array<string>>([]); //
const orgId = ref<string>(""); // id
/**
* งกนดงขอมลโครงสราง
*
* เกบขอมลโครงสรางไวใน nodeTree
*/
async function fatchOrg() {
showLoader();
await http
.get(config.API.permissionOrg)
.then(async (res) => {
const data = await res.data.result;
nodeTree.value = data;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* งกนเลอกหนวยงาน
* @param id หนวยงานทเลอก
*
* กำหนดคาของ qureyBody ใหเปนค defult และกำหนดคาของ qureyBody.id เปนหนวยงานทเลอก
* และดงขอมลรายชอคนททธดการโครงสรางในหนวยงานทเลอก
*/
function selectedOrg(id: string) {
orgId.value = id;
qureyBody.id = id;
qureyBody.searchKeyword = "";
qureyBody.searchField = "fullName";
qureyBody.page = 1;
fetchListPerson(true);
}
/******* รายชื่อคนที่มีสิทธิ์จัดการโครงสร้าง *******/
const qureyBody = reactive<QueryProfile>({
searchKeyword: "", //
searchField: "fullName", // field
page: 1, //
pageSize: 10, //
id: null, //
});
const rows = ref<DataProfile[]>([]); //
const total = ref<number>(0); //
const maxPage = ref<number>(0); //
const columns = ref<QTableProps["columns"]>([
{
name: "fullName",
align: "left",
label: "ชื่อ-นามสกุล",
sortable: true,
field: (row) => `${row.prefix}${row.firstName} ${row.lastName}`,
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "org",
align: "left",
label: "หน่วยงาน",
sortable: true,
field: "org",
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: "position",
align: "left",
label: "ตำแหน่งในสายงาน",
sortable: true,
field: "position",
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
{
name: "posType",
align: "left",
label: "ประเภทตำแหน่ง",
sortable: true,
field: (row) =>
`${row.posType ? row.posType : "-"} ${
row.posLevel ? `(${row.posLevel})` : ``
} `,
headerStyle: "font-size: 14px",
style: "font-size: 14px",
},
]);
const modalAdd = ref<boolean>(false); // modal
/**
* งกนดงขอมลรายชอคนททธดการโครงสราง
* @param newPage โหลดหนาแรก าเป true โหลดหนาแรก false ใหโหลดหนาปจจ
*
* เกบมลรายชอคนททธดการโครงสรางไวใน rows.value
*/
async function fetchListPerson(newPage: boolean = false) {
qureyBody.page = newPage ? 1 : qureyBody.page;
showLoader();
await http
.post(config.API.permissionOrgProfile, qureyBody)
.then(async (res) => {
const data = await res.data.result;
maxPage.value = Math.ceil(data.total / qureyBody.pageSize); //
total.value = data.total; //
rows.value = data.data;
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
}
/**
* งกนยนยนการลบรายชอคนททธดการโครงสราง
* @param id รายชอคนททธดการโครงสราง
*
* ลบเสรจจะโหลดขอมลรายชอคนททธดการโครงสราง
*/
function onDeletePerson(id: string) {
dialogRemove($q, async () => {
showLoader();
await http
.delete(config.API.permissionOrg + `/${id}`)
.then(async () => {
// maxPage.value 1
if (maxPage.value !== 1) {
// page 1
if (rows.value.length === 1) {
qureyBody.page = qureyBody.page - 1;
}
await fetchListPerson(false); //
} else {
await fetchListPerson(true); //
}
success($q, "ลบข้อมูสำเร็จ");
})
.catch((err) => {
messageError($q, err);
})
.finally(() => {
hideLoader();
});
});
}
/**
* function updatePagination
* @param newPagination อม Pagination ใหม
*/
function updatePagination(newPagination: Pagination) {
qureyBody.pageSize = newPagination.rowsPerPage;
}
/**
* การเปลยนแปลงของ pageSize ใน queryBody
*
* เม pageSize การเปลยนแปลงใหโหลดขอมลหนาแรก
*/
watch(
() => qureyBody.pageSize,
() => {
fetchListPerson(true);
}
);
/**
* hook ทำงานเม Components กเรยกใชงาน
*
*/
onMounted(async () => {
await Promise.all([
fatchOrg(), //
fetchListPerson(), //
]);
});
</script>
<template>
<div class="toptitle text-dark col-12 row items-center">
กำหนดสทธดการโครงสราง
</div>
<q-card style="height: 100%">
<q-card-section :horizontal="$q.screen.gt.xs">
<q-card-section class="col-lg-3 col-md-4 col-xs-12 q-gutter-sm">
<div>
<q-input dense outlined v-model="filter" label="ค้นหา">
<template v-slot:append>
<q-icon
v-if="filter !== ''"
name="clear"
class="cursor-pointer"
@click="filter = ''"
/>
<q-icon name="search" color="grey-5" />
</template>
</q-input>
</div>
<div class="bg-white tree-container q-pa-xs">
<q-tree
class="q-pa-sm q-gutter-sm"
dense
:nodes="nodeTree"
node-key="orgTreeId"
label-key="labelName"
:filter="filter"
no-results-label="ไม่พบข้อมูลที่ค้นหา"
no-nodes-label="ไม่มีข้อมูล"
v-model:expanded="expanded"
>
<template v-slot:default-header="prop">
<q-item
clickable
class="row col-12 items-center text-dark q-py-xs q-pl-sm rounded-borders my-list"
:active="orgId == prop.node.id"
active-class="my-list-link text-primary text-weight-medium"
@click.stop="selectedOrg(prop.node.id)"
>
<div>
<div class="text-weight-medium">
{{ prop.node.orgRootName }}
</div>
<div class="text-weight-light text-grey-8">
{{
prop.node.orgRootCode == null
? null
: prop.node.orgRootCode
}}
{{
prop.node.orgRootShortName == null
? null
: prop.node.orgRootShortName
}}
</div>
</div>
</q-item>
</template>
</q-tree>
</div>
</q-card-section>
<q-separator :vertical="$q.screen.gt.xs" />
<q-card-section
class="col-lg-9 col-md-8 col-xs-12 scroll"
style="height: 83vh"
>
<q-card bordered style="height: 100%; 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>
<q-card-section>
<div class="row col-12 q-col-md">
<div class="col-2">
<q-btn
v-if="orgId"
flat
dense
color="primary"
icon="add"
round
@click="modalAdd = true"
>
<q-tooltip>เพมขอม</q-tooltip>
</q-btn>
</div>
<div class="col-md-10">
<div class="row col-12 q-col-gutter-sm">
<div class="col-md-4">
<q-select
outlined
dense
v-model="qureyBody.searchField"
:options="store.searchFieldOption"
label="เลือกที่ต้องการค้นหา"
emit-value
map-options
option-label="name"
option-value="id"
/>
</div>
<div class="col-md-6">
<q-input
v-model="qureyBody.searchKeyword"
outlined
dense
lazy-rules
label="คำค้น"
hide-bottom-space
>
<template v-slot:append>
<q-icon
v-if="qureyBody.searchKeyword"
name="cancel"
@click="qureyBody.searchKeyword = ''"
class="cursor-pointer"
></q-icon>
</template>
</q-input>
</div>
<div class="row col-md-2">
<q-btn
color="primary"
icon="search"
label="ค้นหา"
class="full-width"
@click="fetchListPerson(true)"
/>
</div>
</div>
</div>
</div>
<div class="col-12 q-mt-sm">
<d-table
:columns="columns"
:rows="rows"
:paging="true"
row-key="id"
flat
bordered
dense
:rows-per-page-options="[10, 25, 50, 100]"
@update:pagination="updatePagination"
>
<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">
<q-tr :props="props">
<q-td auto-width>
<q-btn
flat
dense
color="red"
icon="delete"
round
@click.pervent="onDeletePerson(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>
<template v-slot:pagination="scope">
งหมด {{ total }} รายการ
<q-pagination
v-model="qureyBody.page"
active-color="primary"
color="dark"
:max="maxPage"
:max-pages="5"
size="sm"
boundary-links
direction-links
@update:model-value="fetchListPerson(false)"
>
</q-pagination>
</template>
</d-table>
</div>
</q-card-section>
</q-card>
</q-card-section>
</q-card-section>
</q-card>
<DialogAddPerson
v-model:modal="modalAdd"
v-model:org-id="orgId"
:fetch-data="fetchListPerson"
/>
</template>
<style scoped>
.tree-container {
overflow: auto;
height: 74vh;
border: 1px solid #e6e6e7;
border-radius: 10px;
}
.my-list-link {
color: rgb(118, 168, 222);
border-radius: 5px;
background: #a3d3fb48 !important;
font-weight: 600;
border: 1px solid rgba(175, 185, 196, 0.217);
}
</style>

View file

@ -62,6 +62,11 @@ const menuList = ref<{ icon: string; label: string; path: string }[]>([
label: "ระบบการบรรจุ แต่งตั้ง ย้าย โอน",
path: "/lists?system=placement",
},
{
icon: "mdi-account-convert",
label: "ระบบทดลองปฏิบัติหน้าที่ราชการ",
path: "/lists?system=probation",
},
{
icon: "mdi-account-star",
label: "ระบบการประเมินผลการปฏิบัติราชการระดับบุคคล",

View file

@ -55,21 +55,22 @@ async function getData(index: number) {
totalInbox.value = await res.data.result.total;
let list: DataInbox[] = [];
data.map((e: ResponseInbox) => {
list.push({
no: e.id ?? "",
sender:
e.createdFullName == "" || e.createdFullName == null
? "เจ้าหน้าที่"
: e.createdFullName,
subject: e.subject ?? "",
timereceive: new Date(e.receiveDate),
body: e.body ?? "",
payload: e.payload,
ratingModel: 0,
isOpen: e.isOpen,
data &&
data.map((e: ResponseInbox) => {
list.push({
no: e.id ?? "",
sender:
e.createdFullName == "" || e.createdFullName == null
? "เจ้าหน้าที่"
: e.createdFullName,
subject: e.subject ?? "",
timereceive: new Date(e.receiveDate),
body: e.body ?? "",
payload: e.payload,
ratingModel: 0,
isOpen: e.isOpen,
});
});
});
inboxList.value.push(...list);
if (inboxList.value.length > 0 && index === 1) {

View file

@ -98,27 +98,28 @@ async function getDataNotification(index: number, type: string) {
};
await http
.get(config.API.msgNotificate + `?page=${index}&pageSize=${10}`)
.then((res: any) => {
.then((res) => {
const response = res.data.result.data;
totalInbox.value = res.data.result.total;
let list: notiType[] = [];
if (type === "DEL") {
notiList.value = [];
}
response.map((e: any) => {
list.push({
id: e.id,
sender:
e.createdFullName == "" || e.createdFullName == null
? "เจ้าหน้าที่"[0]
: e.createdFullName[0],
body: e.body ?? "",
timereceive: `${date2Thai(e.receiveDate)} ${new Date(
e.receiveDate
).toLocaleTimeString("th-TH", thaiOptions)} .`,
isOpen: e.isOpen,
response &&
response.map((e: any) => {
list.push({
id: e.id,
sender:
e.createdFullName == "" || e.createdFullName == null
? "เจ้าหน้าที่"[0]
: e.createdFullName[0],
body: e.body ?? "",
timereceive: `${date2Thai(e.receiveDate)} ${new Date(
e.receiveDate
).toLocaleTimeString("th-TH", thaiOptions)} .`,
isOpen: e.isOpen,
});
});
});
notiList.value.push(...list);
statusLoad.value = totalInbox.value === 0 ? true : false;
})