v2.0: 4 分类真实数据替换 + 手册彩色重做 + 网页版改版

数据 (定位销/勾销缸/翻转缸/电缸 → Univer 真实数模)
- 新增 19 系列 1124 型号 (PIN 303 / HOK 73 / ROT 741 / ELEC 9)
- 命名规则取自官方图册 CODIFICATION KEY (pdftotext 提取), 非推测
- 解析用「官方值域校验 + 左对齐 DFS 填空」, 可处理省略空位的型号
- 参数组合唯一性门禁: 1124 型号全部通过 (避免选型静默错配)
- 占位/测试文件 (如 USP50J_0___K0 销径为空) 在解析阶段丢弃
- catalog.json 分类系列表同步; crossref 失效引用清理

手册页
- 全部 42 系列改为彩色 (此前脚本误写死 csGRAY 转灰度), 346 页
- 修复 UCBT 附图只有 1 页 (筛选条件写死 UCBT40 且漏 CLAMPING ARMS)
- gen-pending-series.py 改为扫描 manual 目录自动生成附件清单,
  避免重跑脚本把多页附件覆盖回单页

网页版
- 首页改为系列缩略图网格 (40 张: 27 张真实产品图 + 13 张占位图),
  按大类分组; 图放 web/series/<系列码>.webp, 覆盖同名文件即可替换
- 左侧树改手风琴 (默认收起, 一次展开一个), 父级带 SVG 图标,
  悬停放大动画, 按钮间距随高度均匀分布; 可收起为图标窄条
- 修复分隔条被拖出容器后无法找回 (上限改为容器感知 + resize 重夹取
  + 双击复位); 同时阻止图片原生拖拽
- 购物车打包改取 .step.gz 副档 (传输量降至约 1/4.5), 无副档自动回退
- 修复搜索到型号后点结果内容区空白 (switchTab('dim') 已失效)
- OpcPackage 超大包 (>1.5GB) 改走临时文件, 绕过 MemoryStream 2GB 上限

工具
- gen-pending-series/manual, copy-pending-step, gen-step-gz,
  gen-series-thumbs, scan-pending-series 等 11 个脚本

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
wangruiguo
2026-09-15 01:05:17 +08:00
parent 0584ba67b4
commit 3c4f236cba
483 changed files with 6520 additions and 201 deletions

View File

