diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..895db04
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,28 @@
+__pycache__/
+*.pyc
+*.pyo
+*.pyd
+.Python
+env/
+venv/
+.env
+.venv
+pip-log.txt
+pip-delete-this-directory.txt
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.log
+.git
+.gitignore
+.vscode/
+.idea/
+*.sqlite3
+erp_data.db
+output/
+browser_login/output/
+terminals/
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..bbd81e8
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,41 @@
+# 使用官方 Python 基础镜像 (Debian 体系)
+FROM python:3.9-slim
+
+# 设置工作目录
+WORKDIR /app
+
+# 防止 apt-get 安装时出现交互式弹窗(比如选择时区)导致卡死
+ENV DEBIAN_FRONTEND=noninteractive
+
+# Debian 12 (Bookworm) 的 apt 源文件变成了 /etc/apt/sources.list.d/debian.sources
+# 这里替换源以加速下载,并安装必要的系统依赖
+# 必须安装:Xvfb(虚拟屏幕), Chromium(浏览器核心), 中文字体(防乱码)
+RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list.d/debian.sources 2>/dev/null || \
+ sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list 2>/dev/null || true && \
+ apt-get update && \
+ apt-get install -y --no-install-recommends \
+ xvfb \
+ chromium \
+ chromium-driver \
+ fonts-wqy-zenhei \
+ tzdata \
+ && rm -rf /var/lib/apt/lists/*
+
+# 设置时区为中国上海
+ENV TZ=Asia/Shanghai
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
+
+# 复制依赖清单并安装 Python 库
+COPY requirements.txt .
+RUN pip install --no-cache-dir -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ && \
+ pip install gunicorn -i https://mirrors.aliyun.com/pypi/simple/
+
+# 复制整个项目到容器内
+COPY . .
+
+# 暴露 Flask 服务的 5050 端口
+EXPOSE 5050
+
+# 启动脚本:使用 xvfb-run 虚拟出一个屏幕来运行 Python 程序
+# --server-args="-screen 0 1920x1080x24" 设置虚拟屏幕的分辨率
+CMD ["xvfb-run", "--server-args=-screen 0 1920x1080x24", "gunicorn", "-w", "4", "-b", "0.0.0.0:5050", "--timeout", "120", "web_ui.app:app"]
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..228c6ec
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,85 @@
+aiofiles==24.1.0
+aiohappyeyeballs==2.6.1
+aiohttp==3.13.2
+aiosignal==1.4.0
+alibabacloud-credentials==1.0.3
+alibabacloud-credentials-api==1.0.0
+alibabacloud-endpoint-util==0.0.4
+alibabacloud-gateway-spi==0.0.3
+alibabacloud-ice20201109==6.8.4
+alibabacloud-imm20170906==1.23.8
+alibabacloud-openapi-util==0.2.2
+alibabacloud-tea==0.4.3
+alibabacloud-tea-openapi==0.4.2
+alibabacloud-tea-util==0.3.14
+altgraph==0.17.2
+anyio==4.12.1
+APScheduler==3.11.1
+async-timeout==5.0.1
+attrs==25.4.0
+blinker==1.9.0
+certifi==2025.11.12
+cffi==2.0.0
+charset-normalizer==3.4.4
+click==8.1.8
+cryptography==44.0.3
+cssselect==1.3.0
+darabonba-core==1.0.4
+DrissionGet==1.2.1
+DrissionPage==4.1.1.4
+DrissionRecord==2.0.1
+et_xmlfile==2.0.0
+exceptiongroup==1.3.1
+Faker==37.12.0
+filelock==3.19.1
+Flask==3.1.3
+frozenlist==1.8.0
+future==0.18.2
+greenlet==3.2.5
+h11==0.16.0
+httpcore==1.0.9
+httpx==0.28.1
+idna==3.11
+importlib_metadata==8.7.1
+iniconfig==2.1.0
+itsdangerous==2.2.0
+Jinja2==3.1.6
+lark-oapi==1.5.5
+lxml==6.1.1
+macholib==1.15.2
+MarkupSafe==3.0.3
+multidict==6.7.0
+mysql-connector-python==9.4.0
+numpy==2.0.2
+openpyxl==3.1.5
+packaging==25.0
+pandas==2.3.3
+playwright==1.59.0
+pluggy==1.6.0
+propcache==0.4.1
+psutil==7.2.2
+pycparser==2.23
+pycryptodome==3.23.0
+pyee==13.0.1
+Pygments==2.19.2
+PyMySQL==1.1.2
+pytest==8.4.2
+python-dateutil==2.9.0.post0
+python-dotenv==1.2.1
+pytz==2025.2
+requests==2.32.5
+requests-file==3.0.1
+requests-toolbelt==1.0.0
+six==1.15.0
+sqlparse==0.5.5
+tldextract==5.3.0
+tomli==2.3.0
+typing_extensions==4.15.0
+tzdata==2025.3
+tzlocal==5.3.1
+urllib3==2.5.0
+websocket-client==1.9.0
+websockets==15.0.1
+Werkzeug==3.1.8
+yarl==1.22.0
+zipp==3.23.1
diff --git a/web_ui/templates/home.html b/web_ui/templates/home.html
index 0df49d9..97cea02 100644
--- a/web_ui/templates/home.html
+++ b/web_ui/templates/home.html
@@ -242,6 +242,15 @@
round>
+
+
+
+
@@ -255,6 +264,7 @@
syncing: false,
syncingBom: false,
syncingWorkOrders: false,
+ syncingIssueReceipts: false,
isSystemBusy: false,
globalTaskName: "",
statusTimer: null
@@ -364,6 +374,31 @@
.finally(() => {
this.syncingWorkOrders = false;
});
+ },
+ syncIssueReceipts() {
+ this.syncingIssueReceipts = true;
+ if (window.globalLogApp) {
+ window.globalLogApp.logDialogVisible = true;
+ }
+ axios.post('/api/sync_issue_receipts')
+ .then(res => {
+ if (res.data.success) {
+ this.$message.success('已触发!' + res.data.message);
+ setTimeout(this.checkTaskStatus, 500);
+ } else {
+ this.$message.error('触发失败:' + res.data.message);
+ }
+ })
+ .catch(err => {
+ if (err.response && err.response.status === 409) {
+ this.$message.warning(err.response.data.message);
+ } else {
+ this.$message.error('请求发生异常,请检查后端日志。');
+ }
+ })
+ .finally(() => {
+ this.syncingIssueReceipts = false;
+ });
}
}
});