modified: amazon/__pycache__/approve.cpython-39.pyc

modified:   amazon/__pycache__/del_brand.cpython-39.pyc
	modified:   amazon/__pycache__/main.cpython-39.pyc
	modified:   amazon/approve.py
	new file:   amazon/backend_api.py
	modified:   amazon/del_brand.py
	modified:   amazon/main.py
	modified:   blueprints/main.py
	modified:   main.py
This commit is contained in:
铭坤
2026-04-08 16:03:48 +08:00
parent 3149d80456
commit 7151f6b5e0
9 changed files with 231 additions and 45 deletions

View File

@@ -7,6 +7,12 @@ from config import runing_task, runing_shop
from datetime import datetime from datetime import datetime
from amazon.del_brand import AmamzonBase, kill_process from amazon.del_brand import AmamzonBase, kill_process
# 导入 webview 用于前端通知
try:
import webview
except ImportError:
webview = None
class AmzoneApprove(AmamzonBase): class AmzoneApprove(AmamzonBase):
def SwitchPage(self): def SwitchPage(self):
@@ -190,8 +196,8 @@ class AmzoneApprove(AmamzonBase):
num = 0 num = 0
while 1: while 1:
if num > 3: #测试 # if num > 3: #测试
return # return
# 等待加载完成 # 等待加载完成
load_ele = self.tab.eles("xpath://div[contains(@class,'Loader-module__loader')]",timeout=5) load_ele = self.tab.eles("xpath://div[contains(@class,'Loader-module__loader')]",timeout=5)
if len(load_ele) > 0: if len(load_ele) > 0:
@@ -202,14 +208,17 @@ class AmzoneApprove(AmamzonBase):
page_pamel = self.tab.eles('xpath://kat-pagination',timeout=5) page_pamel = self.tab.eles('xpath://kat-pagination',timeout=5)
if len(page_pamel) > 0: if len(page_pamel) > 0:
current_page = page_pamel[0].sr('xpath:.//ul[@class="pages"]//li[@aria-current="true"]').text current_page = page_pamel[0].sr('xpath:.//ul[@class="pages"]//li[@aria-current="true"]').text
print(f"开始处理,当前页码: {current_page}") print(f"【current_page】开始处理,当前页码: {current_page}")
sku_ls = self.tab.eles("xpath://div[@data-sku]",timeout=3) sku_ls = self.tab.eles("xpath://div[@data-sku]",timeout=10)
print(f"获取到 {len(sku_ls)}") print(f"获取到 {len(sku_ls)}")
for sku_ele in sku_ls[0:2]: # for sku_ele in sku_ls[0:2]:
for sku_ele in sku_ls:
# solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]') # solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]')
solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]|.//kat-link[@label="修复被禁止显示的商品"]|//kat-link[@label="解决商品移除风险"]') solve_problem = sku_ele.eles('xpath:.//kat-link[@label="解决商品信息问题"]|.//kat-link[@label="修复被禁止显示的商品"]|.//kat-link[@label="解决商品移除风险"]')
if len(solve_problem) == 0: if len(solve_problem) == 0:
print(f"{asin},没有处理入口,不处理")
yield (asin,"没有处理入口,不处理")
continue continue
asin = sku_ele.ele('xpath:.//div[contains(@class,"JanusSplitBox-module__container")]//div[contains(@class,"JanusSplitBox-module__panel--") and contains(string(.),"ASIN")]/..//div[last()]').text asin = sku_ele.ele('xpath:.//div[contains(@class,"JanusSplitBox-module__container")]//div[contains(@class,"JanusSplitBox-module__panel--") and contains(string(.),"ASIN")]/..//div[last()]').text
print(f"ASIN {asin} 存在问题,正在点击解决...") print(f"ASIN {asin} 存在问题,正在点击解决...")
@@ -235,9 +244,9 @@ class AmzoneApprove(AmamzonBase):
# 解决商品信息违规问题 按钮 # 解决商品信息违规问题 按钮
kat_box_ls = action_panel.eles('xpath:.//kat-box[@variant="white"]',timeout=5) kat_box_ls = action_panel.eles('xpath:.//kat-box[@variant="white"]',timeout=5)
if len(kat_box_ls) == 0: # if len(kat_box_ls) == 0:
print(f"ASIN {asin} 的操作面板中未找到选项,跳过...") # print(f"ASIN {asin} 的操作面板中未找到选项,跳过...")
continue # continue
print(f"ASIN {asin}需要处理的有:{len(kat_box_ls)}个问题.") print(f"ASIN {asin}需要处理的有:{len(kat_box_ls)}个问题.")
# 解决商品信息违规问题 按钮,有多少个都要处理 # 解决商品信息违规问题 按钮,有多少个都要处理
for i in range(len(kat_box_ls)): for i in range(len(kat_box_ls)):
@@ -284,7 +293,7 @@ class AmzoneApprove(AmamzonBase):
kat_box_ls = action_panel.eles('xpath:.//kat-box[@variant="white"]', timeout=5) kat_box_ls = action_panel.eles('xpath:.//kat-box[@variant="white"]', timeout=5)
# 修复商品信息 # 修复商品信息
repair_product = action_panel.eles('xpath://kat-button[@class="action-button"]',timeout=5) repair_product = action_panel.eles('xpath:.//kat-button[@class="action-button"]',timeout=5)
if len(repair_product) > 0: if len(repair_product) > 0:
print(f"ASIN {asin} 存在修复商品信息按钮,正在点击...") print(f"ASIN {asin} 存在修复商品信息按钮,正在点击...")
self.clear_tab() self.clear_tab()
@@ -316,7 +325,7 @@ class AmzoneApprove(AmamzonBase):
break break
next_page_btn.click() next_page_btn.click()
num += 1 num += 1
print(f"正在翻页,已翻 {num} 页...") # print(f"正在翻页,已翻 {num} 页...")
class ApproveTask: class ApproveTask:
@@ -330,6 +339,75 @@ class ApproveTask:
"UK": "英国" "UK": "英国"
} }
@staticmethod
def show_notification(message: str, message_type: str = "error"):
"""显示 pywebview 顶层通知5秒后自动消失
Args:
message: 通知消息
message_type: 消息类型 (success/warning/error/info)
"""
if webview and webview.windows:
try:
# 转义单引号,防止 JavaScript 语法错误
safe_message = message.replace("'", "\\'").replace('"', '\\"').replace('\n', '\\n')
# 设置通知样式颜色
color_map = {
'success': '#67C23A',
'warning': '#E6A23C',
'error': '#F56C6C',
'info': '#909399'
}
bg_color = color_map.get(message_type, '#F56C6C')
# 构建前端通知的 JavaScript 代码 - 创建原生 HTML 通知
js_code = f"""
(function() {{
// 移除已存在的通知
var existingNotif = document.getElementById('pywebview-notification');
if (existingNotif) {{
existingNotif.remove();
}}
// 创建通知容器
var notif = document.createElement('div');
notif.id = 'pywebview-notification';
notif.style.cssText = 'position: fixed; top: 20px; left: 50%; transform: translateX(-50%); ' +
'background: {bg_color}; color: white; padding: 12px 20px; border-radius: 4px; ' +
'box-shadow: 0 2px 12px rgba(0,0,0,0.3); z-index: 99999; font-size: 14px; ' +
'max-width: 600px; word-wrap: break-word; display: flex; align-items: center; gap: 10px;';
// 添加消息内容
var msgSpan = document.createElement('span');
msgSpan.textContent = '{safe_message}';
notif.appendChild(msgSpan);
// 添加关闭按钮
var closeBtn = document.createElement('span');
closeBtn.innerHTML = '×';
closeBtn.style.cssText = 'cursor: pointer; font-size: 18px; font-weight: bold; margin-left: 10px;';
closeBtn.onclick = function() {{ notif.remove(); }};
notif.appendChild(closeBtn);
// 添加到页面
document.body.appendChild(notif);
// 5秒后自动消失
setTimeout(function() {{
if (notif && notif.parentNode) {{
notif.style.transition = 'opacity 0.3s';
notif.style.opacity = '0';
setTimeout(function() {{ notif.remove(); }}, 300);
}}
}}, 5000);
}})();
"""
# 在第一个窗口中执行 JavaScript
webview.windows[0].evaluate_js(js_code)
except Exception as e:
print(f"显示通知失败: {str(e)}")
""" """
处理流程: 处理流程:
1、解析任务json,提取对应的companyName 组组装user_info实例化 AmzoneApprove 1、解析任务json,提取对应的companyName 组组装user_info实例化 AmzoneApprove
@@ -385,6 +463,8 @@ class ApproveTask:
""" """
from datetime import datetime from datetime import datetime
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if level == "ERROR":
self.show_notification(message, "error")
print(f"[{timestamp}] [ApproveTask] [{level}] {message}") print(f"[{timestamp}] [ApproveTask] [{level}] {message}")
def process_task(self, task_data: dict): def process_task(self, task_data: dict):
@@ -439,6 +519,7 @@ class ApproveTask:
shop_name = shop_item.get("shopName", "未知店铺") shop_name = shop_item.get("shopName", "未知店铺")
self.log(f"[{idx}/{len(items)}] 开始处理店铺: {shop_name}") self.log(f"[{idx}/{len(items)}] 开始处理店铺: {shop_name}")
self.show_notification(f"开始处理店铺: {shop_name}", "info")
try: try:
self.process_shop(shop_item, country_codes, task_id,risk_listing_filter) self.process_shop(shop_item, country_codes, task_id,risk_listing_filter)
@@ -502,11 +583,11 @@ class ApproveTask:
self.log(f"尝试打开店铺 {shop_name} (第 {retry + 1}/{max_retries} 次)") self.log(f"尝试打开店铺 {shop_name} (第 {retry + 1}/{max_retries} 次)")
# 如果不是第一次尝试,先杀进程 # 如果不是第一次尝试,先杀进程
if retry > 0: # if retry > 0:
self.log("重试前先杀掉浏览器进程...") # self.log("重试前先杀掉浏览器进程...")
kill_process("v6") # kill_process("v6")
kill_process("v5") # kill_process("v5")
time.sleep(2) # time.sleep(2)
# 组装用户信息并创建驱动 # 组装用户信息并创建驱动
user_info = { user_info = {
@@ -522,6 +603,7 @@ class ApproveTask:
else: else:
self.log(f"打开店铺失败: {browser}", "WARNING") self.log(f"打开店铺失败: {browser}", "WARNING")
driver = None driver = None
# 判断是否需要登录
except Exception as e: except Exception as e:
import traceback import traceback
@@ -535,7 +617,8 @@ class ApproveTask:
# 检查是否成功打开 # 检查是否成功打开
if not driver or not browser or browser == "店铺不存在": if not driver or not browser or browser == "店铺不存在":
self.log(f"店铺 {shop_name} 打开失败,已重试 {max_retries} 次,跳过该店铺", "ERROR") error_msg = f"店铺 {shop_name} 打开失败,已重试 {max_retries} 次,跳过该店铺"
self.log(error_msg, "ERROR")
# 从执行列表中移除 # 从执行列表中移除
if shop_name in runing_shop: if shop_name in runing_shop:
del runing_shop[shop_name] del runing_shop[shop_name]
@@ -589,7 +672,9 @@ class ApproveTask:
# 转换国家代码为中文名称 # 转换国家代码为中文名称
country_name = self.country_info.get(country_code, country_code) country_name = self.country_info.get(country_code, country_code)
self.log(f"开始处理国家: {country_name} ({country_code})") info_mes = f"开始处理国家: {country_name} ({country_code})"
self.log(info_mes)
self.show_notification(info_mes, "info")
# 更新当前处理的国家 # 更新当前处理的国家
if task_id in runing_task: if task_id in runing_task:
@@ -630,7 +715,8 @@ class ApproveTask:
# 如果切换失败,直接返回 # 如果切换失败,直接返回
if not switch_success: if not switch_success:
self.log(f"切换到国家 {country_name} 失败,已重试 {max_retries} 次,跳过该国家", "ERROR") error_message = f"切换到国家 {country_name} 失败,已重试 {max_retries} 次,跳过该国家"
self.log(error_message, "ERROR")
if task_id in runing_task: if task_id in runing_task:
runing_task[task_id]["processed_countries"] += 1 runing_task[task_id]["processed_countries"] += 1
return return
@@ -819,3 +905,5 @@ if __name__ == '__main__':
print(f"ASIN {asin} 的处理结果: {status}") print(f"ASIN {asin} 的处理结果: {status}")
print("已完成操作") print("已完成操作")
# for i in range(0):
# print(i)

View File

View File

@@ -10,6 +10,7 @@ import os
from typing import Literal from typing import Literal
from DrissionPage import Chromium from DrissionPage import Chromium
from DrissionPage.common import By from DrissionPage.common import By
from DrissionPage._pages.chromium_tab import ChromiumTab
def kill_process(version: Literal["v5", "v6"]): def kill_process(version: Literal["v5", "v6"]):
@@ -33,7 +34,7 @@ class ZiniaoDriver:
self.socket_port = socket_port self.socket_port = socket_port
self.client_path = None self.client_path = None
self.browser = None self.browser = None
self.tab = None self.tab: ChromiumTab = None
self.store_id = None self.store_id = None
def get_zinaio_exe(self, protocol_name: str = "superbrowser"): def get_zinaio_exe(self, protocol_name: str = "superbrowser"):
@@ -136,11 +137,11 @@ class ZiniaoDriver:
print(r) print(r)
return r.get("browserList") return r.get("browserList")
elif str(r.get("statusCode")) == "-10003": elif str(r.get("statusCode")) == "-10003":
print(f"login Err {json.dumps(r, ensure_ascii=False)}") print(f"【get_browser_list】登录失败 {json.dumps(r, ensure_ascii=False)}")
exit() # exit()
else: else:
print(f"Fail {json.dumps(r, ensure_ascii=False)} ") print(f"【get_browser_list】失败 {json.dumps(r, ensure_ascii=False)} ")
exit() # exit()
def open_store(self, store_info, isWebDriverReadOnlyMode=0, isprivacy=0, def open_store(self, store_info, isWebDriverReadOnlyMode=0, isprivacy=0,
isHeadless=0, cookieTypeSave=0, jsInfo=""): isHeadless=0, cookieTypeSave=0, jsInfo=""):
@@ -188,11 +189,11 @@ class ZiniaoDriver:
if str(r.get("statusCode")) == "0": if str(r.get("statusCode")) == "0":
return r return r
elif str(r.get("statusCode")) == "-10003": elif str(r.get("statusCode")) == "-10003":
print(f"login Err {json.dumps(r, ensure_ascii=False)}") raise RuntimeError(f"【open_store】登录失败 {json.dumps(r, ensure_ascii=False)}")
exit() # exit()
else: else:
print(f"Fail {json.dumps(r, ensure_ascii=False)} ") raise RuntimeError(f"【open_store】失败 {json.dumps(r, ensure_ascii=False)} ")
exit() # exit()
def get_browser(self, port) -> Chromium: def get_browser(self, port) -> Chromium:
""" """
@@ -209,6 +210,17 @@ class ZiniaoDriver:
def start_client(self): def start_client(self):
"""启动紫鸟客户端""" """启动紫鸟客户端"""
# 检查端口是否已经启动
try:
url = f'http://127.0.0.1:{self.socket_port}'
response = requests.get(url, timeout=2)
print(f"端口 {self.socket_port} 已经启动,跳过启动客户端操作")
return
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
# 端口未启动,继续执行启动操作
print(f"端口 {self.socket_port} 未启动,开始启动客户端")
self.client_path = self.get_zinaio_exe("superbrowserv6").strip('"') self.client_path = self.get_zinaio_exe("superbrowserv6").strip('"')
print(self.client_path) print(self.client_path)
cmd = [self.client_path, '--run_type=web_driver', '--ipc_type=http', cmd = [self.client_path, '--run_type=web_driver', '--ipc_type=http',
@@ -261,7 +273,8 @@ class ZiniaoDriver:
print("ip检测页地址为空请升级紫鸟浏览器到最新版") print("ip检测页地址为空请升级紫鸟浏览器到最新版")
print(f"=====关闭店铺:{shop_name}=====") print(f"=====关闭店铺:{shop_name}=====")
self.close_store(self.store_id) self.close_store(self.store_id)
exit() # exit()
raise RuntimeError("没有IP检测地址为了店铺安全不打开店铺")
ip_usable = self.open_ip_check(self.browser, ip_check_url) ip_usable = self.open_ip_check(self.browser, ip_check_url)
if ip_usable: if ip_usable:
print("ip检测通过打开店铺平台主页") print("ip检测通过打开店铺平台主页")
@@ -298,11 +311,11 @@ class ZiniaoDriver:
if str(r.get("statusCode")) == "0": if str(r.get("statusCode")) == "0":
return r return r
elif str(r.get("statusCode")) == "-10003": elif str(r.get("statusCode")) == "-10003":
print(f"login Err {json.dumps(r, ensure_ascii=False)}") raise RuntimeError(f"【close_store】登录失败 {json.dumps(r, ensure_ascii=False)}")
exit() # exit()
else: else:
print(f"Fail {json.dumps(r, ensure_ascii=False)} ") raise RuntimeError(f"【close_store】失败 {json.dumps(r, ensure_ascii=False)} ")
exit() # exit()
def open_launcher_page(self, launcher_page: str, browser: Chromium = None): def open_launcher_page(self, launcher_page: str, browser: Chromium = None):
""" """
@@ -436,8 +449,38 @@ class AmamzonBase(ZiniaoDriver):
print(f"切换国家时发生异常:{traceback.format_exc()}") print(f"切换国家时发生异常:{traceback.format_exc()}")
return False return False
def need_login(self):
"""
判断是否需要登录,部分国家可能需要登录后才能切换国家
处理流程:
"""
need_login_ele = self.tab.eles('xpath://h1[@class="a-spacing-small"]',timeout=5)
if len(need_login_ele) >0:
print("检测到需要登录元素")
return True
return False
def login(self,password,username=""):
try:
pwd_input = self.tab.ele('xpath://input[@type="password"]',timeout=10)
pwd_input.input(password)
submit_btn = self.tab.ele('xpath://input[@id="signInSubmit"]',timeout=10)
submit_btn.click()
self.tab.wait.doc_loaded()
opt_code_input = self.tab.ele('xpath://input[@name="otpCode"]',timeout=10)
opt_code_input.wait.displayed(timeout=10,raise_err=False)
for _ in range(30):
if opt_code_input.value is not None and opt_code_input.value.strip() != "":
print("检测到验证码输入完成")
submit_btn = self.tab.ele('xpath://input[@id="auth-signin-button"]',timeout=10)
submit_btn.click()
self.tab.wait.doc_loaded()
return True
except Exception as e:
print("登录过程中发生异常:" + traceback.format_exc())
return False
class AmazoneDriver(AmamzonBase): class AmazoneDriver(AmamzonBase):
"""亚马逊专用驱动类继承自ZiniaoDriver""" """亚马逊专用驱动类继承自ZiniaoDriver"""

View File

@@ -78,7 +78,7 @@ class TaskMonitor:
except Exception as e: except Exception as e:
# 静默处理队列为空的超时,只记录真正的异常 # 静默处理队列为空的超时,只记录真正的异常
if "Empty" not in str(e): if str(e) and "Empty" not in str(e):
self.log(f"任务监控异常: {str(e)}", "ERROR") self.log(f"任务监控异常: {str(e)}", "ERROR")
# 队列为空时不需要额外等待,直接继续循环 # 队列为空时不需要额外等待,直接继续循环
@@ -210,11 +210,11 @@ class TaskMonitor:
self.log(f"尝试打开店铺 {shop_name} (第 {retry + 1}/{max_retries} 次)") self.log(f"尝试打开店铺 {shop_name} (第 {retry + 1}/{max_retries} 次)")
# 如果不是第一次尝试,先杀进程 # 如果不是第一次尝试,先杀进程
if retry > 0: # if retry > 0:
self.log("重试前先杀掉浏览器进程...") # self.log("重试前先杀掉浏览器进程...")
kill_process("v6") # kill_process("v6")
kill_process("v5") # kill_process("v5")
time.sleep(2) # time.sleep(2)
# 创建驱动并打开店铺 # 创建驱动并打开店铺
user_info = { user_info = {
@@ -231,6 +231,27 @@ class TaskMonitor:
self.log(f"打开店铺失败: {browser}", "WARNING") self.log(f"打开店铺失败: {browser}", "WARNING")
driver = None driver = None
# 判断是否需要登录
# need_login = driver.need_login()
# if need_login:
# self.log(f"店铺 {shop_name} 需要登录,正在登录...")
# password = ""
# login_success = driver.login(password)
# if login_success:
# self.log(f"店铺 {shop_name} 登录成功,正在重新打开店铺...")
# browser = driver.open_shop(shop_name)
# if browser and browser != "店铺不存在":
# self.log(f"成功打开店铺 {shop_name} 登录后")
# break
# else:
# self.log(f"登录后打开店铺失败: {browser}", "WARNING")
# driver = None
# else:
# self.log(f"店铺 {shop_name} 登录失败", "WARNING")
# driver = None
except Exception as e: except Exception as e:
self.log(f"打开店铺异常: {str(e)}", "ERROR") self.log(f"打开店铺异常: {str(e)}", "ERROR")
driver = None driver = None

View File

@@ -110,7 +110,10 @@ def proxy(path):
if key.lower() in ['host', 'content-length', 'connection']: if key.lower() in ['host', 'content-length', 'connection']:
continue continue
headers[key] = value headers[key] = value
ignore_url = [f"{JAVA_API_BASE}/api/delete-brand/tasks/batch",f"{JAVA_API_BASE}/api/delete-brand/history"] ignore_url = [f"{JAVA_API_BASE}/api/delete-brand/tasks/batch",
f"{JAVA_API_BASE}/api/delete-brand/history",
f"{JAVA_API_BASE}/api/product-risk-resolve/tasks/batch",
]
if target_url not in ignore_url: if target_url not in ignore_url:
try: try:
print("=============================") print("=============================")

View File

@@ -11,7 +11,7 @@ from amazon.main import TaskMonitor
os.makedirs(cache_path,exist_ok=True) os.makedirs(cache_path,exist_ok=True)
if not debug: if not debug:
today = datetime.datetime.now().strftime("%Y_%m_%d") today = datetime.datetime.now().strftime("%Y_%m_%d")
sys.stdout = open(os.path.join(cache_path,f'{today}.log'), 'a',buffering=1) sys.stdout = open(os.path.join(cache_path,f'{today}.log'), 'a',buffering=1,encoding='utf-8')
sys.stderr = sys.stdout sys.stderr = sys.stdout
import webview import webview
@@ -20,6 +20,9 @@ import time
import requests import requests
import subprocess import subprocess
from amazon.del_brand import kill_process
with open("version.txt","w",encoding="utf-8") as file: with open("version.txt","w",encoding="utf-8") as file:
file.write(json.dumps({"version":version})) file.write(json.dumps({"version":version}))
@@ -54,8 +57,15 @@ class WindowAPI:
window.events.restored += lambda _: setattr(self, '_is_maximized', False) window.events.restored += lambda _: setattr(self, '_is_maximized', False)
def close(self): def close(self):
sys.exit(1) try:
self._window.destroy() kill_process('v6')
except Exception as e:
print(f"【退出前】关闭紫鸟浏览器进程异常: {str(e)}")
try:
self._window.destroy()
except:
pass
os._exit(0) # 强制退出整个进程,包括所有线程
def minimize(self): def minimize(self):
self._window.minimize() self._window.minimize()
@@ -358,6 +368,23 @@ def start_task_monitor():
print(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 任务监控线程异常退出: {traceback.format_exc()}") print(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 任务监控线程异常退出: {traceback.format_exc()}")
def on_window_closing():
"""窗口关闭事件处理器,执行与 close() 方法相同的清理逻辑"""
# 先关闭窗口,避免看起来卡着
try:
if webview.windows:
webview.windows[0].destroy()
except:
pass
# 然后执行清理逻辑
try:
kill_process('v6')
except Exception as e:
print(f"【退出前】关闭紫鸟浏览器进程异常: {str(e)}")
os._exit(0) # 强制退出整个进程,包括所有线程
def main(): def main():
if not os.path.exists(cache_path): if not os.path.exists(cache_path):
os.makedirs(cache_path,exist_ok=True) os.makedirs(cache_path,exist_ok=True)
@@ -384,11 +411,15 @@ def main():
frameless=False, frameless=False,
easy_drag=True easy_drag=True
) )
# 为窗口关闭事件添加处理器
window.events.closing += on_window_closing
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, 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,api.open_external_url,api.enqueue_json,api.get_detail_del,api.save_file_from_url_new) api.save_file_from_url, api.save_template_xlsx,api.save_template_zip,api.upload_file_to_java,api.open_external_url,api.enqueue_json,api.get_detail_del,api.save_file_from_url_new)
webview.start( webview.start(
debug=True, debug=False,
storage_path=cache_path, storage_path=cache_path,
private_mode=False private_mode=False
) )