refactor(registry-edit): exportExcel

This commit is contained in:
DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-04-23 17:22:01 +07:00
parent 88667b8ea8
commit 24d08ac0cf
4 changed files with 217 additions and 56 deletions

View file

@ -70,6 +70,7 @@ interface DataPosition {
status: string;
posNumCodeSitAbb: string;
posNumCodeSit: string;
positionExecutiveField: string;
}
export type { DataSalaryPos, DataPosition };

View file

@ -0,0 +1,157 @@
import ExcelJS from "exceljs";
import { useEditPosDataStore } from "@/modules/04_registryPerson/stores/Edit";
import type { DataPosition } from "@/modules/04_registryPerson/interface/response/Edit";
const store = useEditPosDataStore();
// ฟังก์ชันแปลงวันที่จาก ISO format เป็นรูปแบบ dd/mm/yyyy (เช่น 18/05/2564)
function formatDateToDDMMYYYY(dateString: string | null | Date): string {
if (!dateString) return "";
const date = new Date(dateString);
if (isNaN(date.getTime())) return "";
const day = String(date.getDate()).padStart(2, "0");
const month = String(date.getMonth() + 1).padStart(2, "0");
const year = date.getFullYear() + 543; // แปลงเป็นปีพุทธศักราช
return `${day}/${month}/${year}`;
}
export async function exportToExcelPosition(data: DataPosition[]) {
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet("รายการประวัติตำแหน่งเงินเดือน");
// --- ส่วนที่ 1: สร้าง Master Data Sheet สำหรับอ้างอิง ID ---
// เราจะซ่อนแผ่นงานนี้ไว้ (hidden) เพื่อใช้ทำ Dropdown และ VLOOKUP
const masterSheet = workbook.addWorksheet("MasterData", { state: "hidden" });
const masterData = store.commandCodeData; // [{id: 1, name: "ย้าย"}, ...]
masterData.forEach((item, index) => {
masterSheet.getCell(`A${index + 1}`).value = item.name;
masterSheet.getCell(`B${index + 1}`).value = item.id;
});
// --- ส่วนที่ 2: กำหนด Columns ---
worksheet.columns = [
{ header: "ลำดับ", key: "no", width: 8 },
{ header: "วันที่คำสั่งมีผล", key: "commandDateAffect", width: 18 },
{ header: "ตำแหน่งในสายงาน", key: "positionName", width: 25 },
{ header: "ตำแหน่งประเภท", key: "positionType", width: 18 },
{ header: "ระดับ", key: "positionLevel", width: 12 },
{ header: "ระดับซี", key: "positionCee", width: 12 },
{ header: "สายงาน", key: "positionLine", width: 20 },
{ header: "ด้าน/สาขา", key: "positionPathSide", width: 15 },
{ header: "ตำแหน่งทางการบริหาร", key: "positionExecutive", width: 20 },
{ header: "ด้านทางการบริหาร", key: "positionExecutiveField", width: 20 },
{ header: "เงินเดือน", key: "amount", width: 15 },
{ header: "เงินค่าตอบแทนรายเดือน", key: "mouthSalaryAmount", width: 15 },
{ header: "เงินประจำตำแหน่ง", key: "positionSalaryAmount", width: 15 },
{ header: "เงินค่าตอบแทนพิเศษ", key: "amountSpecial", width: 15 },
{ header: "หน่วยงาน", key: "organization", width: 30 },
{ header: "ส่วนราชการระดับ 1", key: "orgChild1", width: 20 },
{ header: "ส่วนราชการระดับ 2", key: "orgChild2", width: 20 },
{ header: "ส่วนราชการระดับ 3", key: "orgChild3", width: 20 },
{ header: "ส่วนราชการระดับ 4", key: "orgChild4", width: 20 },
{ header: "ตัวย่อเลขที่ตำแหน่ง", key: "posNoAbb", width: 15 },
{ header: "เลขที่ตำแหน่ง", key: "posNo", width: 15 },
{ header: "หน่วยงานที่ออกคำสั่ง", key: "posNumCodeSit", width: 20 },
{
header: "ตัวย่อหน่วยงานที่ออกคำสั่ง",
key: "posNumCodeSitAbb",
width: 15,
},
{ header: "เลขที่คำสั่ง", key: "commandNo", width: 15 },
{ header: "ปีเลขที่คำสั่ง", key: "commandYear", width: 12 },
{ header: "วันที่ลงนาม", key: "commandDateSign", width: 18 },
{ header: "ประเภทคำสั่ง", key: "commandCodeName", width: 25 }, // AA
{ header: "หมายเหตุ", key: "remark", width: 20 },
{ header: "commandId", key: "commandId", width: 20 }, // AC (ลำดับที่ 29)
{ header: "commandCode", key: "commandCode", width: 20 }, // AD (ลำดับที่ 30)
];
// 3. Map ข้อมูล
const newData = data.map((e, index) => ({
no: index + 1,
commandDateAffect: e.commandDateAffect
? formatDateToDDMMYYYY(e.commandDateAffect)
: "",
positionName: e.positionName,
positionType: e.positionType,
positionLevel: e.positionLevel,
positionCee: e.positionCee,
positionLine: e.positionLine || "",
positionPathSide: e.positionPathSide || "",
positionExecutive: e.positionExecutive,
positionExecutiveField: e.positionExecutiveField || "",
amount: e.amount || 0,
mouthSalaryAmount: e.mouthSalaryAmount || 0,
positionSalaryAmount: e.positionSalaryAmount || 0,
amountSpecial: e.amountSpecial || 0,
organization: e.orgRoot,
orgChild1: e.orgChild1,
orgChild2: e.orgChild2,
orgChild3: e.orgChild3,
orgChild4: e.orgChild4,
posNoAbb: e.posNoAbb,
posNo: e.posNo,
posNumCodeSit: e.posNumCodeSit,
posNumCodeSitAbb: e.posNumCodeSitAbb,
commandNo: e.commandNo,
commandYear: e.commandYear ? Number(e.commandYear) + 543 : "",
commandDateSign: e.commandDateSign
? formatDateToDDMMYYYY(e.commandDateSign)
: "",
commandCodeName: store.convertCommandCodeName(e.commandCode),
remark: e.remark,
commandId: e.commandId || "",
commandCode: e.commandCode || "", // ใส่ค่าเริ่มต้นไว้
}));
worksheet.addRows(newData);
// 4. ตกแต่งสี, Dropdown และ สูตร VLOOKUP
newData.forEach((_, index) => {
const rowIndex = index + 2;
// --- ตั้งค่า Format สำหรับเซลล์วันที่ ---
// เซลล์ B (commandDateAffect) และ Z (commandDateSign)
const dateAffectCell = worksheet.getCell(`B${rowIndex}`);
const dateSignCell = worksheet.getCell(`Z${rowIndex}`);
[dateAffectCell, dateSignCell].forEach((cell) => {
cell.numFmt = "dd/mm/yyyy";
});
// --- ทำ Dropdown คอลัมน์ AA ---
// อ้างอิงรายการจาก MasterData Sheet จะทำให้ Excel ทำงานได้เสถียรกว่า (กรณีรายการเยอะ)
worksheet.getCell(`AA${rowIndex}`).dataValidation = {
type: "list",
allowBlank: true,
formulae: [`MasterData!$A$1:$A$${masterData.length}`],
};
// --- ผูกสูตรให้ commandCode (AD) เปลี่ยนตามการเลือกใน AA ---
// AA คือประเภทคำสั่ง, AD คือ commandCode
worksheet.getCell(`AD${rowIndex}`).value = {
formula: `=IFERROR(VLOOKUP(AA${rowIndex}, MasterData!$A$1:$B$${masterData.length}, 2, FALSE), "")`,
};
});
// 5. สไตล์ Header
worksheet.getRow(1).font = { bold: true };
worksheet.getRow(1).alignment = { vertical: "middle", horizontal: "center" };
// 6. เขียนไฟล์
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const url = window.URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "รายการประวัติตำแหน่งเงินเดือน.xlsx";
a.click();
window.URL.revokeObjectURL(url);
}

