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; + }); } } });