1. 新增SSE看板广播机制,业务操作后主动推送刷新事件 2. 新增产线工序横道图与工位状态展示面板 3. 移除dashboard无用的three.js依赖 4. 重构WMS客户端布局与WMS前端资源哈希 5. 新增工位终端时钟图标与大屏自适应布局 6. 新增系统截图脚本与说明书生成工具 7. 修复多处代码细节与空值处理逻辑
177 lines
7.8 KiB
Python
177 lines
7.8 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""逐页截图 4 个前端系统,输出 PNG + manifest.json 供生成 PDF。"""
|
||
import os, json, sys
|
||
from playwright.sync_api import sync_playwright
|
||
|
||
SHOTS = "D:/hardman/bj_power/shots"
|
||
os.makedirs(SHOTS, exist_ok=True)
|
||
|
||
WMS = "http://127.0.0.1:8891"
|
||
MES = "http://127.0.0.1:8888"
|
||
DASH = "http://127.0.0.1:5173"
|
||
WS = "http://127.0.0.1:8892"
|
||
|
||
W = 1920
|
||
H = 1080
|
||
|
||
# (route, 标题, 说明用的key) —— 标题用于 PDF
|
||
WMS_PAGES = [
|
||
("/", "工作台", "dashboard"),
|
||
("/inbound", "入库管理", "inbound"),
|
||
("/outbound", "出库管理", "outbound"),
|
||
("/inventory", "库存查询", "inventory"),
|
||
("/inspection", "质量检验", "inspection"),
|
||
("/stocktake", "库存盘点", "stocktake"),
|
||
("/semi", "半成品管理", "semi"),
|
||
("/ledger", "工单备料台账", "ledger"),
|
||
("/agv", "AGV配送", "agv"),
|
||
("/zone", "区域维护", "zone"),
|
||
("/material", "物料档案", "material"),
|
||
("/users", "账号管理", "users"),
|
||
("/roles", "角色管理", "roles"),
|
||
("/event-log", "操作日志", "eventlog"),
|
||
("/change-password", "修改密码", "changepwd"),
|
||
("/display", "库存动态大屏", "display"),
|
||
]
|
||
|
||
MES_PAGES = [
|
||
("/work-order", "工单管理", "workorder"),
|
||
("/daily-plan", "日排产", "dailyplan"),
|
||
("/bom", "物料清单", "bom"),
|
||
("/material-request","备料单", "materialrequest"),
|
||
("/plc-send", "工位组合下发", "plcsend"),
|
||
("/torque", "拧紧查询", "torque"),
|
||
("/process-flow", "工艺流程", "processflow"),
|
||
("/station", "关联工位", "station"),
|
||
("/performance", "绩效报表", "performance"),
|
||
("/inspect", "巡检终端", "inspect"),
|
||
("/scan", "手动报工", "scan"),
|
||
("/trace", "工件追溯", "trace"),
|
||
("/product-type", "产品类型", "producttype"),
|
||
("/account", "账号管理", "account"),
|
||
("/role", "角色管理", "role"),
|
||
("/event-log", "操作日志", "eventlog"),
|
||
("/change-password", "修改密码", "changepwd"),
|
||
]
|
||
|
||
manifest = {"wms": [], "mes": [], "dash": [], "ws": []}
|
||
|
||
def log(msg):
|
||
sys.stdout.write(msg + "\n"); sys.stdout.flush()
|
||
|
||
def do_login(page, base, user, pwd):
|
||
page.goto(base + "/login", wait_until="networkidle")
|
||
page.wait_for_timeout(600)
|
||
filled = False
|
||
for sel in ['input[placeholder*="用户名"]', 'input[placeholder*="账号"]',
|
||
'input[name="username"]', 'input[type="text"]']:
|
||
try:
|
||
page.fill(sel, user, timeout=3000); filled = True; break
|
||
except Exception:
|
||
continue
|
||
if not filled:
|
||
raise RuntimeError("找不到用户名输入框")
|
||
page.fill('input[type="password"]', pwd, timeout=5000)
|
||
page.click('button.el-button--primary', timeout=5000)
|
||
page.wait_for_timeout(1500)
|
||
return "login" not in page.url
|
||
|
||
def shot(page, url, path, wait=1500, full=False):
|
||
page.goto(url, wait_until="networkidle")
|
||
page.wait_for_timeout(wait)
|
||
page.screenshot(path=path, full_page=full)
|
||
return os.path.getsize(path)
|
||
|
||
def main():
|
||
with sync_playwright() as p:
|
||
browser = p.chromium.launch(channel="msedge", args=["--no-sandbox", "--disable-gpu"])
|
||
ctx = browser.new_context(viewport={"width": W, "height": H}, device_scale_factor=1)
|
||
page = ctx.new_page()
|
||
page.set_default_timeout(15000)
|
||
|
||
# ---------- WMS 库房客户端 ----------
|
||
log("===== WMS 库房客户端 (8891) =====")
|
||
ok = do_login(page, WMS, "admin", "123456")
|
||
log(" login=%s" % ok)
|
||
for route, title, key in WMS_PAGES:
|
||
d = "wms" if route != "/display" else "wms"
|
||
out = os.path.join(SHOTS, d, "%02d_%s.png" % (len(manifest["wms"])+1, key))
|
||
try:
|
||
if route == "/display":
|
||
# 免登录大屏,单独开上下文避免 token 干扰
|
||
page2 = ctx.new_page()
|
||
sz = shot(page2, WMS + "/display", out, wait=3500)
|
||
page2.close()
|
||
else:
|
||
sz = shot(page, WMS + route, out, wait=1600)
|
||
manifest["wms"].append({"route": route, "title": title, "file": out, "size": sz})
|
||
log(" [OK ] %-14s %s (%dKB)" % (title, route, sz//1024))
|
||
except Exception as e:
|
||
log(" [ERR] %-14s %s -> %s" % (title, route, e))
|
||
|
||
# ---------- MES 产线控制 ----------
|
||
log("===== MES 产线控制 (8888) =====")
|
||
ok = do_login(page, MES, "admin", "123456")
|
||
log(" login=%s" % ok)
|
||
for route, title, key in MES_PAGES:
|
||
out = os.path.join(SHOTS, "mes", "%02d_%s.png" % (len(manifest["mes"])+1, key))
|
||
try:
|
||
sz = shot(page, MES + route, out, wait=1600)
|
||
manifest["mes"].append({"route": route, "title": title, "file": out, "size": sz})
|
||
log(" [OK ] %-14s %s (%dKB)" % (title, route, sz//1024))
|
||
except Exception as e:
|
||
log(" [ERR] %-14s %s -> %s" % (title, route, e))
|
||
|
||
# ---------- 大屏 (5173) ----------
|
||
log("===== 数据大屏 (5173) =====")
|
||
# 整页(含生产/仓储/3D产线三板)
|
||
dctx = browser.new_context(viewport={"width": W, "height": 1440}, device_scale_factor=1)
|
||
dpage = dctx.new_page()
|
||
try:
|
||
dpage.goto(DASH + "/", wait_until="load")
|
||
# 等数据加载 + 3D 渲染(大屏用轮询/SSE,networkidle 不可达)
|
||
dpage.wait_for_timeout(7000)
|
||
out = os.path.join(SHOTS, "dash", "01_dashboard.png")
|
||
dpage.screenshot(path=out, full_page=True)
|
||
manifest["dash"].append({"route": "/", "title": "生产·仓储·产线三维总览", "file": out, "size": os.path.getsize(out)})
|
||
log(" [OK ] 大屏总览 (%dKB)" % (os.path.getsize(out)//1024))
|
||
except Exception as e:
|
||
log(" [ERR] 大屏 -> %s" % e)
|
||
dpage.close(); dctx.close()
|
||
|
||
# ---------- 工位终端 (8892) ----------
|
||
log("===== 工位终端 (8892) =====")
|
||
wctx = browser.new_context(viewport={"width": W, "height": H}, device_scale_factor=1)
|
||
wpage = wctx.new_page()
|
||
try:
|
||
wpage.goto(WS + "/#/login", wait_until="networkidle")
|
||
wpage.wait_for_timeout(1200)
|
||
out = os.path.join(SHOTS, "ws", "01_login.png")
|
||
wpage.screenshot(path=out)
|
||
manifest["ws"].append({"route": "/#/login", "title": "工位终端登录", "file": out, "size": os.path.getsize(out)})
|
||
log(" [OK ] 工位登录页")
|
||
# 登录
|
||
wpage.fill('input[placeholder*="账号"]', "syf", timeout=5000)
|
||
wpage.fill('input[type="password"]', "123456", timeout=5000)
|
||
wpage.click('button.el-button--primary', timeout=5000)
|
||
wpage.wait_for_url("**/#/main", timeout=8000)
|
||
wpage.wait_for_timeout(2500)
|
||
out = os.path.join(SHOTS, "ws", "02_main.png")
|
||
wpage.screenshot(path=out)
|
||
manifest["ws"].append({"route": "/#/main", "title": "工位终端主界面", "file": out, "size": os.path.getsize(out)})
|
||
log(" [OK ] 工位主界面")
|
||
except Exception as e:
|
||
log(" [ERR] 工位终端 -> %s" % e)
|
||
wpage.close(); wctx.close()
|
||
|
||
browser.close()
|
||
|
||
with open(os.path.join(SHOTS, "manifest.json"), "w", encoding="utf-8") as f:
|
||
json.dump(manifest, f, ensure_ascii=False, indent=2)
|
||
log("===== DONE =====")
|
||
log("WMS=%d MES=%d DASH=%d WS=%d" % (
|
||
len(manifest["wms"]), len(manifest["mes"]), len(manifest["dash"]), len(manifest["ws"])))
|
||
|
||
if __name__ == "__main__":
|
||
main()
|