@@ -1,216 +0,0 @@
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 ) ;
} ) ;