View file

@ -8,6 +8,7 @@ import config from "@/app.config";
import { useRoute } from "vue-router";
import { useCounterMixin } from "@/stores/mixin";
import { useEditPosDataStore } from "@/modules/04_registryPerson/stores/Edit";
import { exportToExcelPosition } from "@/modules/04_registryPerson/utils/exportPosition";
import type { QTableColumn } from "quasar";
import type { FormDataSalary } from "@/modules/04_registryPerson/interface/index/Edit";
@ -430,66 +431,67 @@ function classColorRow(isDelete: boolean, isEdit: boolean, isEntry: boolean) {
/** ฟังก์ชันดาวน์โหลดไฟล Excel */
function exportToExcel() {
const newData = rows.value.map((e: DataPosition) => {
return {
commandDateAffect: date2Thai(e.commandDateAffect),
positionName: e.positionName,
positionType: e.positionType,
positionLevel: e.positionLevel
? e.positionLevel
: e.positionCee
? e.positionCee
: "",
positionExecutive: e.positionExecutive,
amount: e.amount,
mouthSalaryAmount: e.mouthSalaryAmount,
positionSalaryAmount: e.positionSalaryAmount,
organization: findOrgName({
root: e.orgRoot,
child1: e.orgChild1,
child2: e.orgChild2,
child3: e.orgChild3,
child4: e.orgChild4,
}),
posNo:
e.posNoAbb && e.posNo
? `${e.posNoAbb} ${e.posNo}`
: e.posNo
? e.posNo
: "",
posNumCodeSit:
e.posNumCodeSitAbb && e.posNumCodeSit
? `${e.posNumCodeSit} (${e.posNumCodeSitAbb})`
: e.posNumCodeSit
? e.posNumCodeSit
: "",
commandNo:
e.commandNo && e.commandYear
? `${e.commandNo}/${Number(e.commandYear) + 543}`
: "",
commandDateSign: date2Thai(e.commandDateSign),
commandCode: store.convertCommandCodeName(e.commandCode),
remark: e.remark,
};
});
exportToExcelPosition(rows.value);
// const newData = rows.value.map((e: DataPosition) => {
// return {
// commandDateAffect: date2Thai(e.commandDateAffect),
// positionName: e.positionName,
// positionType: e.positionType,
// positionLevel: e.positionLevel
// ? e.positionLevel
// : e.positionCee
// ? e.positionCee
// : "",
// positionExecutive: e.positionExecutive,
// amount: e.amount,
// mouthSalaryAmount: e.mouthSalaryAmount,
// positionSalaryAmount: e.positionSalaryAmount,
// organization: findOrgName({
// root: e.orgRoot,
// child1: e.orgChild1,
// child2: e.orgChild2,
// child3: e.orgChild3,
// child4: e.orgChild4,
// }),
// posNo:
// e.posNoAbb && e.posNo
// ? `${e.posNoAbb} ${e.posNo}`
// : e.posNo
// ? e.posNo
// : "",
// posNumCodeSit:
// e.posNumCodeSitAbb && e.posNumCodeSit
// ? `${e.posNumCodeSit} (${e.posNumCodeSitAbb})`
// : e.posNumCodeSit
// ? e.posNumCodeSit
// : "",
// commandNo:
// e.commandNo && e.commandYear
// ? `${e.commandNo}/${Number(e.commandYear) + 543}`
// : "",
// commandDateSign: date2Thai(e.commandDateSign),
// commandCode: store.convertCommandCodeName(e.commandCode),
// remark: e.remark,
// };
// });
const headers = columns.value.map((item: any) => item.label) || []; //
const worksheet = XLSX.utils.json_to_sheet(newData, {
header: visibleColumns.value,
});
// const headers = columns.value.map((item: any) => item.label) || []; //
// const worksheet = XLSX.utils.json_to_sheet(newData, {
// header: visibleColumns.value,
// });
// ( A1, B1, C1 )
XLSX.utils.sheet_add_aoa(worksheet, [headers], { origin: "A1" });
// // ( A1, B1, C1 )
// XLSX.utils.sheet_add_aoa(worksheet, [headers], { origin: "A1" });
// Create a new workbook and append the worksheet
const workbook = XLSX.utils.book_new();
// // Create a new workbook and append the worksheet
// const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(
workbook,
worksheet,
`รายการประวัติตำแหน่งเงินเดือน`
);
XLSX.writeFile(workbook, "รายการประวัติตำแหน่งเงินเดือน.xlsx");
// XLSX.utils.book_append_sheet(
// workbook,
// worksheet,
// ``
// );
// XLSX.writeFile(workbook, ".xlsx");
}
const commandCodeOptions = ref<DataOption[]>(store.commandCodeData); //