# -*- coding: utf-8 -*- """读取 shots/manifest.json,生成甲方交付用系统页面截图说明书 PDF。""" import os, json, base64, sys, datetime from playwright.sync_api import sync_playwright SHOTS = "D:/hardman/bj_power/shots" OUT = "D:/hardman/bj_power/系统页面截图说明书.pdf" # ---------------- 每页说明文案 ---------------- # key -> [功能说明, 关键操作, 主要字段] (均为列表,逐条展示) DESC = { # ===== WMS 库房客户端 ===== "dashboard": ["登录后首页,集中展示仓库运行概览与快捷入口。", ["点击快捷卡片可跳转到对应业务模块", "顶部显示当前用户与待办提醒"], ["待入库/待出库数量", "库存总量", "近期出入库动态", "快捷入口"]], "inbound": ["统一入库管理,按「结构件入库 / 精密件SN入库 / 入库记录」三 Tab 组织。", ["选择对应 Tab 录入(前两个默认展开)", "入库记录 Tab 带查询条件、分页与全量导出"], ["入库单号", "物料编码/名称", "批次(结构件) 或 序列号(精密件)", "数量", "库位区域", "操作人", "入库时间"]], "outbound": ["统一出库管理,含「备料出库 / 通用出库 / 出库记录」。", ["备料出库受工单备料台账约束(累计≤需求)", "通用出库支持装箱号与合同号", "记录 Tab 分页+导出"], ["出库单号", "出库类型(备料/通用)", "物料/数量", "目标工位(DOCKxx)", "装箱号", "合同号", "状态"]], "inventory": ["库存查询三视图:①物料汇总(默认) ②库存明细 ③区域汇总。", ["默认首屏为物料汇总(忽略区域/质量聚合)", "点物料行下钻批次/SN 抽屉查看明细"], ["物料编码/名称", "总数/锁定/可用", "合格/未检/不合格", "分布区域", "最近入库单"]], "inspection": ["质量检验,两 Tab:检验录入(默认) / 检验记录。", ["在录入 Tab 填写检验项并提交", "提交成功后记录 Tab 自动刷新"], ["检验单号", "关联工单/物料", "检验项目", "结果(合格/不合格)", "检验人", "时间"]], "stocktake": ["库存盘点,顶部步骤条:发起 → 扫码实盘 → 确认差异写回。", ["选择区域发起盘点", "录入各物料实盘数量(支持差异)", "确认后将差异写回库存"], ["盘点单号", "盘点区域", "系统量", "实盘量", "差异量", "状态(进行中/已完成)"]], "semi": ["半成品管理,登记与查询车间半成品。", ["录入半成品入库", "按条件查询并分页展示"], ["半成品编码/名称", "数量", "状态", "入库时间", "关联工单"]], "ledger": ["工单备料台账,按工单维度展示物料备料与领用进度。", ["状态由已领量派生:待领料/部分领料/领料完结", "支持工单号/物料/状态筛选"], ["工单号", "物料编码", "需求总量", "已领量", "领料状态"]], "agv": ["AGV 配送任务监控,展示搬运任务与执行状态。", ["按状态筛选任务", "查看任务详情与时间线"], ["任务号", "起点/终点工位", "物料", "状态(待执行/执行中/完成)", "创建时间"]], "zone": ["区域维护(基础资料),管理库区/货位区域。", ["新增/编辑区域", "编码搜索大小写不敏感"], ["区域编码", "区域名称", "描述", "创建时间"]], "material": ["物料档案(基础资料),维护物料主数据,支持 Excel 导入/导出。", ["新增/编辑物料", "导入 Excel 模板(全成功或全失败)", "按筛选导出 xlsx"], ["物料编码", "名称/简称", "规格", "单位", "类型(结构件/精密件)", "说明"]], "users": ["账号管理,系统用户增删改查(admin 账号锁定不可改)。", ["新增/编辑账号并分配角色", "禁用/启用账号", "非 admin 可正常维护"], ["用户名", "姓名", "角色", "状态(启用/禁用)", "创建时间"]], "roles": ["角色管理,按树形勾选配置菜单+按钮级权限(check-strictly)。", ["编辑角色权限(树勾选父子独立)", "admin 角色锁定为全权限"], ["角色名称", "权限码", "关联账号数", "是否内置"]], "eventlog": ["操作日志,全量记录用户操作行为用于审计。", ["按时间/操作人/模块筛选", "分页浏览"], ["操作人", "模块", "动作", "IP", "操作时间"]], "changepwd": ["修改密码,当前登录用户自助改密。", ["输入原密码与新密码", "二次确认后提交"], ["原密码", "新密码", "确认新密码"]], "display": ["库存动态大屏(免登录),实时轮播展示库房运行态势。", ["大屏独立展示,无需登录", "数据实时/轮询刷新"], ["库存总量", "出入库趋势", "预警信息", "区域分布"]], # ===== MES 产线控制 ===== "workorder": ["生产工单管理,覆盖 CREATED/RELEASED/IN_PROGRESS/PAUSED/DONE 全生命周期。", ["创建工单", "下发/暂停/完工操作", "按状态筛选"], ["工单号", "产品类型", "数量", "状态", "计划/实际起止"]], "dailyplan": ["日排产,按日期将工单拆解为每日生产计划。", ["按排产日期生成计划", "查看每日排产明细"], ["工单号", "排产日期", "排产数量", "状态"]], "bom": ["物料清单(BOM),维护产品多级物料构成。", ["新增/编辑 BOM 物料与用量", "查看产品物料树"], ["产品", "子物料", "单位用量", "损耗率"]], "materialrequest": ["备料单,由日排产自动生成的下料/领料清单。", ["按日生成备料单", "查看备料明细与状态"], ["备料单号", "工单号", "物料", "需求数", "状态"]], "plcsend": ["工位组合下发,将工艺指令下发至 PLC/工位。", ["下发组合指令", "查看下发日志与结果"], ["指令号", "目标工位", "下发内容", "下发时间", "结果"]], "torque": ["拧紧查询,记录并查询拧紧力矩数据。", ["按工单/工位/时间查询", "查看合格判定"], ["工件", "工位", "力矩值", "合格阈值", "结果", "时间"]], "processflow": ["工艺流程,定义产品加工的工序流。", ["新增流程并配置工序步骤", "启用/停用流程"], ["流程名称", "工序步骤", "状态"]], "station": ["关联工位,维护工位与工艺流程/产品的绑定关系。", ["绑定工位到流程", "查看工位配置"], ["工位号", "工位名称", "关联流程"]], "performance": ["绩效报表,统计人员/产线产出与质量。", ["按时间/人员筛选", "查看完工与合格率"], ["人员", "完工数", "合格率", "工时"]], "inspect": ["巡检终端,录入质量巡检项与结果。", ["录入巡检点结果", "提交巡检记录"], ["巡检点", "结果", "巡检人", "时间"]], "scan": ["手动报工,支持扫描/录入方式上报工序完工。", ["扫描或录入报工", "填写数量与工序"], ["工单", "工序", "数量", "报工人", "时间"]], "trace": ["工件追溯,按工件/序列号查询全生命周期。", ["输入工件号查询", "查看工序时间线"], ["工件号", "工单", "各工序", "拧紧/检验", "时间线"]], "producttype": ["产品类型,维护产品主数据类型。", ["新增/编辑产品类型", "启停状态管理"], ["类型编码", "名称", "类别", "状态"]], "account": ["账号管理(MES),维护产线系统账号与工位权限。", ["新增/编辑账号", "分配角色与工位权限"], ["用户名", "姓名", "角色", "工位权限", "状态"]], "role": ["角色管理(MES),配置角色与权限。", ["编辑角色权限", "查看角色关联"], ["角色名", "权限", "关联账号"]], "eventlog_mes": ["操作日志(MES),记录产线系统操作审计。", ["按条件筛选", "分页浏览"], ["操作人", "动作", "时间"]], "changepwd_mes": ["修改密码(MES),当前用户自助改密。", ["输入原/新密码提交"], ["原密码", "新密码", "确认"]], # ===== 大屏 ===== "dashboard_dash": ["生产·仓储·产线三维总览大屏,一屏聚合三块看板。", ["生产看板:工单进度与产量", "仓储看板:库存与出入库", "产线三维模型实时展示"], ["工单进度", "产量统计", "库存态势", "产线 3D 模型"]], # ===== 工位终端 ===== "ws_login": ["工位终端登录页,账号 + 工位终端密码登录,工位号由配置文件固定。", ["输入账号与工位终端密码", "点击登录进入主界面"], ["账号", "密码", "固定工位号(由配置下发)"]], "ws_main": ["工位终端主界面,触屏操作当前工位任务。", ["工序完成上报", "暂存退库", "查看我的工作量", "放大查看图纸"], ["工位号", "当前任务", "操作按钮", "工作量统计"]], } SYSTEMS = [ ("wms", "一、库房客户端系统(WMS Client · 端口 8891)", "wms"), ("mes", "二、MES 产线控制系统(端口 8888)", "mes"), ("dash", "三、数据可视化大屏(端口 5173)", "dash"), ("ws", "四、工位终端系统(端口 8892)", "ws"), ] def b64(path): with open(path, "rb") as f: return "data:image/png;base64," + base64.b64encode(f.read()).decode() def build_html(manifest): today = datetime.date.today().strftime("%Y-%m-%d") parts = [] parts.append("""""") # 封面 parts.append("""

