aps-agent/build-manual-v2.js

322 lines
21 KiB
JavaScript
Raw Permalink Normal View History

// ============================================================
// 图文版操作手册(大白话 + 真实截图)生成脚本
// ============================================================
import fs from "node:fs";
import path from "node:path";
import {
AlignmentType, BorderStyle, Document, Footer, Header, HeadingLevel,
ImageRun, Packer, PageBreak, PageNumber, Paragraph, ShadingType,
Table, TableCell, TableRow, TextRun, WidthType,
} from "docx";
const outputPath = process.argv[2];
if (!outputPath) throw new Error("Usage: node build-manual-v2.js /abs/out.docx");
const ROOT = path.dirname(outputPath);
const SHOTS = path.join(ROOT, "_shots");
const T = String.raw;
const palette = { dark: "1F2A36", primary: "2F4858", light: "6B7A86", fill: "F2F5F7" };
const fontBody = { ascii: "Times New Roman", hAnsi: "Times New Roman", cs: "Times New Roman", eastAsia: "SimSun" };
const fontHead = { ascii: "Times New Roman", hAnsi: "Times New Roman", cs: "Times New Roman", eastAsia: "SimHei" };
const run = (text, options = {}) => new TextRun({ text, font: fontBody, size: 24, ...options });
const para = (children, options = {}) => new Paragraph({
spacing: { after: 140, line: 340 }, ...options,
children: Array.isArray(children) ? children : [children],
});
const body = (text) => para(run(text), { indent: { firstLine: 480 }, alignment: AlignmentType.JUSTIFIED });
const h1 = (text) => para(new TextRun({ text, font: fontHead, bold: true, size: 32, color: palette.dark }), {
heading: HeadingLevel.HEADING_1, spacing: { before: 360, after: 200 },
});
const h2 = (text) => para(new TextRun({ text, font: fontHead, bold: true, size: 26, color: palette.primary }), {
heading: HeadingLevel.HEADING_2, spacing: { before: 240, after: 140 },
});
const tip = (text) => para(
[new TextRun({ text: T`💡 `, size: 24 }), run(text, { color: palette.primary })],
{ indent: { left: 240 }, shading: { type: ShadingType.CLEAR, fill: palette.fill } },
);
let figNo = 0;
const fig = (file, captionText) => {
figNo += 1;
const img = new ImageRun({
type: "png",
data: fs.readFileSync(path.join(SHOTS, file)),
transformation: { width: 640, height: 400 },
});
return [
new Paragraph({ alignment: AlignmentType.CENTER, spacing: { before: 120, after: 60 }, children: [img] }),
para(new TextRun({ text: T`图 ${figNo} ${captionText}`, font: fontHead, size: 20, color: palette.light }), {
alignment: AlignmentType.CENTER, spacing: { after: 200 },
}),
];
};
const noBorder = { style: BorderStyle.NONE, size: 0, color: "FFFFFF" };
const table3 = (headers, rows, widths) => {
const total = widths.reduce((a, b) => a + b, 0);
const mk = (text, i, opts = {}) => new TableCell({
children: [new Paragraph({
spacing: { after: 30, line: 300 },
children: [new TextRun({ text, font: opts.bold ? fontHead : fontBody, size: 21, bold: !!opts.bold })],
})],
width: { size: widths[i], type: WidthType.DXA },
margins: { top: 70, bottom: 70, left: 100, right: 100 },
borders: opts.borders,
});
return new Table({
width: { size: total, type: WidthType.DXA },
columnWidths: widths,
rows: [
new TableRow({
tableHeader: true,
children: headers.map((h, i) => mk(h, i, {
bold: true,
borders: {
top: { style: BorderStyle.SINGLE, size: 12, color: palette.dark },
bottom: { style: BorderStyle.SINGLE, size: 6, color: palette.dark },
left: noBorder, right: noBorder,
},
})),
}),
...rows.map((row, ri) => new TableRow({
children: row.map((c, i) => mk(String(c), i, {
borders: {
top: noBorder,
bottom: ri === rows.length - 1
? { style: BorderStyle.SINGLE, size: 12, color: palette.dark } : noBorder,
left: noBorder, right: noBorder,
},
})),
})),
],
});
};
const children = [];
const add = (...items) => children.push(...items);
const PAGEBREAK = () => add(new Paragraph({ children: [new PageBreak()] }));
// ---------------- 封面 ----------------
add(
para(new TextRun({ text: T`工业智核 APS 排产智能体`, font: fontHead, bold: true, size: 48, color: palette.dark }), {
alignment: AlignmentType.CENTER, spacing: { before: 3200, after: 240 },
}),
para(new TextRun({ text: T`图 文 使 用 手 册`, font: fontHead, bold: true, size: 44, color: palette.dark }), {
alignment: AlignmentType.CENTER, spacing: { after: 900 },
}),
para(run(T`—— 全截图实操版 · 看完就会用 ——`, { color: palette.light }), {
alignment: AlignmentType.CENTER, spacing: { after: 2400 },
}),
para(run(T`版本:V1.0(按 2026-09-01 系统实际界面截图)`), { alignment: AlignmentType.CENTER, spacing: { after: 160 } }),
para(run(T`适合谁看:计划员、车间调度、实施和售前同事`), { alignment: AlignmentType.CENTER }),
new Paragraph({ children: [new PageBreak()] }),
);
// ---------------- 写在前面 ----------------
add(
h1(T`写在前面`),
body(T`这本手册不讲原理、不堆术语,就干一件事:照着截图,一步一步告诉你这个系统每个功能是干嘛的、在哪点、怎么用。手册里每一张图都是从真实系统上现截的,你照着点,看到的就是图上那样。`),
body(T`这个系统一句话就能说清:它是一个会听人话的排产助手。你在左边跟它聊天(比如“跑一版柔性排产”),它在右边把甘特图、产能、订单这些画面给你摆出来。排产行业里管这叫 APS(高级计划排产),你就把它当成一个特别懂生产、还特别守规矩的计划员搭档就行。`),
body(T`说它守规矩,是因为系统里有个铁律:凡是动真格的操作(发布版本、下达采购、改主数据、下发车间),它都会先弹一张“确认卡”让你点“批准”才执行,执行前还自动拍个快照存档。所以放心大胆地点,错了随时能回滚。`),
);
// ---------------- 一、登录 ----------------
PAGEBREAK();
add(
h1(T`一、登录:选对企业,输账号密码`),
body(T`打开系统后,先点左下角的“登录”。在弹窗里填三样东西:企业名称、用户名、密码,然后点登录按钮。账号由你们企业的管理员开通,忘了密码也找管理员,系统本身没有自助注册。`),
...fig("01-登录.png", T`登录弹窗:企业名称 + 用户名 + 密码`),
tip(T`如果你用的是桌面版(安装包装的那个),不用账号,输入授权码就能进。`),
);
// ---------------- 二、主界面 ----------------
add(
h1(T`二、先认门:主界面分三块`),
body(T`登录进来看到的就是主界面。记住三块区域,后面所有操作都绕不开它们:`),
...fig("02-主界面.png", T`主界面:左边办事入口,中间聊天,右边工具条`),
table3(
[T`区域`, T`叫什么`, T`干嘛用的`],
[
[T`左边竖栏`, T`导航栏`, T`新建任务、看订单、管主数据、进柔性工作台;下面是你的项目和任务列表`],
[T`中间大块`, T`对话区`, T`跟系统聊天的地方。底下输入框打字,回车发送;系统回的话里会带各种卡片,能直接点`],
[T`右边细条`, T`工具导轨`, T`一列小图标:会话结果、订单、主数据、柔性工作台、多智能体、图纸识别……点哪个,哪个就在右边展开`],
],
[1700, 1700, 5800],
),
body(T`刚进系统时中间会有一张欢迎页,给你三个常用建议(跑一版柔性排产、试排一版交期优先、多策略方案对比),点一下就等于把这句话打进对话区,新手从这儿开始最省事。`),
);
// ---------------- 三、对话区 ----------------
PAGEBREAK();
add(
h1(T`三、动嘴就能干活:对话区`),
h2(T`3.1 让它排一版产`),
body(T`在底部输入框打一句“跑一版柔性排产”,回车。系统想一会儿,就会回你一张结果卡:排了多少条虚拟产线、多少张工单、最晚哪天完工、有几张单能准时、有没有冲突。有什么料缺、什么工艺没确认,它也会老老实实列出来,绝不装没事。`),
...fig("03-对话柔性排产.png", T`说一句“跑一版柔性排产”,结果直接打在对话里`),
tip(T`常用的说法还有:“试排一版交期优先”“按产能均衡排一下”“换型最小化试排”。注意“试排”只是草稿,不会动真数据。`),
h2(T`3.2 让几个策略打擂台`),
body(T`拿不准哪种排法好?说一句“对比几种策略”。系统会在后台沙盒里把交期优先、产能均衡、换型最小化、战役合并、综合优化几种打法各跑一遍,给你一溜方案卡,每张卡上写清延迟、冲突、利用率、换型次数。看中哪张点“采用此方案”,它就变成正式草稿。`),
...fig("19-对话策略对比.png", T`“对比几种策略”:五种打法并排比,选好就采用`),
h2(T`3.3 有规矩不懂,直接问`),
body(T`系统里有个知识库,装着你们的工艺规定、SOP 这些文档。直接问“换线有什么规定”,它会把相关规定翻出来给你,并且标明出处;没查到就老实说没查到,绝不瞎编。`),
...fig("20-对话知识问答.png", T`知识问答:答案带来源,查不到就老实说没有`),
);
// ---------------- 四、柔性工作台 ----------------
PAGEBREAK();
add(
h1(T`四、不爱打字?点按钮也行:柔性工作台`),
body(T`点右边工具导轨的“柔性工作台”,右边就展开一个操作台,排产能点的按钮都在这儿。这是为“多品种小批量、设备能挪来挪去”的车间准备的:系统不问“走哪条产线”,只问“哪道工序”,从能干这道工序的设备池里临时挑设备,拼一条一次性的“虚拟产线”,用完就散。`),
...fig("12-柔性工作台.png", T`柔性工作台:排产、询价、异常、冲突、What-if 一页搞定`),
body(T`台面上你能干这些事:`),
table3(
[T`按钮区`, T`点了会怎样`],
[
[T`瓶颈锚排产 / 正排 / 倒排`, T`三种排法任选:按交期顺推、从交期倒推、或者卡住最慢的瓶颈工序往两头排`],
[T`三模式对比`, T`三种排法一起跑,摆在一起比,哪种好一眼看见`],
[T`滚动时域`, T`选排产看多远:实时 1 小时、短窗 2 小时、中窗 2 天、长窗 7 天`],
[T`分级重排`, T`出异常时选动作幅度:L2 短窗、L3 日窗、L4 全局重排(都要你确认才动)`],
[T`交期询价`, T`销售问“这批啥时候能交”,把产品和数量填进去,给出乐观 / 预计两个交期和缺口提示`],
[T`SAP 集成 / MES 执行`, T`跟 ERP 拉单回写、把定稿的排产下发车间(下发要二次确认)`],
],
[2600, 6600],
),
tip(T`截图下方还能看到“当前限制性瓶颈:喷涂,日产能≈1728件”——系统永远告诉你全厂最慢的那道工序是谁,产能就按它算,不虚报。`),
);
// ---------------- 五、看结果 ----------------
PAGEBREAK();
add(
h1(T`五、排完了去哪看:结果视图`),
body(T`排产有结果后,点右边导轨第一个图标“会话结果”,右边就打开一排页签:甘特图、负荷热力、利用率、交期承诺、分桶计划、KPI、对比表、世界对比、产能池。挨个看:`),
h2(T`5.1 产能池:全厂家底一页看清`),
body(T`每个工序能力池一张卡:日产能多少、几台设备、负载百分之几,未来 4 小时 / 1 天 / 1 周的预测都在。最上面黄条永远标着“限制性瓶颈”是谁。哪台设备停了、调走了,这里马上重算。`),
...fig("09-产能池.png", T`产能池仪表盘:瓶颈一目了然,设备停了立刻重算`),
h2(T`5.2 利用率:哪台设备闲、哪台累`),
body(T`设备热力图按天涂色:绿的是闲(<50%),黄橙渐忙,红的是超载(≥100%)。哪台设备天天红、哪台天天绿,排产合不合理一眼就知道。`),
...fig("23-利用率.png", T`资源利用率:设备 7 天热力图,红黄绿一眼分明`),
h2(T`5.3 KPI 仪表盘:老板要的数都在这`),
body(T`平均利用率、准时率、总延迟、在制工单、未解冲突、MES 完工率,六张卡汇总固定轨和柔性轨两本账。`),
...fig("08-KPI仪表盘.png", T`运营 KPI:利用率、准时率、延迟、冲突一屏汇总`),
h2(T`5.4 分桶计划:看得更远一点`),
body(T`这是给“排产之前”做打算的:近 7 天按日、中期按周、远期按月,把产能和需求放进桶里比。还能点“可行性”看订单能不能按期、点“库存投影”看哪天会断料、点“削峰”让系统建议把超产的桶往空档挪。`),
...fig("07-分桶计划.png", T`分桶计划:粗能力、可行性、库存投影、削峰都在这`),
h2(T`5.5 对比表与世界对比`),
body(T`“对比表”把几种策略的指标排成矩阵,最优的格子高亮;“世界对比”把两个版本的世界状态并排摆开,订单、资源占用、KPI 差了多少逐项列——回滚前后、方案前后差在哪,不用猜。`),
...fig("22-世界对比.png", T`两个世界对比:版本之间差了什么,逐项摆开`),
tip(T`甘特图页签在试排出正式版本后,会把每张工单画成时间条,工单还能直接用手拖着调(15 分钟吸附,拖完出确认卡,批准才生效)。`),
);
// ---------------- 六、订单 ----------------
PAGEBREAK();
add(
h1(T`六、订单管理:从接单到下发一条链`),
body(T`点右边导轨“订单管理”。最上面是订单总数、待审、加急、预测四个数;下面一排是“闭环 APS 阶段”流水线,把一张单子的命分成六站:商务订单 → BOM/供需净算 → 制造需求准入 → 排产草案 → P2 发布 → P3 下发 MES。每站都有独立门禁,过不去就卡在那站,系统会告诉你缺什么。`),
...fig("13-订单管理.png", T`订单管理:六站流水线,卡在哪站、缺什么,写得明明白白`),
body(T`这里你还能:新增订单、批量批准待审订单、一键清理、导入 Excel/CSV 订单、看“排产分析报告”。订单相关的口令也好使:“订单池”“提交审核”“批量批准”“插单快评”。`),
);
// ---------------- 七、主数据 ----------------
add(
h1(T`七、主数据:排产的米和锅`),
body(T`点右边导轨“主数据”。排产准不准,七分靠主数据。这里分几个页签:资源(工厂→车间→产线→设备的家谱树)、工艺模型(物料 / BOM / 工序 / 工艺路线 / 产线绑定 / 换型矩阵)、柔性资源、日历与维保、工时维护。`),
...fig("14-主数据.png", T`主数据:工艺模型页签,物料、BOM、工序、路线都在这`),
body(T`录数据有个铁顺序,千万别乱:先工厂产线 → 再物料 → 再 BOM → 再工序 → 再工艺路线 → 再绑定产线,最后才录订单。缺一环,排产要么失败,要么排出来你也找不到依据。改任何主数据都会弹确认卡,而且只影响之后的新版本,不会偷偷改历史。`),
tip(T`懒得一条条录?点“导入 Excel/CSV”,先预览校验再确认入库;订单、物料、BOM、路线、设备、模具都能导。`),
);
// ---------------- 八、排产结构 ----------------
add(
h1(T`八、排产结构:一张单凭什么这么排`),
body(T`点右边导轨“排产结构”,能看到当前排产版本的结构化分解:哪张订单拆了哪几道工序、占了哪台设备哪个时间段。排产结果不是一张孤零零的图,每道工序都能回指到订单、BOM 和工艺——“凭什么这么排”永远有据可查。`),
...fig("15-排产结构.png", T`排产结构:订单到工序到设备的完整拆解`),
);
// ---------------- 九、多智能体 ----------------
PAGEBREAK();
add(
h1(T`九、多智能体编排:让一群 AI 替你跑流程`),
body(T`点右边导轨“多智能体编排”。这功能相当于组建一支 AI 小分队:你创建一个 Goal(比如“锐扬排产全流程”,自带解析数据 → MRP 分解 → 下达 → 排产 → 出报告五件事),点“分发执行”,系统就把活派给有对应本事的智能体,没有合适的就现场造一个。`),
...fig("16-多智能体编排.png", T`多智能体编排:挂 Goal、自动分发、看门狗盯梢`),
body(T`底下有个“看门狗”盯着:哪个任务超时(默认 45 秒)就收回来重排,反复失败就升级叫人来看;全干完自动结案。消息总线里能实时看到智能体之间接力传话。干砸了的任务会老实标 FAILED,不会假装成功。`),
);
// ---------------- 十、图纸识别 ----------------
add(
h1(T`十、图纸识别:DXF 图纸直接变主数据`),
body(T`点右边导轨“图纸识别”,把 CAD 的 DXF 图纸传上去(或填个路径、扫个文件夹)。系统自动读标题栏:图号、名称、比例、版本,每个字段都标了可信度;图纸还能在页面上缩放、平移、按图层开关地预览。`),
...fig("17-图纸识别.png", T`图纸识别:读标题栏、标置信度、SVG 预览`),
body(T`认出来的物料信息会变成“候选”,你核对一遍、点确认,才正式写进主数据。图纸上没有的信息(材质、工时、BOM 子件数量),系统一律标“缺失”,绝不替你瞎猜。`),
);
// ---------------- 十一、命令面板 ----------------
add(
h1(T`十一、懒人神器:Ctrl+K 命令面板`),
body(T`键盘按 Ctrl+K,屏幕中央弹出命令面板。想找什么功能直接打字过滤,比如输“柔性”,“柔性工作台”“柔性甘特”就都冒出来了,回车直达。所有面板和视图都能这么跳。`),
...fig("18-命令面板.png", T`Ctrl+K:输俩字,功能直达`),
);
// ---------------- 十二、安全规矩 ----------------
PAGEBREAK();
add(
h1(T`十二、几条安全规矩(知道就不慌)`),
table3(
[T`规矩`, T`通俗解释`],
[
[T`确认卡`, T`凡是动真格的(发布、下达、改主数据、下发 MES),先弹卡让你批准,不批不执行`],
[T`自动存档`, T`每次执行确认卡之前,系统自动拍快照。你也可以随时说“存个档”`],
[T`随时回滚`, T`点时间线上的存档就能回到过去,回滚前还会再帮你存一次现在的状态`],
[T`试排不动真数据`, T`“试排”“对比策略”都在草稿或沙盒里玩,主干数据分毫不动`],
[T`审计留痕`, T`谁、什么时候、批了什么,全部记账且改不了(哈希链),出了事能倒查`],
[T`不装成功`, T`排不动、缺料、没设备,系统直接红色阻断并写清原因,不会给你假绿灯`],
],
[2200, 7000],
),
);
// ---------------- 附录 ----------------
add(
h1(T`附录:张口就来的口令大全`),
table3(
[T`想干啥`, T`就说`],
[
[T`排产`, T`试排一版交期优先 / 按产能均衡排一下 / 换型最小化试排 / 跑一版柔性排产 / 三模式对比`],
[T`比方案`, T`对比几种策略 → 看中后点“采用此方案” → “发布这个版本”`],
[T`订单分解`, T`执行MRP分解 / 分解订单 SO-001 / 生成采购建议 / 下达MRP建议单`],
[T`插单`, T`插单快评 → 看影响 → 采用插单`],
[T`看计划层`, T`时间分桶 / 可行性分析 / 能否按期 / 库存投影 / 削峰 / 产供决策`],
[T`管订单`, T`订单池 / 提交审核 / 批量批准 / 查看预测 / 预测转正`],
[T`问规矩`, T`换线有什么规定 / @知识:标题`],
[T`出报表`, T`生成日报 / 版本对比 / 打开仪表盘`],
[T`存档后悔`, T`存个档 / 回滚`],
[T`数据`, T`分析一下数据文件 / 导入完整生产路线 / 清空数据重新初始化`],
],
[2000, 7200],
),
body(T`最后一句:系统答不上来的话会老实说“听不懂”或“没查到”,绝不糊弄你。遇到红色阻断别硬来,按它给的修复入口把缺的料、缺的工艺补上,再重新排就是了。`),
);
const doc = new Document({
features: { updateFields: false },
sections: [{
properties: {
page: { size: { width: 11906, height: 16838 }, margin: { top: 1440, bottom: 1440, left: 1440, right: 1440 } },
},
headers: {
default: new Header({ children: [para(run(T`工业智核 APS 排产智能体 · 图文使用手册`, { size: 18, color: palette.light }), { alignment: AlignmentType.CENTER, spacing: { after: 0 } })] }),
},
footers: {
default: new Footer({ children: [para(new TextRun({ children: [PageNumber.CURRENT], size: 18 }), { alignment: AlignmentType.CENTER, spacing: { after: 0 } })] }),
},
children,
}],
});
fs.writeFileSync(outputPath, await Packer.toBuffer(doc));