Merge branch 'develop' into dev
All checks were successful
Build & Deploy on Dev / build (push) Successful in 1m4s

* develop:
  feat: add position and salary review
This commit is contained in:
Warunee Tamkoo 2026-01-27 16:22:59 +07:00
commit 2cef4b4a2f
9 changed files with 1589 additions and 79 deletions

View file

@ -1,68 +1,69 @@
{ {
"name": "ehr", "name": "ehr",
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "run-p build-only", "build": "run-p build-only",
"preview": "vite preview", "preview": "vite preview",
"test:unit": "vitest --environment jsdom --root src/", "test:unit": "vitest --environment jsdom --root src/",
"test:e2e": "start-server-and-test preview :4173 'cypress run --e2e'", "test:e2e": "start-server-and-test preview :4173 'cypress run --e2e'",
"test:e2e:dev": "start-server-and-test 'vite dev --port 4173' :4173 'cypress open --e2e'", "test:e2e:dev": "start-server-and-test 'vite dev --port 4173' :4173 'cypress open --e2e'",
"build-only": "vite build", "build-only": "vite build",
"type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false", "type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"format": "prettier ./src --write" "format": "prettier ./src --write"
}, },
"dependencies": { "dependencies": {
"@fullcalendar/core": "^6.0.1", "@fullcalendar/core": "^6.0.1",
"@fullcalendar/daygrid": "^6.0.1", "@fullcalendar/daygrid": "^6.0.1",
"@fullcalendar/interaction": "^6.0.2", "@fullcalendar/interaction": "^6.0.2",
"@fullcalendar/list": "^6.0.2", "@fullcalendar/list": "^6.0.2",
"@fullcalendar/react": "^6.0.1", "@fullcalendar/react": "^6.0.1",
"@fullcalendar/timegrid": "^6.0.2", "@fullcalendar/timegrid": "^6.0.2",
"@fullcalendar/vue3": "^6.0.1", "@fullcalendar/vue3": "^6.0.1",
"@quasar/extras": "^1.15.8", "@quasar/extras": "^1.15.8",
"@tato30/vue-pdf": "^1.5.1", "@tato30/vue-pdf": "^1.5.1",
"@vuepic/vue-datepicker": "^3.6.3", "@vuepic/vue-datepicker": "^3.6.3",
"bma-org-chart": "^0.0.7", "bma-org-chart": "^0.0.7",
"dom-to-image-more": "^3.6.0", "dom-to-image-more": "^3.6.0",
"keycloak-js": "^20.0.2", "keycloak-js": "^20.0.2",
"moment": "^2.29.4", "moment": "^2.29.4",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"pinia": "^2.0.29", "pinia": "^2.0.29",
"quasar": "^2.11.1", "quasar": "^2.11.1",
"socket.io-client": "^4.7.4", "socket.io-client": "^4.7.4",
"structure-chart": "^0.0.9", "structure-chart": "^0.0.9",
"vue": "^3.4.15", "vue": "^3.4.15",
"vue-router": "^4.1.6", "vue-router": "^4.1.6",
"vue3-datepicker": "^0.3.4", "vue3-datepicker": "^0.3.4",
"vue3-pdfjs": "^0.1.6" "vue3-pdfjs": "^0.1.6",
}, "xlsx": "^0.18.5"
"devDependencies": { },
"@quasar/vite-plugin": "^1.3.0", "devDependencies": {
"@rushstack/eslint-patch": "^1.1.4", "@quasar/vite-plugin": "^1.3.0",
"@types/jsdom": "^20.0.1", "@rushstack/eslint-patch": "^1.1.4",
"@types/node": "^18.11.12", "@types/jsdom": "^20.0.1",
"@vitejs/plugin-vue": "^4.0.0", "@types/node": "^18.11.12",
"@vitejs/plugin-vue-jsx": "^3.0.0", "@vitejs/plugin-vue": "^4.0.0",
"@vue/eslint-config-prettier": "^7.0.0", "@vitejs/plugin-vue-jsx": "^3.0.0",
"@vue/eslint-config-typescript": "^11.0.0", "@vue/eslint-config-prettier": "^7.0.0",
"@vue/test-utils": "^2.2.6", "@vue/eslint-config-typescript": "^11.0.0",
"@vue/tsconfig": "^0.1.3", "@vue/test-utils": "^2.2.6",
"cypress": "^12.0.2", "@vue/tsconfig": "^0.1.3",
"eslint": "^8.22.0", "cypress": "^12.0.2",
"eslint-plugin-cypress": "^2.12.1", "eslint": "^8.22.0",
"eslint-plugin-vue": "^9.3.0", "eslint-plugin-cypress": "^2.12.1",
"jsdom": "^20.0.3", "eslint-plugin-vue": "^9.3.0",
"npm-run-all": "^4.1.5", "jsdom": "^20.0.3",
"prettier": "^2.7.1", "npm-run-all": "^4.1.5",
"sass": "^1.32.12", "prettier": "^2.7.1",
"start-server-and-test": "^1.15.2", "sass": "^1.32.12",
"typescript": "~4.7.4", "start-server-and-test": "^1.15.2",
"vite": "^4.0.0", "typescript": "~4.7.4",
"vitest": "^0.25.6", "vite": "^4.0.0",
"vue-table-to-excel": "^1.0.6", "vitest": "^0.25.6",
"vue-tsc": "^1.0.12" "vue-table-to-excel": "^1.0.6",
} "vue-tsc": "^1.0.12"
}
} }