北京电力 WMS 系统 · 页面截图说明书

库房客户端 · MES 产线控制 · 数据大屏 · 工位终端
交付对象:甲方(项目验收 / 操作参考)
系统版本:V1.0  |  编制日期:%s
配套后端 API 服务:WMS 后端(端口 8890,无独立界面,本册聚焦 4 个前端系统)
""" % today) # 目录 toc = ['

目录

    '] total_pages = 0 for key, title, _ in SYSTEMS: n = len(manifest.get(key, [])) total_pages += n toc.append('
  1. %s%d 页
  2. ' % (title, n)) toc.append('
  3. 全册共 %d 个功能页面截图
  4. ' % total_pages) toc.append('
') toc.append('') toc.append('
') parts.append("".join(toc)) # 各系统章节 for key, title, prefix in SYSTEMS: items = manifest.get(key, []) parts.append('

%s

' % title) parts.append('' % len(items)) parts.append('
') for idx, it in enumerate(items, 1): key = os.path.splitext(os.path.basename(it["file"]))[0].split("_", 1)[-1] dkey = it.get("desc_key") or key d = DESC.get(dkey, DESC.get(key, [["", [], []]])) func = d[0] if d and d[0] else "(详见截图)" ops = d[1] if len(d) > 1 else [] fields = d[2] if len(d) > 2 else [] ops_html = "".join("
  • %s
  • " % o for o in ops) fields_html = "、".join(fields) img = b64(it["file"]) parts.append( '
    ' '

    %d.%s%s

    ' % (idx, it["title"], title.split("(")[0]) + '
    ' % img + '
    ' '
    功能说明:%s
    ' % func + ('
    关键操作:
      %s
    ' % ops_html if ops else '') + ('
    主要字段:%s
    ' % fields_html if fields else '') + '
    ' ) return "".join(parts) def main(): with open(os.path.join(SHOTS, "manifest.json"), "r", encoding="utf-8") as f: manifest = json.load(f) # 给 dash / ws / MES 的 key 映射成特殊 desc key(manifest 没存 key,从文件名取) def file_key(path): return os.path.splitext(os.path.basename(path))[0].split("_", 1)[-1] for it in manifest.get("dash", []): it["desc_key"] = "dashboard_dash" for it in manifest.get("ws", []): k = file_key(it["file"]) it["desc_key"] = "ws_login" if k == "login" else "ws_main" for it in manifest.get("mes", []): k = file_key(it["file"]) if k == "eventlog": it["desc_key"] = "eventlog_mes" if k == "changepwd": it["desc_key"] = "changepwd_mes" html = build_html(manifest) with sync_playwright() as p: browser = p.chromium.launch(channel="msedge", args=["--no-sandbox"]) page = browser.new_page() page.set_content(html, wait_until="networkidle") page.pdf(path=OUT, format="A4", landscape=True, print_background=True, margin={"top":"10mm","bottom":"10mm","left":"8mm","right":"8mm"}) browser.close() size = os.path.getsize(OUT) print("PDF 生成完成: %s (%.2f MB)" % (OUT, size/1024/1024)) if __name__ == "__main__": main()