数据 (定位销/勾销缸/翻转缸/电缸 → 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>
117 lines
4.5 KiB
Python
117 lines
4.5 KiB
Python
# -*- 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()
|