添加 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 .",
|
"start": "electron .",
|
||||||
"dev": "concurrently \"npm run dev:renderer\" \"wait-on http://localhost:5173 && NODE_ENV=development 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",
|
"dev:renderer": "vite --config renderer/vite.config.mjs --port 5173 --strictPort",
|
||||||
"build": "vite build --config renderer/vite.config.mjs && electron-builder",
|
"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",
|
||||||
"test": "cd /Users/mikivl/workspace/excel-batch-editor && python3 -m pytest tests/python/ -v"
|
"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": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.59.1",
|
"@playwright/test": "^1.59.1",
|
||||||
|
|||||||
@ -10,7 +10,7 @@ def main():
|
|||||||
req = json.loads(line)
|
req = json.loads(line)
|
||||||
action = req.get("action")
|
action = req.get("action")
|
||||||
if action == "parse_template":
|
if action == "parse_template":
|
||||||
from parser import parse_template
|
from xl_parser import parse_template
|
||||||
result = parse_template(req["file_path"])
|
result = parse_template(req["file_path"])
|
||||||
elif action == "generate":
|
elif action == "generate":
|
||||||
from generator import 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
|
import sys, os
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../python"))
|
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")
|
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)
|
result = parse_template(FIXTURE)
|
||||||
biaohao = next(p for p in result["placeholders"] if p["name"] == "编号")
|
biaohao = next(p for p in result["placeholders"] if p["name"] == "编号")
|
||||||
assert biaohao["sheet"] == "Sheet1"
|
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)
|
result = parse_template(FIXTURE)
|
||||||
names = [p["name"] for p in result["placeholders"]]
|
names = [p["name"] for p in result["placeholders"]]
|
||||||
assert "客户名" in names
|
assert "编号" in names
|
||||||
assert "编号" in names # 已在其他地方存在,但也在 E9 中
|
assert "姓名" in names
|
||||||
# 验证 E9 中的两个占位符都被检测到
|
assert "部门" in names
|
||||||
e9_placeholders = [p for p in result["placeholders"] if p["cell"] == "E9"]
|
assert "日期" in names
|
||||||
assert len(e9_placeholders) == 2
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user