- 在app.py中,将f-string替换为str.format()以提高兼容性。 - 在config.py中添加日志记录功能,记录加载和保存配置的详细信息。 - 更新build.py以确保在构建过程中正确处理文件路径和依赖项。 - 修改README.md以反映项目的最新功能和安装说明。 - 更新requirements.txt以调整依赖包版本,确保兼容性。master
a = Analysis( | a = Analysis( | ||||
['app.py'], | |||||
pathex=[], | |||||
['main.py'], | |||||
pathex=['D:\\2025Project\\MCP\\excel_converter'], | |||||
binaries=[], | binaries=[], | ||||
datas=[('employee_info.json', '.'), ('company_options.json', '.'), ('bank_options.json', '.'), ('template.xlsx', '.'), ('config.py', '.')], | |||||
datas=[('D:\\2025Project\\MCP\\excel_converter\\config.py', '.'), ('D:\\2025Project\\MCP\\excel_converter\\template.xlsx', '.')], | |||||
hiddenimports=[], | hiddenimports=[], | ||||
hookspath=[], | hookspath=[], | ||||
hooksconfig={}, | hooksconfig={}, |
# 工资明细转换工具 | |||||
# 日本工资明细转换工具 | |||||
这是一个用于批量转换Excel工资明细表的图形界面工具。 | |||||
## 功能简介 | |||||
## 功能特点 | |||||
此工具用于将日本工资明细表格数据转换为标准格式,支持批量处理多名员工的数据。 | |||||
1. 批量导入Excel文件并根据模板进行转换 | |||||
2. 自定义设置每个文件的公司、银行和其他信息 | |||||
3. 按照指定的单元格映射规则转换数据 | |||||
4. 自动生成包含员工姓名和日期的输出文件名 | |||||
5. 支持复制和保留原始Excel的格式和样式 | |||||
## 主要功能 | |||||
## 环境要求 | |||||
1. 导入Excel格式的工资明细表 | |||||
2. 维护员工信息(姓名、公司、银行账户等) | |||||
3. 自动转换并生成标准格式的工资明细表 | |||||
4. 批量处理多个员工的数据 | |||||
- Python 3.7 或更高版本 | |||||
- 依赖包:pandas, openpyxl, xlrd, xlwt | |||||
## 安装说明 | |||||
## 快速开始 | |||||
### 开发环境安装 | |||||
1. 确保已安装Python环境(3.7或更高版本) | |||||
2. 使用uv创建虚拟环境: | |||||
```bash | |||||
uv venv | |||||
1. 确保已安装Python 3.6+ | |||||
2. 安装依赖包: | |||||
``` | ``` | ||||
3. 激活虚拟环境: | |||||
```bash | |||||
# Windows | |||||
.venv\Scripts\activate | |||||
# macOS/Linux | |||||
source .venv/bin/activate | |||||
pip install -r requirements.txt | |||||
``` | ``` | ||||
4. 安装依赖包: | |||||
```bash | |||||
uv pip install -r requirements.txt | |||||
3. 运行程序: | |||||
``` | ``` | ||||
5. 运行程序: | |||||
```bash | |||||
# 方法1: 直接运行Python脚本 | |||||
python main.py | python main.py | ||||
# 方法2: 使用批处理文件 (Windows) | |||||
run.bat | |||||
``` | ``` | ||||
## 使用方法 | |||||
### 构建可执行文件 | |||||
1. 在界面上点击"选择Excel文件"按钮选择要转换的Excel文件 | |||||
2. 双击列表中的每个文件,设置公司信息(C2)、银行信息(B30)和其他信息(F2) | |||||
3. 点击"选择导出位置"按钮选择输出文件保存位置 | |||||
4. 点击"开始转换"按钮进行批量转换 | |||||
5. 转换完成后会显示成功转换的文件数量 | |||||
要构建独立的可执行文件,请运行: | |||||
## 文件命名规则 | |||||
输出文件名格式为:`YYYY年M月份給料明細書-姓名.xls` | |||||
- 年份(YYYY):从输入文件第二个Sheet页的B4单元格中提取 | |||||
- 月份(M):从输入文件第二个Sheet页的B4单元格中提取 | |||||
- 姓名:从输入文件第一个Sheet页的C3单元格中提取 | |||||
``` | |||||
python build.py | |||||
``` | |||||
## 配置指南 | |||||
构建完成后,可执行文件将位于`dist`目录中。 | |||||
### 修改模板路径 | |||||
在 `config.py` 文件中修改 `TEMPLATE_PATH` 变量。 | |||||
## 常见问题解决 | |||||
### 修改单元格映射关系 | |||||
在 `config.py` 文件中的 `CELL_MAPPINGS` 字典中定义映射关系: | |||||
```python | |||||
{ | |||||
(源文件sheet索引, 行, 列): (目标文件sheet索引, 行, 列) | |||||
} | |||||
``` | |||||
### 关于"信息维护无法保存"的问题 | |||||
### 修改公司和银行列表 | |||||
编辑 `config.py` 文件中的 `COMPANY_OPTIONS` 和 `BANK_OPTIONS` 列表即可添加或删除选项。 | |||||
如果使用打包后的程序遇到信息维护功能无法保存的问题,请按照以下步骤解决: | |||||
## 常见问题解决 | |||||
1. 确保运行程序时有足够的权限(管理员权限) | |||||
2. 检查程序所在目录是否有写入权限 | |||||
3. 手动创建一个名为`employee_info.json`的空文件: | |||||
- 在程序所在的文件夹中创建一个文本文件 | |||||
- 内容填写`[]`(一个空数组) | |||||
- 将文件重命名为`employee_info.json` | |||||
4. 重新启动程序,应该就可以正常保存员工信息了 | |||||
### 程序启动失败 | |||||
- 检查 Python 版本是否兼容 (3.7+) | |||||
- 确认所有依赖已正确安装 | |||||
- 检查模板文件是否存在于指定路径 | |||||
### 数据文件路径说明 | |||||
### 转换后的文件格式不正确 | |||||
- 确认模板文件格式无误 | |||||
- 检查输入文件是否符合要求的格式 | |||||
- 确认映射关系配置正确 | |||||
打包后的程序会在以下位置存储数据文件: | |||||
- 员工信息: 程序同一目录下的`employee_info.json` | |||||
- 模板文件: 程序同一目录下的`template.xlsx` | |||||
### 无法识别日期 | |||||
如果程序无法正确识别日期格式,可能需要在 `app.py` 的 `process_file` 方法中添加更多的日期解析逻辑。 | |||||
## 使用说明 | |||||
## 项目结构 | |||||
1. 启动程序 | |||||
2. 点击"选择Excel文件"导入原始工资明细表 | |||||
3. 点击"信息维护"添加或更新员工信息 | |||||
4. 选择导出位置 | |||||
5. 点击"开始转换"生成标准格式的工资明细表 | |||||
``` | |||||
excel_converter/ | |||||
├── app.py # 主应用程序代码 | |||||
├── config.py # 配置文件 | |||||
├── main.py # 入口点 | |||||
├── requirements.txt # 依赖列表 | |||||
├── run.bat # Windows 运行脚本 | |||||
├── README.md # 项目说明 | |||||
├── template.xlsx # Excel模板文件 | |||||
└── test_data/ # 测试数据目录 | |||||
``` | |||||
## 开发者说明 | |||||
## 维护和扩展 | |||||
本程序基于Python开发,使用以下库: | |||||
- tkinter: 用于GUI界面 | |||||
- openpyxl: 用于Excel文件处理 | |||||
- pandas: 用于数据处理 | |||||
- xlwings: 用于高级Excel操作 | |||||
### 添加新功能 | |||||
1. 尽量将配置参数放在 `config.py` 文件中 | |||||
2. 保持用户界面简洁直观 | |||||
3. 添加适当的错误处理和用户反馈 | |||||
## 联系方式 | |||||
### 代码维护 | |||||
- 定期更新依赖包版本 | |||||
- 如果更改了核心功能,请更新文档 | |||||
- 考虑添加单元测试以确保功能正常 | |||||
如有问题,请联系开发者。 |
# -*- coding: utf-8 -*- | |||||
""" | |||||
Build script for Excel Converter application | |||||
This script uses PyInstaller to build an executable from the Python code | |||||
""" | |||||
import os | import os | ||||
import subprocess | |||||
import sys | import sys | ||||
import subprocess | |||||
import shutil | |||||
def build_executable(): | |||||
# Ensure we're in the project root directory | |||||
os.chdir(os.path.dirname(os.path.abspath(__file__))) | |||||
def main(): | |||||
# Package name and path | |||||
package_name = "ExcelConverter" | |||||
main_script = "main.py" | |||||
icon_path = "icon.ico" | |||||
# Install required packages using uv | |||||
print("Installing required packages...") | |||||
subprocess.run(["uv", "pip", "install", "-r", "requirements.txt"], check=True) | |||||
# Make sure we're in the right directory | |||||
script_dir = os.path.dirname(os.path.abspath(__file__)) | |||||
os.chdir(script_dir) | |||||
# Build the executable using PyInstaller | |||||
print("Building executable...") | |||||
subprocess.run([ | |||||
# Create build command | |||||
cmd = [ | |||||
"pyinstaller", | "pyinstaller", | ||||
"--name=ExcelConverter", | |||||
"--name={}".format(package_name), | |||||
"--onefile", | "--onefile", | ||||
"--windowed", | "--windowed", | ||||
"--add-data=employee_info.json;.", | |||||
"--add-data=company_options.json;.", | |||||
"--add-data=bank_options.json;.", | |||||
"--add-data=template.xlsx;.", | |||||
"--add-data=config.py;.", | |||||
"app.py" | |||||
], check=True) | |||||
"--clean", | |||||
"--paths={}".format(script_dir), | |||||
"--add-data={}{}config.py;.".format(script_dir, os.path.sep), | |||||
"--add-data={}{}template.xlsx;.".format(script_dir, os.path.sep) | |||||
] | |||||
# Add icon if it exists | |||||
if os.path.exists(icon_path): | |||||
cmd.append("--icon={}".format(icon_path)) | |||||
# Add main script | |||||
cmd.append(main_script) | |||||
# Print command | |||||
print("Running command: {}".format(" ".join(cmd))) | |||||
# Run PyInstaller | |||||
try: | |||||
subprocess.check_call(cmd) | |||||
print("\nBuild completed successfully!") | |||||
# Copy necessary files to dist folder | |||||
dist_dir = os.path.join(script_dir, "dist", package_name) | |||||
if not os.path.exists(dist_dir): | |||||
dist_dir = os.path.join(script_dir, "dist") | |||||
# Copy template file to dist folder | |||||
if os.path.exists("template.xlsx"): | |||||
shutil.copy("template.xlsx", dist_dir) | |||||
print("Copied template.xlsx to {}".format(dist_dir)) | |||||
# Create empty config file if it doesn't exist | |||||
config_path = os.path.join(dist_dir, "employee_info.json") | |||||
if not os.path.exists(config_path): | |||||
with open(config_path, 'w', encoding='utf-8') as f: | |||||
f.write("[]") | |||||
print("Created empty employee_info.json in {}".format(dist_dir)) | |||||
print("\nBuild is ready in: {}".format(dist_dir)) | |||||
except subprocess.CalledProcessError as e: | |||||
print("Build failed with error:") | |||||
print(e) | |||||
return 1 | |||||
except Exception as e: | |||||
print("An error occurred:") | |||||
print(e) | |||||
return 1 | |||||
print("Build completed successfully!") | |||||
print("The executable can be found in the 'dist' directory.") | |||||
return 0 | |||||
if __name__ == "__main__": | if __name__ == "__main__": | ||||
build_executable() | |||||
sys.exit(main()) |
('_threading_local', | ('_threading_local', | ||||
'C:\\Users\\WIN\\AppData\\Roaming\\uv\\python\\cpython-3.12.3-windows-x86_64-none\\Lib\\_threading_local.py', | 'C:\\Users\\WIN\\AppData\\Roaming\\uv\\python\\cpython-3.12.3-windows-x86_64-none\\Lib\\_threading_local.py', | ||||
'PYMODULE'), | 'PYMODULE'), | ||||
('app', 'D:\\2025Project\\MCP\\excel_converter\\app.py', 'PYMODULE'), | |||||
('argparse', | ('argparse', | ||||
'C:\\Users\\WIN\\AppData\\Roaming\\uv\\python\\cpython-3.12.3-windows-x86_64-none\\Lib\\argparse.py', | 'C:\\Users\\WIN\\AppData\\Roaming\\uv\\python\\cpython-3.12.3-windows-x86_64-none\\Lib\\argparse.py', | ||||
'PYMODULE'), | 'PYMODULE'), |
IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for | IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for | ||||
tracking down the missing module yourself. Thanks! | tracking down the missing module yourself. Thanks! | ||||
missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional), setuptools._distutils.archive_util (optional), setuptools._vendor.backports.tarfile (optional) | |||||
missing module named pwd - imported by posixpath (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional), http.server (delayed, optional), netrc (delayed, conditional), getpass (delayed), setuptools._distutils.util (delayed, conditional, optional), setuptools._distutils.archive_util (optional), setuptools._vendor.backports.tarfile (optional) | missing module named pwd - imported by posixpath (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional), http.server (delayed, optional), netrc (delayed, conditional), getpass (delayed), setuptools._distutils.util (delayed, conditional, optional), setuptools._distutils.archive_util (optional), setuptools._vendor.backports.tarfile (optional) | ||||
missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional), setuptools._distutils.archive_util (optional), setuptools._vendor.backports.tarfile (optional) | |||||
missing module named posix - imported by os (conditional, optional), posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional) | missing module named posix - imported by os (conditional, optional), posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional) | ||||
missing module named resource - imported by posix (top-level) | missing module named resource - imported by posix (top-level) | ||||
missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional), zipimport (top-level) | missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional), zipimport (top-level) | ||||
missing module named trove_classifiers - imported by setuptools.config._validate_pyproject.formats (optional) | missing module named trove_classifiers - imported by setuptools.config._validate_pyproject.formats (optional) | ||||
missing module named pyimod02_importers - imported by D:\2025Project\MCP\excel_converter\.venv\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed), D:\2025Project\MCP\excel_converter\.venv\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgres.py (delayed) | missing module named pyimod02_importers - imported by D:\2025Project\MCP\excel_converter\.venv\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed), D:\2025Project\MCP\excel_converter\.venv\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgres.py (delayed) | ||||
missing module named collections.Mapping - imported by collections (optional), pytz.lazy (optional) | missing module named collections.Mapping - imported by collections (optional), pytz.lazy (optional) | ||||
missing module named plotly - imported by xlwings.utils (optional), xlwings.pro.reports.main (optional) | |||||
missing module named 'matplotlib.figure' - imported by pandas.plotting._misc (conditional), xlwings.utils (optional), xlwings.pro.reports.main (optional) | |||||
missing module named 'PIL.Image' - imported by xlwings.pro.reports.main (optional) | |||||
missing module named PIL - imported by openpyxl.drawing.image (optional), xlwings.pro.reports.main (optional), xlwings.main (optional), xlwings._xlwindows (optional), xlwings._xlmac (optional) | missing module named PIL - imported by openpyxl.drawing.image (optional), xlwings.pro.reports.main (optional), xlwings.main (optional), xlwings._xlwindows (optional), xlwings._xlmac (optional) | ||||
missing module named jinja2 - imported by xlwings.pro.reports.main (optional), pandas.io.formats.style (top-level) | |||||
missing module named cryptography - imported by xlwings.pro.utils (optional) | |||||
missing module named mistune - imported by xlwings.pro.reports.markdown (optional) | |||||
missing module named 'defusedxml.ElementTree' - imported by openpyxl.xml.functions (conditional) | |||||
missing module named 'lxml.etree' - imported by openpyxl.xml.functions (conditional), pandas.io.xml (delayed), pandas.io.formats.xml (delayed), pandas.io.html (delayed) | |||||
missing module named openpyxl.tests - imported by openpyxl.reader.excel (optional) | |||||
missing module named defusedxml - imported by openpyxl.xml (delayed, optional) | |||||
missing module named lxml - imported by openpyxl.xml (delayed, optional), pandas.io.xml (conditional) | |||||
missing module named _dummy_thread - imported by numpy._core.arrayprint (optional) | missing module named _dummy_thread - imported by numpy._core.arrayprint (optional) | ||||
missing module named 'numpy_distutils.cpuinfo' - imported by numpy.f2py.diagnose (delayed, conditional, optional) | missing module named 'numpy_distutils.cpuinfo' - imported by numpy.f2py.diagnose (delayed, conditional, optional) | ||||
missing module named 'numpy_distutils.fcompiler' - imported by numpy.f2py.diagnose (delayed, conditional, optional) | missing module named 'numpy_distutils.fcompiler' - imported by numpy.f2py.diagnose (delayed, conditional, optional) | ||||
missing module named numpy._core.add - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional) | missing module named numpy._core.add - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional) | ||||
missing module named yaml - imported by numpy.__config__ (delayed) | missing module named yaml - imported by numpy.__config__ (delayed) | ||||
missing module named numpy._distributor_init_local - imported by numpy (optional), numpy._distributor_init (optional) | missing module named numpy._distributor_init_local - imported by numpy (optional), numpy._distributor_init (optional) | ||||
missing module named plotly - imported by xlwings.utils (optional), xlwings.pro.reports.main (optional) | |||||
missing module named 'matplotlib.figure' - imported by pandas.plotting._misc (conditional), xlwings.utils (optional), xlwings.pro.reports.main (optional) | |||||
missing module named 'PIL.Image' - imported by xlwings.pro.reports.main (optional) | |||||
missing module named jinja2 - imported by xlwings.pro.reports.main (optional), pandas.io.formats.style (top-level) | |||||
missing module named cryptography - imported by xlwings.pro.utils (optional) | |||||
missing module named mistune - imported by xlwings.pro.reports.markdown (optional) | |||||
missing module named 'win32com.gen_py' - imported by win32com (conditional, optional) | missing module named 'win32com.gen_py' - imported by win32com (conditional, optional) | ||||
missing module named 'appscript.reference' - imported by xlwings._xlmac (top-level) | missing module named 'appscript.reference' - imported by xlwings._xlmac (top-level) | ||||
missing module named osax - imported by xlwings._xlmac (top-level) | missing module named osax - imported by xlwings._xlmac (top-level) | ||||
missing module named pdfrw - imported by xlwings.pro.reports.pdf (optional) | missing module named pdfrw - imported by xlwings.pro.reports.pdf (optional) | ||||
missing module named 'matplotlib.pyplot' - imported by pandas.io.formats.style (optional), xlwings.utils (optional) | missing module named 'matplotlib.pyplot' - imported by pandas.io.formats.style (optional), xlwings.utils (optional) | ||||
missing module named cStringIO - imported by xlrd.timemachine (conditional) | missing module named cStringIO - imported by xlrd.timemachine (conditional) | ||||
missing module named defusedxml - imported by openpyxl.xml (delayed, optional) | |||||
missing module named lxml - imported by openpyxl.xml (delayed, optional), pandas.io.xml (conditional) | |||||
missing module named 'defusedxml.ElementTree' - imported by openpyxl.xml.functions (conditional) | |||||
missing module named 'lxml.etree' - imported by openpyxl.xml.functions (conditional), pandas.io.xml (delayed), pandas.io.formats.xml (delayed), pandas.io.html (delayed) | |||||
missing module named openpyxl.tests - imported by openpyxl.reader.excel (optional) | |||||
missing module named six.moves.range - imported by six.moves (top-level), dateutil.rrule (top-level) | missing module named six.moves.range - imported by six.moves (top-level), dateutil.rrule (top-level) | ||||
runtime module named six.moves - imported by dateutil.tz.tz (top-level), dateutil.tz._factories (top-level), dateutil.tz.win (top-level), dateutil.rrule (top-level) | runtime module named six.moves - imported by dateutil.tz.tz (top-level), dateutil.tz._factories (top-level), dateutil.tz.win (top-level), dateutil.rrule (top-level) | ||||
missing module named dateutil.tz.tzfile - imported by dateutil.tz (top-level), dateutil.zoneinfo (top-level) | missing module named dateutil.tz.tzfile - imported by dateutil.tz (top-level), dateutil.zoneinfo (top-level) |
# -*- coding: utf-8 -*- | |||||
# Excel 转换工具配置文件 | # Excel 转换工具配置文件 | ||||
import os | import os | ||||
import json | import json | ||||
import sys | |||||
import logging | |||||
# 模板文件路径 | # 模板文件路径 | ||||
TEMPLATE_PATH = "template.xlsx" | TEMPLATE_PATH = "template.xlsx" | ||||
# 配置文件路径 | # 配置文件路径 | ||||
CONFIG_DIR = os.path.dirname(os.path.abspath(__file__)) | |||||
# 检测是否为打包环境 | |||||
if getattr(sys, 'frozen', False): | |||||
# PyInstaller打包后的环境 | |||||
CONFIG_DIR = os.path.dirname(sys.executable) | |||||
else: | |||||
# 开发环境 | |||||
CONFIG_DIR = os.path.dirname(os.path.abspath(__file__)) | |||||
EMPLOYEE_CONFIG_PATH = os.path.join(CONFIG_DIR, "employee_info.json") | EMPLOYEE_CONFIG_PATH = os.path.join(CONFIG_DIR, "employee_info.json") | ||||
COMPANY_CONFIG_PATH = os.path.join(CONFIG_DIR, "company_options.json") # 保留兼容性 | COMPANY_CONFIG_PATH = os.path.join(CONFIG_DIR, "company_options.json") # 保留兼容性 | ||||
BANK_CONFIG_PATH = os.path.join(CONFIG_DIR, "bank_options.json") # 保留兼容性 | BANK_CONFIG_PATH = os.path.join(CONFIG_DIR, "bank_options.json") # 保留兼容性 | ||||
# 设置日志记录 | |||||
logging.basicConfig( | |||||
level=logging.DEBUG, | |||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', | |||||
handlers=[ | |||||
logging.FileHandler(os.path.join(CONFIG_DIR, "config.log")), | |||||
logging.StreamHandler() | |||||
] | |||||
) | |||||
logger = logging.getLogger("config") | |||||
# 单元格映射关系(源文件位置 -> 目标文件位置) | # 单元格映射关系(源文件位置 -> 目标文件位置) | ||||
# 格式:{(源文件sheet索引, 行, 列): (目标文件sheet索引, 行, 列)} | # 格式:{(源文件sheet索引, 行, 列): (目标文件sheet索引, 行, 列)} | ||||
# 注意:源文件行索引为0表示从C3开始的行 | # 注意:源文件行索引为0表示从C3开始的行 | ||||
# 加载员工信息 | # 加载员工信息 | ||||
def load_employee_info(): | def load_employee_info(): | ||||
# 检查是否存在员工信息配置 | # 检查是否存在员工信息配置 | ||||
logger.debug("尝试从 {0} 加载员工信息".format(EMPLOYEE_CONFIG_PATH)) | |||||
if os.path.exists(EMPLOYEE_CONFIG_PATH): | if os.path.exists(EMPLOYEE_CONFIG_PATH): | ||||
try: | try: | ||||
with open(EMPLOYEE_CONFIG_PATH, 'r', encoding='utf-8') as f: | with open(EMPLOYEE_CONFIG_PATH, 'r', encoding='utf-8') as f: | ||||
employee_info = json.load(f) | employee_info = json.load(f) | ||||
logger.info("成功加载 {0} 条员工信息".format(len(employee_info))) | |||||
return employee_info | |||||
except Exception as e: | except Exception as e: | ||||
print(f"加载员工信息出错: {e}") | |||||
employee_info = DEFAULT_EMPLOYEE_INFO | |||||
logger.error("加载员工信息出错: {0}".format(str(e))) | |||||
return DEFAULT_EMPLOYEE_INFO | |||||
else: | else: | ||||
# 如果不存在,尝试从旧的配置中导入 | # 如果不存在,尝试从旧的配置中导入 | ||||
logger.warning("员工信息配置文件不存在,尝试从旧配置导入") | |||||
employee_info = [] | employee_info = [] | ||||
company_options = [] | company_options = [] | ||||
bank_options = [] | bank_options = [] | ||||
try: | try: | ||||
with open(COMPANY_CONFIG_PATH, 'r', encoding='utf-8') as f: | with open(COMPANY_CONFIG_PATH, 'r', encoding='utf-8') as f: | ||||
company_options = json.load(f) | company_options = json.load(f) | ||||
logger.info("从旧配置加载了 {0} 个公司选项".format(len(company_options))) | |||||
except Exception as e: | except Exception as e: | ||||
print(f"加载公司配置出错: {e}") | |||||
logger.error("加载公司配置出错: {0}".format(str(e))) | |||||
# 加载旧的银行配置 | # 加载旧的银行配置 | ||||
if os.path.exists(BANK_CONFIG_PATH): | if os.path.exists(BANK_CONFIG_PATH): | ||||
"account_holder": "" | "account_holder": "" | ||||
}) | }) | ||||
bank_options = new_bank_options | bank_options = new_bank_options | ||||
logger.info("从旧配置加载了 {0} 个银行选项".format(len(bank_options))) | |||||
except Exception as e: | except Exception as e: | ||||
print(f"加载银行配置出错: {e}") | |||||
logger.error("加载银行配置出错: {0}".format(str(e))) | |||||
# 合并旧配置 | # 合并旧配置 | ||||
if bank_options and isinstance(bank_options[0], dict): | if bank_options and isinstance(bank_options[0], dict): | ||||
# 保存新的员工信息 | # 保存新的员工信息 | ||||
save_options(EMPLOYEE_CONFIG_PATH, employee_info) | save_options(EMPLOYEE_CONFIG_PATH, employee_info) | ||||
return employee_info | |||||
logger.info("创建了新的员工信息配置,包含 {0} 条记录".format(len(employee_info))) | |||||
return employee_info | |||||
# 保存选项 | # 保存选项 | ||||
def save_options(config_path, options): | def save_options(config_path, options): | ||||
try: | try: | ||||
# 确保目录存在 | |||||
config_dir = os.path.dirname(config_path) | |||||
if not os.path.exists(config_dir): | |||||
os.makedirs(config_dir) | |||||
logger.info("创建目录: {0}".format(config_dir)) | |||||
# 保存前记录当前工作目录和文件路径 | |||||
logger.debug("当前工作目录: {0}".format(os.getcwd())) | |||||
logger.debug("保存配置到: {0}".format(config_path)) | |||||
# 保存配置 | |||||
with open(config_path, 'w', encoding='utf-8') as f: | with open(config_path, 'w', encoding='utf-8') as f: | ||||
json.dump(options, f, ensure_ascii=False, indent=4) | json.dump(options, f, ensure_ascii=False, indent=4) | ||||
return True | |||||
# 验证保存是否成功 | |||||
if os.path.exists(config_path): | |||||
logger.info("成功保存配置到 {0}".format(config_path)) | |||||
return True | |||||
else: | |||||
logger.error("保存失败,文件不存在: {0}".format(config_path)) | |||||
return False | |||||
except Exception as e: | except Exception as e: | ||||
print(f"保存配置出错: {e}") | |||||
logger.error("保存配置出错: {0}".format(str(e)), exc_info=True) | |||||
return False | return False | ||||
# 加载选项 | # 加载选项 | ||||
EMPLOYEE_INFO = load_employee_info() | |||||
COMPANY_OPTIONS = [info.get("company_name") for info in EMPLOYEE_INFO if info.get("company_name")] | |||||
COMPANY_OPTIONS = list(set(COMPANY_OPTIONS)) # 去重 | |||||
BANK_OPTIONS = [info for info in EMPLOYEE_INFO] # 兼容性:旧代码可能仍使用BANK_OPTIONS | |||||
try: | |||||
logger.info("开始加载配置...") | |||||
EMPLOYEE_INFO = load_employee_info() | |||||
COMPANY_OPTIONS = [info.get("company_name") for info in EMPLOYEE_INFO if info.get("company_name")] | |||||
COMPANY_OPTIONS = list(set(COMPANY_OPTIONS)) # 去重 | |||||
BANK_OPTIONS = [info for info in EMPLOYEE_INFO] # 兼容性:旧代码可能仍使用BANK_OPTIONS | |||||
logger.info("加载了 {0} 条员工信息, {1} 个公司选项".format(len(EMPLOYEE_INFO), len(COMPANY_OPTIONS))) | |||||
except Exception as e: | |||||
logger.critical("配置加载失败: {0}".format(str(e)), exc_info=True) | |||||
EMPLOYEE_INFO = DEFAULT_EMPLOYEE_INFO | |||||
COMPANY_OPTIONS = [] | |||||
BANK_OPTIONS = [] |
"account_holder": "" | "account_holder": "" | ||||
}, | }, | ||||
{ | { | ||||
"employee_name": "西本智子", | |||||
"employee_name": "西本智子1", | |||||
"company_name": "SPD株式会社", | "company_name": "SPD株式会社", | ||||
"bank_name": "", | |||||
"branch_account": "", | |||||
"bank_name": "11", | |||||
"branch_account": "11", | |||||
"account_holder": "" | "account_holder": "" | ||||
} | } | ||||
] | ] |
# -*- coding: utf-8 -*- | |||||
import os | import os | ||||
import sys | import sys | ||||
import tkinter as tk | import tkinter as tk | ||||
screen_height = root.winfo_screenheight() | screen_height = root.winfo_screenheight() | ||||
x = (screen_width - window_width) // 2 | x = (screen_width - window_width) // 2 | ||||
y = (screen_height - window_height) // 2 | y = (screen_height - window_height) // 2 | ||||
root.geometry(f"{window_width}x{window_height}+{x}+{y}") | |||||
root.geometry("{0}x{1}+{2}+{3}".format(window_width, window_height, x, y)) | |||||
# Run the application | # Run the application | ||||
root.mainloop() | root.mainloop() |
pandas>=2.0.0 | |||||
openpyxl>=3.1.0 | |||||
xlrd>=2.0.0 | |||||
xlwings>=0.30.0 | |||||
pyinstaller>=6.0.0 | |||||
openpyxl>=3.0.0 | |||||
pandas>=1.0.0 | |||||
xlrd>=1.0.0 | |||||
xlwings>=0.20.0 | |||||
pyinstaller>=4.0.0 |