refactor(registry): exportToExcelPosition

This commit is contained in:
DESKTOP-1R2VSQH\Lenovo ThinkPad E490 2026-04-23 09:13:05 +07:00
parent 3cedcddbac
commit 573a76448e
4 changed files with 208 additions and 55 deletions

View file

@ -26,6 +26,7 @@
"@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",
"exceljs": "^4.4.0",
"html-to-image": "^1.11.13", "html-to-image": "^1.11.13",
"keycloak-js": "^20.0.2", "keycloak-js": "^20.0.2",
"moment": "^2.29.4", "moment": "^2.29.4",

View file

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

View file

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

View file

@ -0,0 +1,148 @@
import ExcelJS from "exceljs";
import { useCounterMixin } from "@/stores/mixin";
import { useGovernmentPosDataStore } from "@/modules/10_registry/store/Position";
import type { DataPosition } from "@/modules/10_registry/interface/review/Edit";
const { date2Thai } = useCounterMixin();
const store = useGovernmentPosDataStore();
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: "commandCode", width: 25 }, // AA
{ header: "หมายเหตุ", key: "remark", width: 20 },
{ header: "commandId", key: "commandId", width: 20 }, // AC (ลำดับที่ 29)
];
// 3. Map ข้อมูล
const newData = data.map((e, index) => ({
no: index + 1,
commandDateAffect: date2Thai(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,
mouthSalaryAmount: e.mouthSalaryAmount || 0,
positionSalaryAmount: e.positionSalaryAmount,
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: date2Thai(e.commandDateSign),
commandCode: store.convertCommandCodeName(e.commandCode),
remark: e.remark,
commandId: e.commandCode, // ใส่ค่าเริ่มต้นไว้
}));
worksheet.addRows(newData);
// 4. ตกแต่งสี, Dropdown และ สูตร VLOOKUP
newData.forEach((_, index) => {
const rowIndex = index + 2;
// --- ระบายสีเหลืองเข้ม (ถอด # ออกจาก ARGB) ---
["B", "O", "U", "V", "X", "Z"].forEach((col) => {
worksheet.getCell(`${col}${rowIndex}`).fill = {
type: "pattern",
pattern: "solid",
fgColor: { argb: "FFBF00" }, // ลบ # ออก
};
});
// --- ระบายสีเหลืองอ่อน ---
["F", "G", "H", "N", "P", "Q", "R", "S", "T", "W", "Y"].forEach((col) => {
worksheet.getCell(`${col}${rowIndex}`).fill = {
type: "pattern",
pattern: "solid",
fgColor: { argb: "FFFFD7" }, // ลบ # ออก
};
});
// --- ทำ Dropdown คอลัมน์ AA ---
// อ้างอิงรายการจาก MasterData Sheet จะทำให้ Excel ทำงานได้เสถียรกว่า (กรณีรายการเยอะ)
worksheet.getCell(`AA${rowIndex}`).dataValidation = {
type: "list",
allowBlank: true,
formulae: [`MasterData!$A$1:$A$${masterData.length}`],
};
// --- ผูกสูตรให้ commandId (AC) เปลี่ยนตามการเลือกใน AA ---
// AA คือประเภทคำสั่ง, AB คือหมายเหตุ, AC คือ commandId
worksheet.getCell(`AC${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);
}