View file

@ -3,7 +3,6 @@ import env from "../index";
const metadata = `${env.API_URI}/org/metadata/`; const metadata = `${env.API_URI}/org/metadata/`;
const org = `${env.API_URI}/org`; const org = `${env.API_URI}/org`;
const profileOrg = `${env.API_URI}/org/profile`; const profileOrg = `${env.API_URI}/org/profile`;
const report = `${env.API_URI}/org`;
const workflow = `${env.API_URI}/org/workflow`; const workflow = `${env.API_URI}/org/workflow`;
export default { export default {
orgCommandCode: `${metadata}commandCode`, orgCommandCode: `${metadata}commandCode`,
@ -70,9 +69,9 @@ export default {
dataUserOtherByType: (type: string) => `${org}/profile${type}/other/user`, dataUserOtherByType: (type: string) => `${org}/profile${type}/other/user`,
profileReportId: (profileId: string, type: string) => profileReportId: (profileId: string, type: string) =>
`${report}/${type}/kk1/${profileId}`, `${org}/${type}/kk1/${profileId}`,
profileKp7ShortId: (profileId: string, type: string) => profileKp7ShortId: (profileId: string, type: string) =>
`${report}/${type}/kp7-short/${profileId}`, `${org}/${type}/kp7-short/${profileId}`,
/** history */ /** history */
dataUserInformatioHistory: (type: string) => dataUserInformatioHistory: (type: string) =>
@ -126,7 +125,7 @@ export default {
dataUserCertificateHistoryByType: ( dataUserCertificateHistoryByType: (
emType: string, emType: string,
type: string, type: string,
id: string id: string,
) => `${org}/profile${emType}/${type}/history/${id}`, ) => `${org}/profile${emType}/${type}/history/${id}`,
dataUserHonorHistory: (type: string, emType: string, id: string) => dataUserHonorHistory: (type: string, emType: string, id: string) =>
`${profileOrg}${emType}/${type}/history/${id}`, `${profileOrg}${emType}/${type}/history/${id}`,
@ -176,4 +175,7 @@ export default {
changePassword: `${org}/keycloak/user/change-password`, changePassword: `${org}/keycloak/user/change-password`,
orgAssistance: (id: string) => `${profileOrg}/assistance/${id}`, orgAssistance: (id: string) => `${profileOrg}/assistance/${id}`,
// ตรวจสอบข้อมูลตำแหน่งและเงินเดือน
profileSalaryTemp: `${profileOrg}/salaryTemp`,
}; };

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,76 @@
interface DataSalaryPos {
avatar: string;
avatarName: string;
citizenId: string;
firstName: string;
id: string;
lastName: string;
org: string;
orgRevisionId: string;
orgRootShortName: string;
posExecutive: string;
posLevel: string;
posLevelId: string;
posNo: string;
posType: string;
posTypeId: string;
position: string;
prefix: string;
rank: string;
root: string;
rootId: string;
statusCheckEdit: string;
type: string;
}
interface DataPosition {
no?: number;
amount: number;
amountSpecial: number;
commandCode: string;
commandDateAffect: Date;
commandDateSign: Date;
commandId: string;
commandName: string;
commandNo: string;
commandYear: number;
createdAt: string;
createdFullName: string;
createdUserId: string;
dateGovernment: string;
id: string;
isDelete: boolean;
isEdit: boolean;
isEntry: boolean;
isGovernment: string;
lastUpdateFullName: string;
lastUpdateUserId: string;
lastUpdatedAt: string;
mouthSalaryAmount: number;
order: number;
orgChild1: string;
orgChild2: string;
orgChild3: string;
orgChild4: string;
orgRoot: string;
posNo: string;
posNoAbb: string;
positionCee: string;
positionExecutive: string;
positionLevel: string;
positionLine: string;
positionName: string;
positionPathSide: string;
positionSalaryAmount: number;
positionType: string;
profileEmployeeId: string;
profileId: string;
refId: number;
remark: string;
salaryId: string;
status: string;
posNumCodeSitAbb: string;
posNumCodeSit: string;
}
export type { DataSalaryPos, DataPosition };

View file

@ -0,0 +1,155 @@
interface Pagination {
rowsPerPage: number;
}
interface DataOption {
id: string;
name: string;
}
interface DataOption2 {
id: number;
name: string;
}
interface DataOptionSys {
id: string;
name: string;
system: string;
}
interface DataOptionInsignia {
id: string;
name: string;
typeId: string;
typeName: string;
}
interface DataOptionEducation {
label: string;
value: boolean;
}
interface DataOptionEducationLevel {
label: string;
value: string;
}
interface zipCodeOption {
id: string;
name: string;
zipCode: string;
}
interface InformationOps {
prefixOps: DataOption[];
rankOps: DataOption[];
genderOps: DataOption[];
bloodOps: DataOption[];
statusOps: DataOption[];
religionOps: DataOption[];
employeeClassOps: DataOption[];
employeeTypeOps: DataOption[];
}
interface AddressOps {
provinceOps: DataOption[];
districtOps: DataOption[];
districtCOps: DataOption[];
subdistrictOps: zipCodeOption[];
subdistrictCOps: zipCodeOption[];
}
interface InsigniaOps {
insigniaOptions: DataOptionInsignia[];
}
interface ItemTab {
name: string;
icon: string;
label: string;
}
interface Request {
createdAt: string;
createdFullName: string;
detail: string;
fullname: string;
id: string;
idcard: string;
lastUpdateFullName: string;
lastUpdatedAt: string;
profileId: string;
remark: string;
status: string;
topic: string;
}
interface InsigniasType {
id: string;
createdAt: Date;
createdUserId: string;
lastUpdatedAt: Date;
lastUpdateUserId: string;
createdFullName: string;
lastUpdateFullName: string;
name: string;
isActive: boolean;
insignias: InsigniasTypeSub[];
}
interface InsigniasTypeSub {
id: string;
createdAt: Date;
createdUserId: string;
lastUpdatedAt: Date;
lastUpdateUserId: string;
createdFullName: string;
lastUpdateFullName: string;
name: string;
shortName: string;
level: string;
isActive: string;
note: string;
insigniaTypeId: string;
}
interface ResFileData {
downloadUrl: string;
fileName: string;
path: string;
pathname: string;
}
interface DataHistory {
[key: string]: string | number | Date | boolean;
}
interface DataCommandCode {
id: string;
createdAt: Date;
lastUpdatedAt: Date;
createdFullName: string;
lastUpdateFullName: string;
name: string;
code: number;
}
export type {
Pagination,
DataOption,
DataOption2,
DataOptionInsignia,
zipCodeOption,
InformationOps,
AddressOps,
InsigniaOps,
DataOptionSys,
ItemTab,
DataOptionEducation,
DataOptionEducationLevel,
Request,
InsigniasType,
InsigniasTypeSub,
ResFileData,
DataHistory,
DataCommandCode,
};

View file

@ -17,6 +17,8 @@ const requestEditMain = () =>
import("@/modules/10_registry/views/requestEditMain.vue"); import("@/modules/10_registry/views/requestEditMain.vue");
const requestEditDetail = () => const requestEditDetail = () =>
import("@/modules/10_registry/views/requestEditDetail.vue"); import("@/modules/10_registry/views/requestEditDetail.vue");
const positionReviewPage = () =>
import("@/modules/10_registry/views/positionReview.vue");
export default [ export default [
{ {
@ -93,4 +95,15 @@ export default [
Key: [10], Key: [10],
}, },
}, },
// ตรวจสอบข้อมูลตำแหน่งและเงินเดือน
{
path: "/registry/position-review",
name: "position-review",
component: positionReviewPage,
meta: {
Auth: true,
Key: [10],
},
},
]; ];

View file

@ -12,7 +12,7 @@ const {} = mixin;
export const useGovernmentPosDataStore = defineStore("GovernmentPos", () => { export const useGovernmentPosDataStore = defineStore("GovernmentPos", () => {
// commandCode ขอตำแหน่ง // commandCode ขอตำแหน่ง
const positionCode = ref<number[]>([ const positionCode = ref<number[]>([
0, 1, 2, 3, 4, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0, 1, 2, 3, 4, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20,
]); ]);
const commandCodeData = ref<DataOption[]>([]); const commandCodeData = ref<DataOption[]>([]);

View file

@ -52,11 +52,11 @@ async function onClickDownloadKp7(type: string) {
type === "FULL" type === "FULL"
? config.API.profileReportId( ? config.API.profileReportId(
store.profileId, store.profileId,
dataStore.officerType == "OFFICER" ? "profile" : "profile-employee" dataStore.officerType == "OFFICER" ? "profile" : "profile-employee",
) )
: config.API.profileKp7ShortId( : config.API.profileKp7ShortId(
store.profileId, store.profileId,
dataStore.officerType == "OFFICER" ? "profile" : "profile-employee" dataStore.officerType == "OFFICER" ? "profile" : "profile-employee",
); );
const fileName = type === "FULL" ? "ทปอ. สามัญ" : "ประวัติแบบย่อ"; const fileName = type === "FULL" ? "ทปอ. สามัญ" : "ประวัติแบบย่อ";
await http await http
@ -78,11 +78,6 @@ const clickBack = () => {
router.push(`/`); router.push(`/`);
}; };
/** ฟังก์ชัน redirect ไปหน้ารายการคำร้องขอแก้ไขข้อมูล*/
function redirectToPagePetition() {
router.push(`/registry/request-edit`);
}
function onMobile(type: string) { function onMobile(type: string) {
router.push(`/registry/${type}`); router.push(`/registry/${type}`);
} }
@ -103,10 +98,20 @@ function onMobile(type: string) {
@click="clickBack" @click="clickBack"
/> />
อมลทะเบยนประว <q-space /> อมลทะเบยนประว <q-space />
<q-btn
color="blue-6"
icon="mdi-eye"
label="ตรวจสอบข้อมูลตำแหน่งและเงินเดือน"
class="q-mr-sm"
@click="() => router.push('/registry/position-review')"
>
<q-tooltip>ไปยงหนาตรวจสอบขอมลตำแหนงและเงนเดอน</q-tooltip>
</q-btn>
<q-btn <q-btn
color="primary" color="primary"
label="ยื่นคำร้องขอแก้ไขข้อมูล" label="ยื่นคำร้องขอแก้ไขข้อมูล"
@click="redirectToPagePetition" @click="() => router.push('/registry/request-edit')"
> >
<q-tooltip>นคำรองขอแกไขขอม</q-tooltip> <q-tooltip>นคำรองขอแกไขขอม</q-tooltip>
</q-btn> </q-btn>

View file

@ -0,0 +1,197 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
// import { useQuasar } from "quasar";
// import http from "@/plugins/http";
// import config from "@/app.config";
// import { useCounterMixin } from "@/stores/mixin";
import { useRoute, useRouter } from "vue-router";
// import { useDataStore } from "@/stores/data";
/** importType*/
// import type { DataProfile } from "@/modules/04_registryPerson/interface/index/Edit";
import Table from "@/modules/10_registry/components/PositionReview/Table.vue";
// import DialogEdit from "@/modules/04_registryPerson/views/edit/components/DialogEdit.vue";
// const dataStore = useDataStore();
// const $q = useQuasar();
// const { showLoader, hideLoader, messageError, dialogConfirm, success } =
// useCounterMixin();
// const route = useRoute();
const router = useRouter();
const tabs = ref<string>("CHECKED");
const statusCheckEdit = ref<string>("");
// const empType = ref<string>(dataStore.officerType);
// const profileId = ref<string>(dataStore.profileId);
const isConfirmEdit = ref<boolean>(false); //
// const isConfirmDone = ref<boolean>(false); //
// const modalEdit = ref<boolean>(false);
// const modalHistory = ref<boolean>(false);
/** function ยืนยันเสร็จสิ้นการแก้ไข*/
// function onConfirmEdit() {
// dialogConfirm(
// $q,
// () => {
// showLoader();
// http
// .post(config.API.salaryTemp + `/confirm-edit`, {
// profileId: profileId.value,
// type: empType.value?.toLocaleUpperCase(),
// })
// .then(async () => {
// success($q, "");
// })
// .catch((err) => {
// messageError($q, err);
// })
// .finally(() => {
// hideLoader();
// });
// },
// "",
// "?",
// );
// }
/** function ยืนยันข้อมูลถูกต้อง*/
// function onConfirmDone() {
// dialogConfirm(
// $q,
// () => {
// showLoader();
// http
// .post(config.API.salaryTemp + `/confirm-done`, {
// profileId: profileId.value,
// type: empType.value?.toLocaleUpperCase(),
// })
// .then(async () => {
// success($q, "");
// })
// .catch((err) => {
// messageError($q, err);
// })
// .finally(() => {
// hideLoader();
// });
// },
// "",
// "",
// );
// }
</script>
<template>
<div class="row items-center q-gutter-sm q-mb-xs">
<div class="toptitle text-white row items-center q-py-xs">
<q-btn
icon="mdi-arrow-left"
unelevated
round
dense
flat
color="primary"
class="q-mr-sm"
@click="router.go(-1)"
/>
รายการประวตำแหน/เงนเดอน
</div>
</div>
<q-card flat bordered>
<!-- <q-separator v-if="isConfirmEdit || isConfirmDone" />
<q-card-section v-if="isConfirmEdit || isConfirmDone">
<div class="row q-col-gutter-sm justify-center">
<div class="row items-center" v-if="isConfirmEdit">
<q-btn
:disable="
statusCheckEdit === 'EDITED' || statusCheckEdit === 'CHECKED'
"
:color="
statusCheckEdit === 'EDITED' || statusCheckEdit === 'CHECKED'
? 'grey'
: 'public'
"
outline
label="ยืนยันการแก้ไขสำเร็จ"
@click.stop.prevent="onConfirmEdit"
/>
<q-btn
flat
label="ประวัติการดำเนินการ"
class="q-ml-md"
color="blue"
@click="modalHistory = true"
/>
</div>
<div
class="row items-center"
v-if="isConfirmDone && statusCheckEdit === 'EDITED'"
>
<q-btn
color="public"
outline
label="ยืนยันข้อมูลถูกต้อง"
@click.stop.prevent="onConfirmDone"
/>
<q-btn
flat
label="ตีกลับให้แก้ไข"
class="q-ml-md"
color="blue"
@click="modalEdit = true"
/>
</div>
</div>
</q-card-section> -->
<q-separator />
<q-card-section>
<!-- <q-tabs
v-model="tabs"
dense
align="left"
inline-label
class="bg-white text-grey"
active-color="primary"
indicator-color="primary"
>
<q-tab name="PENDING" label="รายการตำแหน่ง/เงินเดือนที่กำลังแก้ไข" />
<q-tab
name="CHECKED"
label="แสดงรายการตำแหน่ง/เงินเดือนหลังจากแก้ไขแล้ว"
/>
</q-tabs>
<q-separator />
<q-tab-panels v-model="tabs" animated>
<q-tab-panel name="PENDING">
<Table
:tabs="tabs"
:status-check-edit="statusCheckEdit"
:is-confirm-edit="isConfirmEdit"
/>
</q-tab-panel>
<q-tab-panel name="CHECKED"> -->
<Table
:tabs="tabs"
:status-check-edit="statusCheckEdit"
:is-confirm-edit="isConfirmEdit"
/>
<!-- </q-tab-panel>
</q-tab-panels> -->
</q-card-section>
</q-card>
<!-- <DialogEdit
v-model:modal="modalEdit"
:emp-type="empType"
:fetch-data="fetchDataProfile"
/> -->
</template>
<style scoped></style>