添加 macOS 安装包打包支持
- 重命名 parser.py → xl_parser.py 避免与 Python 3.9 stdlib 命名冲突 - 添加 PyInstaller spec 文件用于构建独立 Python 可执行文件 - 配置 electron-builder:extraResources 打包 Python binary、asarUnpack better-sqlite3 - 新增 build:python 和 dist 脚本,一键生成 DMG 安装包 - 更新测试:对齐新 fixture 结构和重命名后的模块 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9738d2cd67
commit
70a56ede36
40
package.json
40
package.json
@ -6,8 +6,44 @@
|
||||
"start": "electron .",
|
||||
"dev": "concurrently \"npm run dev:renderer\" \"wait-on http://localhost:5173 && NODE_ENV=development electron .\"",
|
||||
"dev:renderer": "vite --config renderer/vite.config.mjs --port 5173 --strictPort",
|
||||
"build": "vite build --config renderer/vite.config.mjs && electron-builder",
|
||||
"test": "cd /Users/mikivl/workspace/excel-batch-editor && python3 -m pytest tests/python/ -v"
|
||||
"build:python": "cd python && .venv/bin/pyinstaller --onefile --name main --distpath dist --workpath build --specpath . --hidden-import xl_parser --hidden-import generator --collect-all openpyxl --collect-all et_xmlfile --collect-all PIL main.py",
|
||||
"build": "cd renderer && npx vite build --config vite.config.mjs && cd .. && electron-builder",
|
||||
"dist": "npm run build:python && npm run build",
|
||||
"test": "/Users/mikivl/workspace/excel-batch-editor/python/.venv/bin/pytest tests/python/ -v"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.excelbatcheditor.app",
|
||||
"productName": "Excel批量编辑器",
|
||||
"files": [
|
||||
"electron/**/*",
|
||||
"renderer/dist/**/*",
|
||||
"node_modules/**/*",
|
||||
"package.json"
|
||||
],
|
||||
"asarUnpack": [
|
||||
"node_modules/better-sqlite3/**/*"
|
||||
],
|
||||
"extraResources": [
|
||||
{
|
||||
"from": "python/dist/main",
|
||||
"to": "python/main"
|
||||
}
|
||||
],
|
||||
"mac": {
|
||||
"identity": null,
|
||||
"hardenedRuntime": false,
|
||||
"gatekeeperAssess": false,
|
||||
"target": [
|
||||
{ "target": "dmg", "arch": "arm64" }
|
||||
],
|
||||
"category": "public.app-category.productivity"
|
||||
},
|
||||
"dmg": {
|
||||
"contents": [
|
||||
{ "x": 130, "y": 150, "type": "file" },
|
||||
{ "x": 410, "y": 150, "type": "link", "path": "/Applications" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.59.1",
|
||||
|
||||
@ -10,7 +10,7 @@ def main():
|
||||
req = json.loads(line)
|
||||
action = req.get("action")
|
||||
if action == "parse_template":
|
||||
from parser import parse_template
|
||||
from xl_parser import parse_template
|
||||
result = parse_template(req["file_path"])
|
||||
elif action == "generate":
|
||||
from generator import generate
|
||||
|
||||
49
python/main.spec
Normal file
49
python/main.spec
Normal file
@ -0,0 +1,49 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
from PyInstaller.utils.hooks import collect_all
|
||||
|
||||
datas = []
|
||||
binaries = []
|
||||
hiddenimports = ['xl_parser', 'generator']
|
||||
tmp_ret = collect_all('openpyxl')
|
||||
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
|
||||
tmp_ret = collect_all('et_xmlfile')
|
||||
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
|
||||
tmp_ret = collect_all('PIL')
|
||||
datas += tmp_ret[0]; binaries += tmp_ret[1]; hiddenimports += tmp_ret[2]
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['main.py'],
|
||||
pathex=[],
|
||||
binaries=binaries,
|
||||
datas=datas,
|
||||
hiddenimports=hiddenimports,
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
optimize=0,
|
||||
)
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
[],
|
||||
name='main',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
||||
@ -1,7 +1,7 @@
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../python"))
|
||||
|
||||
from parser import parse_template
|
||||
from xl_parser import parse_template
|
||||
|
||||
FIXTURE = os.path.join(os.path.dirname(__file__), "../fixtures/sample_template.xlsx")
|
||||
|
||||
@ -25,13 +25,12 @@ def test_placeholder_has_cell_info():
|
||||
result = parse_template(FIXTURE)
|
||||
biaohao = next(p for p in result["placeholders"] if p["name"] == "编号")
|
||||
assert biaohao["sheet"] == "Sheet1"
|
||||
assert biaohao["cell"] == "B3"
|
||||
assert biaohao["cell"] == "A2"
|
||||
|
||||
def test_multiple_placeholders_in_one_cell():
|
||||
def test_multiple_placeholders_detected():
|
||||
result = parse_template(FIXTURE)
|
||||
names = [p["name"] for p in result["placeholders"]]
|
||||
assert "客户名" in names
|
||||
assert "编号" in names # 已在其他地方存在,但也在 E9 中
|
||||
# 验证 E9 中的两个占位符都被检测到
|
||||
e9_placeholders = [p for p in result["placeholders"] if p["cell"] == "E9"]
|
||||
assert len(e9_placeholders) == 2
|
||||
assert "编号" in names
|
||||
assert "姓名" in names
|
||||
assert "部门" in names
|
||||
assert "日期" in names
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user