[FIX] 修正盤調單明細插入時的欄位名稱錯誤並更新簡報/圖片處理套件
This commit is contained in:
216
generate_pptx.cjs
Normal file
216
generate_pptx.cjs
Normal file
@@ -0,0 +1,216 @@
|
||||
const pptxgen = require("pptxgenjs");
|
||||
const React = require("react");
|
||||
const ReactDOMServer = require("react-dom/server");
|
||||
const sharp = require("sharp");
|
||||
const {
|
||||
LayoutDashboard,
|
||||
Mail,
|
||||
Cpu,
|
||||
Layout,
|
||||
BellRing,
|
||||
Factory,
|
||||
ShieldAlert,
|
||||
Target,
|
||||
Zap
|
||||
} = require("lucide-react");
|
||||
|
||||
let pres = new pptxgen();
|
||||
pres.layout = 'LAYOUT_16x9';
|
||||
pres.author = 'AI Assistant';
|
||||
pres.title = '每週三的開發成果發表';
|
||||
|
||||
// Colors
|
||||
const colors = {
|
||||
primary: "36454F",
|
||||
secondary: "F2F2F2",
|
||||
accent: "028090",
|
||||
textWhite: "FFFFFF",
|
||||
textDark: "1E293B",
|
||||
textMuted: "64748B"
|
||||
};
|
||||
|
||||
// Icon rendering helper
|
||||
function renderIconSvg(IconComponent, color = "#000000", size = 256) {
|
||||
return ReactDOMServer.renderToStaticMarkup(
|
||||
React.createElement(IconComponent, { color, size: String(size) })
|
||||
);
|
||||
}
|
||||
|
||||
async function iconToBase64Png(IconComponent, color, size = 256) {
|
||||
const svg = renderIconSvg(IconComponent, color, size);
|
||||
const pngBuffer = await sharp(Buffer.from(svg)).png().toBuffer();
|
||||
return "image/png;base64," + pngBuffer.toString("base64");
|
||||
}
|
||||
|
||||
pres.defineSlideMaster({
|
||||
title: 'MASTER_TITLE', background: { color: colors.primary },
|
||||
});
|
||||
|
||||
pres.defineSlideMaster({
|
||||
title: 'MASTER_CONTENT', background: { color: colors.secondary },
|
||||
});
|
||||
|
||||
async function generate() {
|
||||
// ==========================================
|
||||
// Slide 1: 封面 (Title Slide)
|
||||
// ==========================================
|
||||
let slide1 = pres.addSlide({ masterName: "MASTER_TITLE" });
|
||||
slide1.addShape(pres.shapes.RECTANGLE, { x: 0, y: 2.8, w: 10, h: 0.1, fill: { color: colors.accent } });
|
||||
|
||||
slide1.addText("每週三的開發成果發表", {
|
||||
x: 0.5, y: 1.5, w: 9, h: 1.2,
|
||||
fontSize: 44, fontFace: "Arial Black", color: colors.textWhite,
|
||||
align: "center", bold: true
|
||||
});
|
||||
|
||||
slide1.addText("Star ERP & Star Cloud 核心功能、架構優化與系統穩定性提升", {
|
||||
x: 0.5, y: 3.2, w: 9, h: 1,
|
||||
fontSize: 20, fontFace: "Outfit", color: "CADCFC",
|
||||
align: "center"
|
||||
});
|
||||
|
||||
|
||||
// ==========================================
|
||||
// Slide 2: 本週核心亮點總覽 (Agenda)
|
||||
// ==========================================
|
||||
let slide2 = pres.addSlide({ masterName: "MASTER_CONTENT" });
|
||||
slide2.addText("本週核心亮點總覽", { x: 0.5, y: 0.5, w: 9, h: 0.8, fontSize: 32, fontFace: "Arial Black", color: colors.primary, bold: true });
|
||||
slide2.addShape(pres.shapes.RECTANGLE, { x: 0.5, y: 1.3, w: 7.5, h: 0.05, fill: { color: colors.accent } });
|
||||
|
||||
let card1Y = 2.2;
|
||||
const cardShadow = { type: "outer", color: "000000", blur: 6, offset: 2, angle: 135, opacity: 0.1 };
|
||||
|
||||
// Card 1: UI
|
||||
slide2.addShape(pres.shapes.RECTANGLE, { x: 0.5, y: card1Y, w: 2.8, h: 2.7, fill: { color: colors.textWhite }, shadow: cardShadow });
|
||||
const iconCloud = await iconToBase64Png(LayoutDashboard, "#" + colors.accent);
|
||||
slide2.addImage({ data: iconCloud, x: 1.5, y: card1Y + 0.2, w: 0.6, h: 0.6 });
|
||||
slide2.addText("UI 視覺重構", { x: 0.7, y: card1Y + 0.9, w: 2.4, h: 0.4, fontSize: 18, fontFace: "Arial", color: colors.accent, bold: true, align: "center" });
|
||||
slide2.addText("Star Cloud 系統日誌大改造,全面導入極簡奢華風 UI。", { x: 0.7, y: card1Y + 1.4, w: 2.4, h: 1.2, fontSize: 14, fontFace: "Calibri", color: colors.textDark, align: "center", valign: "top" });
|
||||
|
||||
// Card 2: ERP
|
||||
slide2.addShape(pres.shapes.RECTANGLE, { x: 3.6, y: card1Y, w: 2.8, h: 2.7, fill: { color: colors.textWhite }, shadow: cardShadow });
|
||||
const iconErp = await iconToBase64Png(Mail, "#" + colors.accent);
|
||||
slide2.addImage({ data: iconErp, x: 4.6, y: card1Y + 0.2, w: 0.6, h: 0.6 });
|
||||
slide2.addText("系統提醒機制", { x: 3.8, y: card1Y + 0.9, w: 2.4, h: 0.4, fontSize: 18, fontFace: "Arial", color: colors.accent, bold: true, align: "center" });
|
||||
slide2.addText("Star ERP 公共事業費提醒與生產工單損耗產出管理。", { x: 3.8, y: card1Y + 1.4, w: 2.4, h: 1.2, fontSize: 14, fontFace: "Calibri", color: colors.textDark, align: "center", valign: "top" });
|
||||
|
||||
// Card 3: Arch
|
||||
slide2.addShape(pres.shapes.RECTANGLE, { x: 6.7, y: card1Y, w: 2.8, h: 2.7, fill: { color: colors.textWhite }, shadow: cardShadow });
|
||||
const iconArch = await iconToBase64Png(Cpu, "#" + colors.accent);
|
||||
slide2.addImage({ data: iconArch, x: 7.7, y: card1Y + 0.2, w: 0.6, h: 0.6 });
|
||||
slide2.addText("底層核心架構", { x: 6.9, y: card1Y + 0.9, w: 2.4, h: 0.4, fontSize: 18, fontFace: "Arial", color: colors.accent, bold: true, align: "center" });
|
||||
slide2.addText("修正閉包變數地雷並導入 SQL select 效能優化與多租戶隔離。", { x: 6.9, y: card1Y + 1.4, w: 2.4, h: 1.2, fontSize: 14, fontFace: "Calibri", color: colors.textDark, align: "center", valign: "top" });
|
||||
|
||||
|
||||
// ==========================================
|
||||
// Slide 3: 亮點 1 - Star Cloud 視覺與架構升級
|
||||
// ==========================================
|
||||
let slide3 = pres.addSlide({ masterName: "MASTER_CONTENT" });
|
||||
slide3.addText("亮點 1: Star Cloud 視覺與架構升級", { x: 0.5, y: 0.5, w: 9, h: 0.8, fontSize: 32, fontFace: "Arial Black", color: colors.primary, bold: true });
|
||||
slide3.addShape(pres.shapes.RECTANGLE, { x: 0.5, y: 1.3, w: 6.5, h: 0.05, fill: { color: colors.accent } });
|
||||
|
||||
slide3.addText("本週開發功能與 Demo", { x: 0.5, y: 1.8, w: 4.5, h: 0.5, fontSize: 24, fontFace: "Arial", color: colors.accent, bold: true });
|
||||
slide3.addText("儀表板與系統日誌介面大改造", { x: 0.5, y: 2.5, w: 4.5, h: 0.4, fontSize: 18, fontFace: "Calibri", color: colors.textDark, bullet: true });
|
||||
slide3.addText("移除舊版復古風,全面導入極簡奢華風、深色模式及 Outfit 字型", { x: 0.5, y: 3.0, w: 4.5, h: 0.6, fontSize: 18, fontFace: "Calibri", color: colors.textDark, bullet: true });
|
||||
slide3.addText("顯著提升 SaaS 系統高級感", { x: 0.5, y: 3.7, w: 4.5, h: 0.4, fontSize: 18, fontFace: "Calibri", color: colors.textDark, bullet: true });
|
||||
|
||||
slide3.addShape(pres.shapes.RECTANGLE, { x: 5.2, y: 1.8, w: 4.3, h: 3, fill: { color: colors.primary }, shadow: { ...cardShadow, opacity: 0.2 } });
|
||||
const iconZap = await iconToBase64Png(Zap, "#FFFFFF");
|
||||
slide3.addImage({ data: iconZap, x: 5.7, y: 2.0, w: 0.4, h: 0.4 });
|
||||
slide3.addText("技術邏輯", { x: 6.2, y: 2.0, w: 3.5, h: 0.5, fontSize: 22, fontFace: "Arial", color: colors.textWhite, bold: true });
|
||||
slide3.addText("建構 IoT 高併發機台通訊架構", { x: 5.7, y: 2.6, w: 3.5, h: 0.4, fontSize: 16, fontFace: "Calibri", color: colors.textWhite, bullet: true });
|
||||
slide3.addText("全面改用 Redis Queue 異步處理海量機台心跳與日誌回報", { x: 5.7, y: 3.1, w: 3.5, h: 0.6, fontSize: 16, fontFace: "Calibri", color: colors.textWhite, bullet: true });
|
||||
|
||||
|
||||
// ==========================================
|
||||
// Slide 4: 亮點 2 - Star ERP 公共事業費發信機制
|
||||
// ==========================================
|
||||
let slide4 = pres.addSlide({ masterName: "MASTER_CONTENT" });
|
||||
slide4.addText("亮點 2: Star ERP 公共事業費發信機制", { x: 0.5, y: 0.5, w: 9, h: 0.8, fontSize: 32, fontFace: "Arial Black", color: colors.primary, bold: true });
|
||||
slide4.addShape(pres.shapes.RECTANGLE, { x: 0.5, y: 1.3, w: 6.5, h: 0.05, fill: { color: colors.accent } });
|
||||
|
||||
slide4.addText("本週開發功能", { x: 0.5, y: 1.6, w: 9, h: 0.5, fontSize: 24, fontFace: "Arial", color: colors.accent, bold: true });
|
||||
const iconBell = await iconToBase64Png(BellRing, "#" + colors.accent);
|
||||
slide4.addImage({ data: iconBell, x: 2.0, y: 1.65, w: 0.4, h: 0.4 });
|
||||
slide4.addText("實作公共事業費逾期提醒機制、租戶自訂通知設定及發送測試信功能。", { x: 0.5, y: 2.1, w: 9, h: 0.5, fontSize: 18, fontFace: "Calibri", color: colors.textDark });
|
||||
|
||||
let timelineY = 3.5;
|
||||
slide4.addShape(pres.shapes.LINE, { x: 1, y: timelineY, w: 8, h: 0, line: { color: colors.primary, width: 4 } });
|
||||
slide4.addShape(pres.shapes.OVAL, { x: 1.5, y: timelineY - 0.2, w: 0.4, h: 0.4, fill: { color: colors.accent } });
|
||||
slide4.addText("前 7 天", { x: 1.0, y: timelineY + 0.3, w: 1.4, h: 0.5, fontSize: 14, fontFace: "Arial", align: "center", bold: true, color: colors.textDark });
|
||||
slide4.addShape(pres.shapes.OVAL, { x: 3.8, y: timelineY - 0.2, w: 0.4, h: 0.4, fill: { color: colors.accent } });
|
||||
slide4.addText("前 3 天", { x: 3.3, y: timelineY + 0.3, w: 1.4, h: 0.5, fontSize: 14, fontFace: "Arial", align: "center", bold: true, color: colors.textDark });
|
||||
slide4.addShape(pres.shapes.OVAL, { x: 6.1, y: timelineY - 0.2, w: 0.4, h: 0.4, fill: { color: colors.accent } });
|
||||
slide4.addText("到期日 (0 天)", { x: 5.6, y: timelineY + 0.3, w: 1.4, h: 0.5, fontSize: 14, fontFace: "Arial", align: "center", bold: true, color: colors.textDark });
|
||||
slide4.addShape(pres.shapes.OVAL, { x: 8.4, y: timelineY - 0.2, w: 0.4, h: 0.4, fill: { color: "990011" } });
|
||||
slide4.addText("逾期 (每日)", { x: 7.9, y: timelineY + 0.3, w: 1.4, h: 0.5, fontSize: 14, fontFace: "Arial", align: "center", bold: true, color: "990011" });
|
||||
|
||||
slide4.addText("附件防呆:實作公共事業費憑證附件上傳管理與前端防呆機制", { x: 0.5, y: 4.5, w: 9, h: 0.5, fontSize: 18, fontFace: "Calibri", color: colors.textDark, align: "center", italic: true });
|
||||
|
||||
|
||||
// ==========================================
|
||||
// Slide 5: 亮點 3 - Star ERP 生產工單耗損與產出管理
|
||||
// ==========================================
|
||||
let slide5 = pres.addSlide({ masterName: "MASTER_CONTENT" });
|
||||
slide5.addText("亮點 3: Star ERP 生產工單耗損與產出管理", { x: 0.5, y: 0.5, w: 9, h: 0.8, fontSize: 32, fontFace: "Arial Black", color: colors.primary, bold: true });
|
||||
slide5.addShape(pres.shapes.RECTANGLE, { x: 0.5, y: 1.3, w: 6.5, h: 0.05, fill: { color: colors.accent } });
|
||||
|
||||
slide5.addText("本週開發功能", { x: 0.5, y: 1.6, w: 9, h: 0.5, fontSize: 24, fontFace: "Arial", color: colors.accent, bold: true });
|
||||
const iconFactory = await iconToBase64Png(Factory, "#" + colors.accent);
|
||||
slide5.addImage({ data: iconFactory, x: 2.0, y: 1.65, w: 0.4, h: 0.4 });
|
||||
slide5.addText("新增生產工單「實際產量」填寫與「耗損原因」紀錄。", { x: 0.5, y: 2.1, w: 9, h: 0.5, fontSize: 18, fontFace: "Calibri", color: colors.textDark });
|
||||
|
||||
slide5.addShape(pres.shapes.RECTANGLE, { x: 0.5, y: 2.8, w: 4.2, h: 2.2, fill: { color: colors.textWhite }, shadow: cardShadow });
|
||||
slide5.addText("後端設計", { x: 0.7, y: 2.9, w: 3.8, h: 0.5, fontSize: 20, fontFace: "Arial", color: colors.accent, bold: true });
|
||||
slide5.addText([
|
||||
{ text: "新增資料庫欄位以儲存實際產量與耗損原因", options: { bullet: true, breakLine: true } },
|
||||
{ text: "完善 API 狀態推進與實際產量/成本的計算邏輯", options: { bullet: true, breakLine: true } },
|
||||
{ text: "修正完工入庫金額未計算問題", options: { bullet: true } }
|
||||
], { x: 0.7, y: 3.4, w: 3.8, h: 1.4, fontSize: 16, fontFace: "Calibri", color: colors.textDark, valign: "top" });
|
||||
|
||||
slide5.addShape(pres.shapes.RECTANGLE, { x: 5.3, y: 2.8, w: 4.2, h: 2.2, fill: { color: colors.textWhite }, shadow: cardShadow });
|
||||
slide5.addText("前端優化", { x: 5.5, y: 2.9, w: 3.8, h: 0.5, fontSize: 20, fontFace: "Arial", color: colors.accent, bold: true });
|
||||
slide5.addText([
|
||||
{ text: "完工入庫實作原生數字輸入框", options: { bullet: true, breakLine: true } },
|
||||
{ text: "支援 step=1 加減功能", options: { bullet: true, breakLine: true } },
|
||||
{ text: "加入嚴謹的資料驗證", options: { bullet: true } }
|
||||
], { x: 5.5, y: 3.4, w: 3.8, h: 1.4, fontSize: 16, fontFace: "Calibri", color: colors.textDark, valign: "top" });
|
||||
|
||||
|
||||
// ==========================================
|
||||
// Slide 6: 深層技術挑戰與解法
|
||||
// ==========================================
|
||||
let slide6 = pres.addSlide({ masterName: "MASTER_CONTENT" });
|
||||
slide6.addText("深層技術挑戰與解法", { x: 0.5, y: 0.5, w: 9, h: 0.8, fontSize: 32, fontFace: "Arial Black", color: colors.primary, bold: true });
|
||||
slide6.addShape(pres.shapes.RECTANGLE, { x: 0.5, y: 1.3, w: 1, h: 0.05, fill: { color: colors.accent } });
|
||||
|
||||
slide6.addShape(pres.shapes.RECTANGLE, { x: 0.5, y: 1.8, w: 4.2, h: 2.8, fill: { color: "990011" }, shadow: { ...cardShadow, opacity: 0.2 } });
|
||||
slide6.addText("日漸增長的效能隱患與閉包變數地雷", { x: 0.7, y: 2.0, w: 3.8, h: 0.8, fontSize: 20, fontFace: "Arial", color: colors.textWhite, bold: true });
|
||||
slide6.addShape(pres.shapes.LINE, { x: 0.7, y: 2.8, w: 3.8, h: 0, line: { color: colors.textWhite, width: 2 } });
|
||||
slide6.addText("全站 Service/Controller 加入 SQL select 具體欄位限制,配置租戶資料表索引 (Index);修正 PHP InventoryService 閉包參考失效的潛藏 Bug。", { x: 0.7, y: 3.0, w: 3.8, h: 1.4, fontSize: 16, fontFace: "Calibri", color: colors.textWhite, valign: "top" });
|
||||
|
||||
slide6.addShape(pres.shapes.RECTANGLE, { x: 5.3, y: 1.8, w: 4.2, h: 2.8, fill: { color: colors.accent }, shadow: { ...cardShadow, opacity: 0.2 } });
|
||||
slide6.addText("跨租戶管理的安全顧慮", { x: 5.5, y: 2.0, w: 3.8, h: 0.8, fontSize: 20, fontFace: "Arial", color: colors.textWhite, bold: true });
|
||||
slide6.addShape(pres.shapes.LINE, { x: 5.5, y: 2.8, w: 3.8, h: 0, line: { color: colors.textWhite, width: 2 } });
|
||||
slide6.addText("強化與整理了開發與 Git 規範文件,隔離多租戶目錄被誤推的風險。", { x: 5.5, y: 3.0, w: 3.8, h: 1.4, fontSize: 16, fontFace: "Calibri", color: colors.textWhite, valign: "top" });
|
||||
|
||||
|
||||
// ==========================================
|
||||
// Slide 7: 未來計畫
|
||||
// ==========================================
|
||||
let slide7 = pres.addSlide({ masterName: "MASTER_TITLE" });
|
||||
const iconTarget = await iconToBase64Png(Target, "#" + colors.accent);
|
||||
slide7.addImage({ data: iconTarget, x: 4.8, y: 0.8, w: 0.5, h: 0.5 });
|
||||
slide7.addText("未來計畫 (Next Steps)", { x: 0.5, y: 1.5, w: 9, h: 1.0, fontSize: 36, fontFace: "Arial Black", color: colors.accent, align: "center", bold: true });
|
||||
slide7.addText("開始開發 Star Cloud,維護 ERP 系統", { x: 0.5, y: 2.8, w: 9, h: 1.5, fontSize: 28, fontFace: "Arial", color: colors.textWhite, align: "center" });
|
||||
|
||||
// Save
|
||||
pres.writeFile({ fileName: "/home/mama/projects/demo_day_presentation.pptx" }).then(fileName => {
|
||||
console.log(`created file: ${fileName}`);
|
||||
});
|
||||
}
|
||||
|
||||
generate().catch(err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user