数据 (定位销/勾销缸/翻转缸/电缸 → 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>
86 lines
2.8 KiB
Python
86 lines
2.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
为网页发布目录生成 STEP 压缩副档 (.step.gz),供浏览器端按需解压下载。
|
|
|
|
背景: 网页版购物车打包是逐个 fetch 原始 STEP (翻转缸单个 7-13MB), 选 20 个型号
|
|
要传 40MB+。预生成 gz 副档后前端优先取 .gz 并在浏览器解压 (DecompressionStream),
|
|
传输量降至约 1/4.5。
|
|
|
|
用法:
|
|
PYTHONUTF8=1 python gen-step-gz.py <web目录> # 全量生成(跳过已存在)
|
|
PYTHONUTF8=1 python gen-step-gz.py <web目录> --force # 强制重建
|
|
PYTHONUTF8=1 python gen-step-gz.py <web目录> --clean # 删除已有 .gz 后重建
|
|
"""
|
|
import os, sys, gzip, shutil, time
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
|
args = [a for a in sys.argv[1:] if not a.startswith('--')]
|
|
FORCE = '--force' in sys.argv
|
|
CLEAN = '--clean' in sys.argv
|
|
if not args:
|
|
sys.exit('用法: python gen-step-gz.py <web目录> [--force|--clean]')
|
|
WEB = args[0]
|
|
STEP = os.path.join(WEB, 'step')
|
|
if not os.path.isdir(STEP):
|
|
sys.exit('找不到 ' + STEP)
|
|
|
|
if CLEAN:
|
|
n = 0
|
|
for f in os.listdir(STEP):
|
|
if f.lower().endswith('.gz'):
|
|
os.remove(os.path.join(STEP, f))
|
|
n += 1
|
|
print(f'已清理 {n} 个 .gz')
|
|
|
|
files = [f for f in os.listdir(STEP)
|
|
if f.lower().endswith(('.step', '.stp'))]
|
|
todo = []
|
|
for f in files:
|
|
src = os.path.join(STEP, f)
|
|
dst = src + '.gz'
|
|
if FORCE or not os.path.exists(dst):
|
|
todo.append((src, dst, f))
|
|
print(f'step 文件 {len(files)} 个, 待压缩 {len(todo)} 个')
|
|
|
|
|
|
def gz_one(item):
|
|
src, dst, name = item
|
|
tmp = dst + '.part'
|
|
try:
|
|
with open(src, 'rb') as fi, gzip.open(tmp, 'wb', compresslevel=6) as fo:
|
|
shutil.copyfileobj(fi, fo, 1024 * 1024)
|
|
os.replace(tmp, dst)
|
|
return (name, os.path.getsize(src), os.path.getsize(dst), None)
|
|
except Exception as e:
|
|
try:
|
|
os.remove(tmp)
|
|
except OSError:
|
|
pass
|
|
return (name, 0, 0, str(e))
|
|
|
|
|
|
t0 = time.time()
|
|
raw = comp = done = err = 0
|
|
errors = []
|
|
with ThreadPoolExecutor(max_workers=4) as ex:
|
|
futs = [ex.submit(gz_one, it) for it in todo]
|
|
for i, fut in enumerate(as_completed(futs), 1):
|
|
name, r, c, e = fut.result()
|
|
if e:
|
|
err += 1
|
|
errors.append(f'{name}: {e}')
|
|
else:
|
|
done += 1
|
|
raw += r
|
|
comp += c
|
|
if i % 200 == 0:
|
|
print(f' {i}/{len(todo)} ({time.time()-t0:.0f}s)')
|
|
|
|
print(f'\n完成: {done} 个, 失败 {err} 个, 用时 {time.time()-t0:.0f}s')
|
|
if raw:
|
|
print(f' 原始 {raw/1073741824:.2f} GB -> gz {comp/1073741824:.2f} GB '
|
|
f'(省 {(1-comp/raw)*100:.1f}%)')
|
|
for e in errors[:10]:
|
|
print(' !', e)
|
|
print(f'step 目录现有 .gz {sum(1 for f in os.listdir(STEP) if f.endswith(".gz"))} 个')
|