@@ -0,0 +1,116 @@
# -*- coding: utf-8 -*-
"""
为待补分类(定位销/勾销缸/翻转缸/电缸)生成附图。
来源: Univer 官方图册 PDF(X盘 官方图册 / 选型表/spanner)。
输出: manual/<系列>/p<NN>.png + <系列>_manual.pdf
用法: PYTHONUTF8=1 python gen-pending-manual.py [--clean]
"""
import fitz, os, shutil, sys
SRC = r'X:/新开发产品-Research and Development/2_产品资料/Univer资料/官方图册'
SPANNER = r'X:/新开发产品-Research and Development/2_产品资料/Univer资料/选型表/spanner'
DST = r'D:/开发/OnebotCatalog/onebot-data/catalog/manual'
DPI = 110
CLEAN = '--clean' in sys.argv
# 系列 -> (目录, [pdf 名]);pdf 名不含扩展名
SPECS = {
# ---- 定位销 ----
'ULA': (SRC, ['ula40cten_en419', 'ula63cten_en407']),
'ULB': (SRC, ['ulb32cten_en423']),
'ULL': (SRC, ['ull50cten_en411']),
'ULM': (SRC, ['ulm50cten_en410']),
'ULO': (SRC, ['ulo50cten_en413']),
'ULP': (SRC, ['ulp32cten_en404', 'ulp50cten_en405', 'ulp63cten_en406']),
'ULR': (SRC, ['ulr50nken_en476', 'ulr63cten_en477']),
'ULT': (SRC, ['ult50cten_en408']),
'ULX': (SRC, ['ulx50cten_en425']),
'ULY': (SRC, ['uly40cten_en426']),
'ULZ': (SRC, ['ulz40cten_en428']),
# ---- 勾销缸 ----
'USP': (SRC, ['usp32cten_en355', 'usp50cten_en437', 'usp50ucten_en168']),
'USL': (SRC, ['usl50cten_en171']),
'USR': (SRC, ['usr40cten_en436']),
'USS': (SRC, ['uss50cten_en433', 'uss63cten_en434']),
'USSL': (SRC, ['ussl50cten_en358']),
# ---- 翻转缸 ----
'UAGP': (SRC, ['uagp075wcten_en580', 'uagp075bcten_en181', 'uagp080wcten_en174',
'uagp080wcten_en573', 'uagp120bcten_en175', 'uagp120wcten_en574',
'uagp150bcten_en178', 'uagp150wcten_en577', 'uagp155wcten_en583',
'uagp170bnken_en176', 'uagp170wcten_en575', 'uagp210bcten_en182',
'uagp210wcten_en581', 'uagp215bcten_en373', 'uagp215wcten_en584',
'uagp300bcten_en177', 'uagp300wcten_en576', 'uagp350bcten_en183',
'uagp350wcten_en582', 'uagp355bcten_en374', 'uagp355wcten_en585',
'uagp600bcten_en179', 'uagp600wcten_en578', 'uagp605bcten_en180',
'uagp605wcten_en579']),
# LAGP 官方已改名 UAGP,图册在选型表/spanner(老版)
'LAGP': (SPANNER, ['lagp075_en_c181', 'lagp120_en_c175', 'lagp150_en_c178',
'lagp155_en_c350', 'lagp170_en_c176', 'lagp210_en_c182',
'lagp215nwrev-00-0119en_en373', 'lagp300_en_c177']),
# ---- 电缸 ----
'LAE': (SRC, ['lae140_en_c220', 'lae225_en_c221', 'lae350_en_c222']),
}
def classify(page):
t = page.get_text().upper()
if 'ACCESSORIES' in t or 'CLAMPING ARMS' in t or 'BLADE ARMS' in t:
return 'arm'
if 'DIMENSIONS' in t:
return 'dim'
if 'CHARACTERISTICS' in t or 'CODIFICATION' in t:
return 'char'
return 'other'
def main():
os.makedirs(DST, exist_ok=True)
missing = []
for series, (srcdir, pdfs) in SPECS.items():
d = os.path.join(DST, series)
if CLEAN and os.path.isdir(d):
shutil.rmtree(d)
os.makedirs(d, exist_ok=True)
docs, all_pages = [], []
for name in pdfs:
p = os.path.join(srcdir, name + '.pdf')
if not os.path.exists(p):
missing.append(f'{series}: {name}')
continue
doc = fitz.open(p)
docs.append((name, doc))
for i in range(doc.page_count):
all_pages.append((doc, i, classify(doc[i])))
if not all_pages:
print(f'[{series}] 无可用源 PDF')
continue
order = {'char': 0, 'dim': 1, 'arm': 2, 'other': 3}
all_pages.sort(key=lambda x: order[x[2]])
n = 1
for doc, i, _k in all_pages:
# 彩色输出 (官方图册本身是彩色的; 早期版本误用 csGRAY 转灰度,
# 体积约翻倍但相对整个数据包可忽略)
doc[i].get_pixmap(dpi=DPI).save(
os.path.join(d, f'p{n:02d}.png'))
n += 1
out_pdf = fitz.open()
for _name, doc in docs:
out_pdf.insert_pdf(doc)
outp = os.path.join(d, series + '_manual.pdf')
out_pdf.save(outp, garbage=3, deflate=True)
out_pdf.close()
print(f'[{series}] {len(all_pages)} 页 / {len(pdfs)} PDF -> '
f'{os.path.getsize(outp)/1024:.0f}KB')
if missing:
print('\n源缺失:')
for m in missing:
print(' ', m)
print('完成')
if __name__ == '__main__':
main()