new file: app/assets/_plugin-vue_export-helper-Dh4yMZWw.js
new file: app/assets/_plugin-vue_export-helper-xvHHTGU_.css new file: app/assets/convert-1dkFPbKa.css new file: app/assets/convert-BxkpMHO9.css new file: app/assets/convert-DwbQug2y.css modified: app/assets/convert.js new file: app/assets/dedupe-B0eqpVpj.css new file: app/assets/dedupe-DzFvTfoj.css new file: app/assets/dedupe-rhGu1E8E.css modified: app/assets/dedupe.js new file: app/assets/el-alert-Ct0RpqUD.css new file: app/assets/pywebview-B6ZNXusn.css new file: app/assets/pywebview-BnpJyyGZ.js new file: app/assets/pywebview-CGwF8TuM.js new file: app/assets/pywebview-D-PKWjUg.js new file: app/assets/pywebview-IqgMfeBe.css new file: app/assets/split-DAa_Usoh.css new file: app/assets/split-DidKJ84l.css new file: app/assets/split-R_8V5xgh.css modified: app/assets/split.js modified: app/blueprints/__pycache__/main.cpython-39.pyc modified: app/blueprints/main.py modified: app/main.py modified: app/new_web_source/convert.html modified: app/new_web_source/dedupe.html modified: app/new_web_source/split.html
This commit is contained in:
55
app/assets/_plugin-vue_export-helper-Dh4yMZWw.js
Normal file
55
app/assets/_plugin-vue_export-helper-Dh4yMZWw.js
Normal file
File diff suppressed because one or more lines are too long
1
app/assets/_plugin-vue_export-helper-xvHHTGU_.css
Normal file
1
app/assets/_plugin-vue_export-helper-xvHHTGU_.css
Normal file
File diff suppressed because one or more lines are too long
1
app/assets/convert-1dkFPbKa.css
Normal file
1
app/assets/convert-1dkFPbKa.css
Normal file
File diff suppressed because one or more lines are too long
1
app/assets/convert-BxkpMHO9.css
Normal file
1
app/assets/convert-BxkpMHO9.css
Normal file
File diff suppressed because one or more lines are too long
1
app/assets/convert-DwbQug2y.css
Normal file
1
app/assets/convert-DwbQug2y.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
app/assets/dedupe-B0eqpVpj.css
Normal file
1
app/assets/dedupe-B0eqpVpj.css
Normal file
File diff suppressed because one or more lines are too long
1
app/assets/dedupe-DzFvTfoj.css
Normal file
1
app/assets/dedupe-DzFvTfoj.css
Normal file
File diff suppressed because one or more lines are too long
1
app/assets/dedupe-rhGu1E8E.css
Normal file
1
app/assets/dedupe-rhGu1E8E.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
app/assets/el-alert-Ct0RpqUD.css
Normal file
1
app/assets/el-alert-Ct0RpqUD.css
Normal file
File diff suppressed because one or more lines are too long
1
app/assets/pywebview-B6ZNXusn.css
Normal file
1
app/assets/pywebview-B6ZNXusn.css
Normal file
File diff suppressed because one or more lines are too long
55
app/assets/pywebview-BnpJyyGZ.js
Normal file
55
app/assets/pywebview-BnpJyyGZ.js
Normal file
File diff suppressed because one or more lines are too long
55
app/assets/pywebview-CGwF8TuM.js
Normal file
55
app/assets/pywebview-CGwF8TuM.js
Normal file
File diff suppressed because one or more lines are too long
55
app/assets/pywebview-D-PKWjUg.js
Normal file
55
app/assets/pywebview-D-PKWjUg.js
Normal file
File diff suppressed because one or more lines are too long
1
app/assets/pywebview-IqgMfeBe.css
Normal file
1
app/assets/pywebview-IqgMfeBe.css
Normal file
File diff suppressed because one or more lines are too long
1
app/assets/split-DAa_Usoh.css
Normal file
1
app/assets/split-DAa_Usoh.css
Normal file
File diff suppressed because one or more lines are too long
1
app/assets/split-DidKJ84l.css
Normal file
1
app/assets/split-DidKJ84l.css
Normal file
File diff suppressed because one or more lines are too long
1
app/assets/split-R_8V5xgh.css
Normal file
1
app/assets/split-R_8V5xgh.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -2,8 +2,7 @@
|
|||||||
主页面蓝图:首页、home、图片工作台、品牌页、静态文件、Logo
|
主页面蓝图:首页、home、图片工作台、品牌页、静态文件、Logo
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import requests
|
from flask import Blueprint, send_file
|
||||||
from flask import Blueprint, Response, request, send_file
|
|
||||||
|
|
||||||
from app_common import (
|
from app_common import (
|
||||||
get_db,
|
get_db,
|
||||||
@@ -16,10 +15,10 @@ from app_common import (
|
|||||||
ASSETS_DIR
|
ASSETS_DIR
|
||||||
)
|
)
|
||||||
from flask import redirect, url_for, session
|
from flask import redirect, url_for, session
|
||||||
|
from flask import Flask, request, Response, stream_with_context
|
||||||
|
import requests
|
||||||
|
|
||||||
from config import base_url,version,java_api_base
|
from config import base_url,version,JAVA_API_BASE
|
||||||
|
|
||||||
JAVA_API_BASE = java_api_base
|
|
||||||
|
|
||||||
main_bp = Blueprint('main', __name__)
|
main_bp = Blueprint('main', __name__)
|
||||||
|
|
||||||
@@ -93,36 +92,60 @@ def serve_new_web_source(filename):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@main_bp.route('/newApi/<path:subpath>', methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'])
|
|
||||||
def proxy_new_api(subpath):
|
|
||||||
target_url = f"{JAVA_API_BASE}/{subpath}"
|
|
||||||
try:
|
|
||||||
headers = {
|
|
||||||
key: value
|
|
||||||
for key, value in request.headers.items()
|
|
||||||
if key.lower() not in {'host', 'content-length'}
|
|
||||||
}
|
|
||||||
upstream = requests.request(
|
|
||||||
method=request.method,
|
|
||||||
url=target_url,
|
|
||||||
params=request.args,
|
|
||||||
data=request.get_data(),
|
|
||||||
headers=headers,
|
|
||||||
cookies=request.cookies,
|
|
||||||
allow_redirects=False,
|
|
||||||
timeout=300,
|
|
||||||
)
|
|
||||||
excluded_headers = {'content-encoding', 'content-length', 'transfer-encoding', 'connection'}
|
|
||||||
response_headers = [
|
|
||||||
(name, value)
|
|
||||||
for name, value in upstream.headers.items()
|
|
||||||
if name.lower() not in excluded_headers
|
|
||||||
]
|
|
||||||
return Response(upstream.content, upstream.status_code, response_headers)
|
|
||||||
except requests.RequestException as exc:
|
|
||||||
return {'success': False, 'message': str(exc), 'data': None}, 502
|
|
||||||
|
|
||||||
|
|
||||||
@main_bp.route('/logo.jpg', methods=['GET'])
|
@main_bp.route('/logo.jpg', methods=['GET'])
|
||||||
def get_logo_image():
|
def get_logo_image():
|
||||||
return send_file(os.path.join(BASE_DIR, "logo.jpg"), mimetype='image/jpeg')
|
return send_file(os.path.join(BASE_DIR, "logo.jpg"), mimetype='image/jpeg')
|
||||||
|
|
||||||
|
|
||||||
|
@main_bp.route('/newApi/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])
|
||||||
|
def proxy(path):
|
||||||
|
target_url = f"{JAVA_API_BASE}/{path}"
|
||||||
|
|
||||||
|
# 复制请求参数
|
||||||
|
params = request.args.to_dict()
|
||||||
|
|
||||||
|
# 处理请求头
|
||||||
|
headers = {}
|
||||||
|
for key, value in request.headers:
|
||||||
|
if key.lower() in ['host', 'content-length', 'connection']:
|
||||||
|
continue
|
||||||
|
headers[key] = value
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 使用流式请求
|
||||||
|
req = requests.request(
|
||||||
|
method=request.method,
|
||||||
|
url=target_url,
|
||||||
|
params=params,
|
||||||
|
headers=headers,
|
||||||
|
data=request.get_data() if request.get_data() else None,
|
||||||
|
cookies=request.cookies,
|
||||||
|
stream=True, # 启用流式传输
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
|
||||||
|
# 流式响应
|
||||||
|
def generate():
|
||||||
|
for chunk in req.iter_content(chunk_size=8192):
|
||||||
|
if chunk:
|
||||||
|
yield chunk
|
||||||
|
|
||||||
|
# 构建响应
|
||||||
|
response = Response(stream_with_context(generate()), status=req.status_code)
|
||||||
|
|
||||||
|
# 复制响应头
|
||||||
|
for key, value in req.headers.items():
|
||||||
|
if key.lower() not in ['content-encoding', 'content-length', 'transfer-encoding', 'connection']:
|
||||||
|
response.headers[key] = value
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
print("后端服务超时")
|
||||||
|
return Response("后端服务超时", status=504)
|
||||||
|
except requests.exceptions.ConnectionError:
|
||||||
|
print("无法连接到后端服务")
|
||||||
|
return Response("无法连接到后端服务", status=502)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"代理请求失败: {e}")
|
||||||
|
return Response(f"代理错误: {str(e)}", status=500)
|
||||||
51
app/main.py
51
app/main.py
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
基于 pywebview 实现的跨平台 UI,需先登录方可使用
|
基于 pywebview 实现的跨平台 UI,需先登录方可使用
|
||||||
"""
|
"""
|
||||||
from config import cache_path,debug,version,java_api_base
|
from config import cache_path,debug,version,JAVA_API_BASE
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
@@ -26,7 +26,6 @@ with open("version.txt","w",encoding="utf-8") as file:
|
|||||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
APP_URL = "http://127.0.0.1:5123"
|
APP_URL = "http://127.0.0.1:5123"
|
||||||
PORT = 5123
|
PORT = 5123
|
||||||
JAVA_API_BASE = java_api_base
|
|
||||||
|
|
||||||
|
|
||||||
def generate_images(params):
|
def generate_images(params):
|
||||||
@@ -145,28 +144,6 @@ class WindowAPI:
|
|||||||
)
|
)
|
||||||
return result[0] if result else ''
|
return result[0] if result else ''
|
||||||
|
|
||||||
def upload_file_to_java(self, file_path):
|
|
||||||
"""按本地路径读取文件并上传到 Java 后端临时目录。"""
|
|
||||||
if not file_path or not str(file_path).strip():
|
|
||||||
return {'success': False, 'error': '文件路径为空'}
|
|
||||||
normalized_path = os.path.abspath(str(file_path).strip())
|
|
||||||
if not os.path.isfile(normalized_path):
|
|
||||||
return {'success': False, 'error': '文件不存在'}
|
|
||||||
try:
|
|
||||||
with open(normalized_path, 'rb') as file_obj:
|
|
||||||
response = requests.post(
|
|
||||||
f"{JAVA_API_BASE}/api/files/upload",
|
|
||||||
files={'file': (os.path.basename(normalized_path), file_obj)},
|
|
||||||
timeout=120,
|
|
||||||
)
|
|
||||||
response.raise_for_status()
|
|
||||||
payload = response.json()
|
|
||||||
if not isinstance(payload, dict):
|
|
||||||
return {'success': False, 'error': 'Java 返回格式错误'}
|
|
||||||
return payload
|
|
||||||
except Exception as e:
|
|
||||||
return {'success': False, 'error': str(e)}
|
|
||||||
|
|
||||||
def save_file_from_url(self, url, default_filename='download.zip'):
|
def save_file_from_url(self, url, default_filename='download.zip'):
|
||||||
"""弹窗选择保存位置,从 url 下载文件并保存。用于品牌任务结果 zip 等。"""
|
"""弹窗选择保存位置,从 url 下载文件并保存。用于品牌任务结果 zip 等。"""
|
||||||
if not url or not url.strip():
|
if not url or not url.strip():
|
||||||
@@ -234,6 +211,27 @@ class WindowAPI:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {'success': False, 'error': str(e)}
|
return {'success': False, 'error': str(e)}
|
||||||
|
|
||||||
|
def upload_file_to_java(self, file_path):
|
||||||
|
"""按本地路径读取文件并上传到 Java 后端临时目录。"""
|
||||||
|
if not file_path or not str(file_path).strip():
|
||||||
|
return {'success': False, 'error': '文件路径为空'}
|
||||||
|
normalized_path = os.path.abspath(str(file_path).strip())
|
||||||
|
if not os.path.isfile(normalized_path):
|
||||||
|
return {'success': False, 'error': '文件不存在'}
|
||||||
|
try:
|
||||||
|
with open(normalized_path, 'rb') as file_obj:
|
||||||
|
response = requests.post(
|
||||||
|
f"{JAVA_API_BASE}/api/files/upload",
|
||||||
|
files={'file': (os.path.basename(normalized_path), file_obj)},
|
||||||
|
timeout=120,
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
payload = response.json()
|
||||||
|
if not isinstance(payload, dict):
|
||||||
|
return {'success': False, 'error': 'Java 返回格式错误'}
|
||||||
|
return payload
|
||||||
|
except Exception as e:
|
||||||
|
return {'success': False, 'error': str(e)}
|
||||||
|
|
||||||
def _select_folder_win32():
|
def _select_folder_win32():
|
||||||
"""Windows: 用 ctypes 调用系统文件夹选择对话框,打包成 exe 后也能正常弹窗(不依赖 tkinter)"""
|
"""Windows: 用 ctypes 调用系统文件夹选择对话框,打包成 exe 后也能正常弹窗(不依赖 tkinter)"""
|
||||||
@@ -345,7 +343,7 @@ def main():
|
|||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
window = webview.create_window(
|
window = webview.create_window(
|
||||||
title="南日AI",
|
title="数富AI",
|
||||||
url=APP_URL,
|
url=APP_URL,
|
||||||
width=1400,
|
width=1400,
|
||||||
height=900,
|
height=900,
|
||||||
@@ -356,7 +354,8 @@ def main():
|
|||||||
easy_drag=True
|
easy_drag=True
|
||||||
)
|
)
|
||||||
api = WindowAPI(window)
|
api = WindowAPI(window)
|
||||||
window.expose(api.close, api.minimize, api.maximize, api.toggle_maximize, generate_images, api.save_image, api.save_image_to_folder, api.select_folder, api.select_brand_xlsx_files, api.select_brand_folder, api.upload_file_to_java, api.save_file_from_url, api.save_template_xlsx,api.save_template_zip)
|
window.expose(api.close, api.minimize, api.maximize, api.toggle_maximize, generate_images, api.save_image, api.save_image_to_folder, api.select_folder, api.select_brand_xlsx_files, api.select_brand_folder,
|
||||||
|
api.save_file_from_url, api.save_template_xlsx,api.save_template_zip,api.upload_file_to_java)
|
||||||
webview.start(
|
webview.start(
|
||||||
debug=True,
|
debug=True,
|
||||||
storage_path=cache_path,
|
storage_path=cache_path,
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>格式转换 - 数富AI</title>
|
<title>格式转换 - 数富AI</title>
|
||||||
<script type="module" crossorigin src="/assets/convert.js"></script>
|
<script type="module" crossorigin src="/assets/convert.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-Rm5Hhqpf.js">
|
<link rel="modulepreload" crossorigin href="/assets/pywebview-BnpJyyGZ.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-Chz98E9c.css">
|
<link rel="stylesheet" crossorigin href="/assets/pywebview-IqgMfeBe.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/convert-D4Yv2oRy.css">
|
<link rel="stylesheet" crossorigin href="/assets/convert-BxkpMHO9.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>数据去重 - 数富AI</title>
|
<title>数据去重 - 数富AI</title>
|
||||||
<script type="module" crossorigin src="/assets/dedupe.js"></script>
|
<script type="module" crossorigin src="/assets/dedupe.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-Rm5Hhqpf.js">
|
<link rel="modulepreload" crossorigin href="/assets/pywebview-BnpJyyGZ.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-Chz98E9c.css">
|
<link rel="stylesheet" crossorigin href="/assets/pywebview-IqgMfeBe.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/dedupe-BHE_BCAJ.css">
|
<link rel="stylesheet" crossorigin href="/assets/dedupe-DzFvTfoj.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>数据拆分 - 数富AI</title>
|
<title>数据拆分 - 数富AI</title>
|
||||||
<script type="module" crossorigin src="/assets/split.js"></script>
|
<script type="module" crossorigin src="/assets/split.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/pywebview-Rm5Hhqpf.js">
|
<link rel="modulepreload" crossorigin href="/assets/pywebview-BnpJyyGZ.js">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/pywebview-Chz98E9c.css">
|
<link rel="stylesheet" crossorigin href="/assets/pywebview-IqgMfeBe.css">
|
||||||
<link rel="stylesheet" crossorigin href="/assets/split-_O6NHqDj.css">
|
<link rel="stylesheet" crossorigin href="/assets/split-DidKJ84l.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
Reference in New Issue
Block a user