v92-v105 多国适配 + 模板导出增强 + 生图可靠性优化
- 新增韩国(KR)适配:countries/pinterest 种子词、K-pop 提示词、UI 国家列表 - 爬虫跨运行持久化已采集 URL(.collected_urls.json),同关键词重复搜索采新图 - 模板导出增强:SPU 商品属性列名前缀剥离匹配、尺码下拉框 INDIRECT 动态引用、 SPU 字段映射(袖长/门襟/胸垫等)、季节/印花图案女装映射、建议售价统一必填、 SKU 分类/数量/单位直接填默认值 - 自定义模式:多模态提示词独立(custom_analyze_*)、生图提示词模板可配置 - Pinterest:空选品守卫、连续空分析保护(max_empty_analyze)、flat_prompt 配置覆盖 - 生图可靠性:开始/完成进度日志、异步任务轮询超时 60s→300s
This commit is contained in:
@@ -31,9 +31,7 @@ import yaml
|
||||
|
||||
from graph.agent import run_country
|
||||
|
||||
COUNTRIES = ["US", "GB", "JP", "AU", "MX", "DE", "BR", "SA", "PL", "ES", "IT", "CA"]
|
||||
# LLM 只适配 OpenAI 兼容协议:openai(真调用,可在下方配置自定义模型/URL/Key)/ mock(无 key 演示占位)
|
||||
PROVIDERS = ["openai", "mock"]
|
||||
COUNTRIES = ["US", "GB", "JP", "AU", "MX", "DE", "BR", "SA", "PL", "ES", "IT", "CA", "KR"]
|
||||
|
||||
|
||||
def resource_root() -> Path:
|
||||
@@ -389,6 +387,10 @@ def run_pinterest_pipeline(countries, provider, log_q, spu_tasks=None, spu_count
|
||||
custom_image_dir 非空 → 自定义图片模式:不搜索不采集,把该文件夹有效图片直接送多模态分析,
|
||||
沿用 Pinterest 后续所有步骤;数量硬校验(有效图片数 ≥ 选品清单总数)由 run_pinterest_ref 执行。
|
||||
"""
|
||||
if not spu_tasks:
|
||||
log_q.put(("log", "[UI] Pinterest/自定义模式必须通过「+添加选品」添加任务清单后才能运行\n"))
|
||||
log_q.put(("error", None))
|
||||
return
|
||||
config = load_config()
|
||||
config["seed_provider"] = provider
|
||||
apply_openai_cfg(config, oai)
|
||||
@@ -498,8 +500,10 @@ def _pack_cache(country: str, log_q=None) -> str:
|
||||
class App(tk.Tk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.title("POD 热点抓取 Agent")
|
||||
self.geometry("920x660")
|
||||
self.title("POD 智能设计 Agent")
|
||||
self.geometry("1280x840")
|
||||
self.minsize(1100, 720)
|
||||
self.configure(bg="#F2F4F8")
|
||||
self._q = queue.Queue()
|
||||
self._busy = False
|
||||
self._results = {}
|
||||
@@ -513,151 +517,249 @@ class App(tk.Tk):
|
||||
except Exception as e: # noqa: BLE001
|
||||
LOG_FILE = None
|
||||
print(f"[UI] 日志文件创建失败(不影响运行): {e}")
|
||||
self._setup_style()
|
||||
self._build_ui()
|
||||
self._load_cache()
|
||||
self.after(100, self._poll)
|
||||
|
||||
def _default_provider(self) -> str:
|
||||
"""默认模式:config.yaml llm_screen.provider(用户已配 key → openai 默认)。"""
|
||||
# ---------- 主题样式 ----------
|
||||
def _setup_style(self):
|
||||
style = ttk.Style(self)
|
||||
try:
|
||||
return "openai" if (load_config().get("llm_screen") or {}).get("provider") == "openai" else "mock"
|
||||
except Exception:
|
||||
return "mock"
|
||||
style.theme_use("clam")
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
# 主色板
|
||||
self._C_PRIMARY = "#2563EB" # 主蓝
|
||||
self._C_PRIMARY_DK = "#1D4ED8" # 主蓝深
|
||||
self._C_ACCENT = "#F59E0B" # 强调橙
|
||||
self._C_BG = "#F2F4F8" # 窗口背景
|
||||
self._C_CARD = "#FFFFFF" # 卡片背景
|
||||
self._C_BORDER = "#DCE1EA" # 边框
|
||||
self._C_TEXT = "#1F2937" # 主文字
|
||||
self._C_SUB = "#6B7280" # 次要文字
|
||||
self._C_OK = "#16A34A" # 成功绿
|
||||
self._C_WARN = "#B45309" # 警示
|
||||
self._FONT = ("Microsoft YaHei UI", 10)
|
||||
self._FONT_SM = ("Microsoft YaHei UI", 9)
|
||||
self._FONT_TITLE = ("Microsoft YaHei UI", 15, "bold")
|
||||
self._FONT_SUB = ("Microsoft YaHei UI", 9)
|
||||
|
||||
style.configure(".", font=self._FONT, background=self._C_BG,
|
||||
foreground=self._C_TEXT)
|
||||
style.configure("Card.TLabelframe", background=self._C_CARD,
|
||||
bordercolor=self._C_BORDER, relief="solid", borderwidth=1)
|
||||
style.configure("Card.TLabelframe.Label", background=self._C_CARD,
|
||||
foreground=self._C_PRIMARY_DK, font=("Microsoft YaHei UI", 10, "bold"))
|
||||
style.configure("Card.TFrame", background=self._C_CARD)
|
||||
style.configure("TLabel", background=self._C_BG, foreground=self._C_TEXT)
|
||||
style.configure("Card.TLabel", background=self._C_CARD, foreground=self._C_TEXT)
|
||||
style.configure("Card.TRadiobutton", background=self._C_CARD, foreground=self._C_TEXT)
|
||||
style.map("Card.TRadiobutton",
|
||||
background=[("active", self._C_CARD)],
|
||||
foreground=[("active", self._C_PRIMARY_DK)])
|
||||
style.configure("Sub.TLabel", background=self._C_BG, foreground=self._C_SUB,
|
||||
font=self._FONT_SM)
|
||||
style.configure("CardSub.TLabel", background=self._C_CARD, foreground=self._C_SUB,
|
||||
font=self._FONT_SM)
|
||||
style.configure("Accent.TLabel", background=self._C_BG, foreground=self._C_PRIMARY_DK,
|
||||
font=self._FONT_SM)
|
||||
style.configure("TButton", font=self._FONT, padding=(12, 6))
|
||||
style.configure("Primary.TButton", font=self._FONT, padding=(18, 8),
|
||||
background=self._C_PRIMARY, foreground="#FFFFFF", borderwidth=0)
|
||||
style.map("Primary.TButton",
|
||||
background=[("active", self._C_PRIMARY_DK), ("disabled", "#A5B4FC")],
|
||||
foreground=[("disabled", "#EEF2FF")])
|
||||
style.configure("AccentBtn.TButton", font=self._FONT, padding=(14, 7),
|
||||
background=self._C_ACCENT, foreground="#FFFFFF", borderwidth=0)
|
||||
style.map("AccentBtn.TButton",
|
||||
background=[("active", "#D97706"), ("disabled", "#FCD34D")],
|
||||
foreground=[("disabled", "#FFFBEB")])
|
||||
style.configure("TCombobox", padding=4)
|
||||
style.configure("TEntry", padding=4)
|
||||
style.configure("Treeview", font=self._FONT_SM, rowheight=26,
|
||||
background="#FFFFFF", fieldbackground="#FFFFFF",
|
||||
bordercolor=self._C_BORDER)
|
||||
style.configure("Treeview.Heading", font=("Microsoft YaHei UI", 9, "bold"),
|
||||
background="#EEF2F7", foreground=self._C_TEXT, padding=6)
|
||||
style.map("Treeview", background=[("selected", "#DBEAFE")],
|
||||
foreground=[("selected", "#1E3A8A")])
|
||||
style.configure("TNotebook", background=self._C_BG, borderwidth=0)
|
||||
style.configure("TNotebook.Tab", font=self._FONT, padding=(14, 7))
|
||||
style.map("TNotebook.Tab",
|
||||
background=[("selected", self._C_CARD)],
|
||||
foreground=[("selected", self._C_PRIMARY_DK)])
|
||||
|
||||
# ---------- UI 构建 ----------
|
||||
def _build_ui(self):
|
||||
top = ttk.Frame(self, padding=8)
|
||||
top.pack(fill="x")
|
||||
ttk.Label(top, text="国家:").pack(side="left")
|
||||
# —— 顶部标题栏 ——
|
||||
header = tk.Frame(self, bg=self._C_PRIMARY, height=62)
|
||||
header.pack(fill="x")
|
||||
header.pack_propagate(False)
|
||||
tk.Label(header, text="🎨 智能设计 Agent",
|
||||
bg=self._C_PRIMARY, fg="#FFFFFF",
|
||||
font=("Microsoft YaHei UI", 16, "bold")).pack(side="left", padx=16, pady=12)
|
||||
tk.Label(header, text="一键生成 T 恤印花设计 · 多国多站点",
|
||||
bg=self._C_PRIMARY, fg="#DBEAFE",
|
||||
font=self._FONT_SM).pack(side="left", padx=(4, 0), pady=12)
|
||||
|
||||
self.hotspot_time_var = tk.StringVar(value="")
|
||||
tk.Label(header, textvariable=self.hotspot_time_var,
|
||||
bg=self._C_PRIMARY, fg="#DBEAFE",
|
||||
font=self._FONT_SM).pack(side="right", padx=(0, 8), pady=12)
|
||||
|
||||
# —— 运行设置卡片:国家 / 流程 / 自定义图片 / 种子数量 ——
|
||||
cfg = ttk.LabelFrame(self, text="运行设置", style="Card.TLabelframe", padding=10)
|
||||
cfg.pack(fill="x", padx=12, pady=(10, 4))
|
||||
|
||||
ttk.Label(cfg, text="目标国家:", style="Card.TLabel").pack(side="left")
|
||||
self.country_var = tk.StringVar(value="US")
|
||||
self.country_combo = ttk.Combobox(top, textvariable=self.country_var,
|
||||
self.country_combo = ttk.Combobox(cfg, textvariable=self.country_var,
|
||||
values=COUNTRIES, state="readonly", width=6)
|
||||
self.country_combo.pack(side="left", padx=4)
|
||||
self.country_combo.pack(side="left", padx=(4, 14))
|
||||
self.country_combo.bind("<<ComboboxSelected>>", lambda e: self._on_country_selected())
|
||||
ttk.Label(top, text=" 模式:").pack(side="left", padx=(14, 0))
|
||||
self.provider_var = tk.StringVar(value=self._default_provider())
|
||||
ttk.Radiobutton(top, text="OpenAI(真 LLM+生图)", value="openai",
|
||||
variable=self.provider_var).pack(side="left", padx=2)
|
||||
ttk.Radiobutton(top, text="Mock 演示", value="mock",
|
||||
variable=self.provider_var).pack(side="left", padx=2)
|
||||
ttk.Label(top, text=" 流程:").pack(side="left", padx=(14, 0))
|
||||
self.flow_var = tk.StringVar(value="trends")
|
||||
ttk.Radiobutton(top, text="热点采集", value="trends",
|
||||
|
||||
ttk.Label(cfg, text="流程:", style="Card.TLabel").pack(side="left")
|
||||
self.flow_var = tk.StringVar(value="pinterest")
|
||||
ttk.Radiobutton(cfg, text="热点采集", value="trends", style="Card.TRadiobutton",
|
||||
variable=self.flow_var).pack(side="left", padx=2)
|
||||
ttk.Radiobutton(top, text="Pinterest 参考", value="pinterest",
|
||||
ttk.Radiobutton(cfg, text="Pinterest 参考", value="pinterest", style="Card.TRadiobutton",
|
||||
variable=self.flow_var).pack(side="left", padx=2)
|
||||
ttk.Radiobutton(top, text="自定义图片", value="custom",
|
||||
ttk.Radiobutton(cfg, text="自定义图片", value="custom", style="Card.TRadiobutton",
|
||||
variable=self.flow_var).pack(side="left", padx=2)
|
||||
ttk.Button(top, text="📁 选图片文件夹…", command=self._choose_custom_dir).pack(side="left", padx=(8, 0))
|
||||
ttk.Button(cfg, text="📁 选图片文件夹…", command=self._choose_custom_dir).pack(side="left", padx=(10, 0))
|
||||
self.custom_dir_var = tk.StringVar(value="")
|
||||
self.custom_dir_name_var = tk.StringVar(value="")
|
||||
ttk.Label(top, textvariable=self.custom_dir_name_var, anchor="w",
|
||||
foreground="#185FA5").pack(side="left", padx=(6, 0))
|
||||
ttk.Label(top, text=" 种子数量:").pack(side="left", padx=(14, 0))
|
||||
self.seed_var = tk.StringVar(value="24")
|
||||
ttk.Entry(top, textvariable=self.seed_var, width=4).pack(side="left")
|
||||
# 右上角:热点状态(缓存时间 / 采集时间)
|
||||
self.hotspot_time_var = tk.StringVar(value="")
|
||||
ttk.Label(top, textvariable=self.hotspot_time_var, anchor="e",
|
||||
foreground="#185FA5").pack(side="right")
|
||||
ttk.Label(cfg, textvariable=self.custom_dir_name_var, anchor="w",
|
||||
style="Accent.TLabel").pack(side="left", padx=(6, 14))
|
||||
|
||||
# —— 商品上传模板(必须上传后才能开始任务;template_export 从该模板解析并填充)——
|
||||
tpl = ttk.LabelFrame(self, text="商品上传模板(必须上传 .xlsx 后才能运行)", padding=4)
|
||||
tpl.pack(fill="x", padx=8, pady=(2, 0))
|
||||
ttk.Label(cfg, text="种子数量:", style="Card.TLabel").pack(side="left")
|
||||
self.seed_var = tk.StringVar(value="24")
|
||||
ttk.Entry(cfg, textvariable=self.seed_var, width=5).pack(side="left", padx=(4, 0))
|
||||
|
||||
# —— 商品上传模板卡片 ——
|
||||
tpl = ttk.LabelFrame(self, text="商品上传模板(必须上传 .xlsx 后才能运行)",
|
||||
style="Card.TLabelframe", padding=8)
|
||||
tpl.pack(fill="x", padx=12, pady=(4, 4))
|
||||
ttk.Button(tpl, text="📁 选择模板…", command=self._choose_template).pack(side="left")
|
||||
self.template_path_var = tk.StringVar(value="")
|
||||
self.template_name_var = tk.StringVar(value="(未选择)")
|
||||
ttk.Label(tpl, textvariable=self.template_name_var, anchor="w",
|
||||
foreground="#993C1D").pack(side="left", padx=(8, 0))
|
||||
style="CardSub.TLabel").pack(side="left", padx=(10, 0))
|
||||
|
||||
# —— 选品面板:国家→款号→颜色→添加任务(支持多款号 + 数量上限)——
|
||||
sel = ttk.LabelFrame(self, text="选品(选定国家自动加载款号;款号 → 颜色 → 添加任务)", padding=6)
|
||||
sel.pack(fill="x", padx=8, pady=(4, 0))
|
||||
# —— 选品卡片:款号 → 颜色 → 添加任务 ——
|
||||
sel = ttk.LabelFrame(self, text="选品(选定国家自动加载款号;款号 → 颜色 → +添加选品)",
|
||||
style="Card.TLabelframe", padding=10)
|
||||
sel.pack(fill="x", padx=12, pady=(4, 4))
|
||||
|
||||
# 左:款号列表
|
||||
lf1 = ttk.Frame(sel)
|
||||
lf1 = ttk.Frame(sel, style="Card.TFrame")
|
||||
lf1.pack(side="left", padx=4)
|
||||
ttk.Label(lf1, text="款号(选定后自动加载颜色与最低价):").pack(anchor="w")
|
||||
self.spu_list = tk.Listbox(lf1, selectmode="extended", height=5, width=14, exportselection=False)
|
||||
self.spu_list.pack(side="left")
|
||||
ttk.Label(lf1, text="款号(多选)", style="Card.TLabel").pack(anchor="w")
|
||||
self.spu_list = tk.Listbox(lf1, selectmode="extended", height=6, width=16,
|
||||
exportselection=False, bg="#FFFFFF", fg=self._C_TEXT,
|
||||
selectbackground="#DBEAFE", selectforeground="#1E3A8A",
|
||||
highlightthickness=1, highlightbackground=self._C_BORDER,
|
||||
highlightcolor=self._C_PRIMARY, font=self._FONT_SM,
|
||||
relief="solid", borderwidth=1)
|
||||
self.spu_list.pack(side="left", pady=(4, 0))
|
||||
self.spu_list.bind("<<ListboxSelect>>", self._on_spu_selected)
|
||||
self.min_price_var = tk.StringVar(value="")
|
||||
ttk.Label(lf1, textvariable=self.min_price_var, anchor="w", foreground="#993C1D").pack(fill="x", pady=(4, 0))
|
||||
ttk.Label(lf1, textvariable=self.min_price_var, anchor="w",
|
||||
style="CardSub.TLabel").pack(fill="x", pady=(4, 0))
|
||||
|
||||
# 中:颜色多选(点击即多选)+ 添加按钮
|
||||
lf2 = ttk.Frame(sel)
|
||||
lf2.pack(side="left", padx=(10, 4))
|
||||
ttk.Label(lf2, text="当前款号颜色(点击多选,显示色名/最低价):").pack(anchor="w")
|
||||
self.sku_list = tk.Listbox(lf2, selectmode="multiple", height=5, width=34, exportselection=False)
|
||||
self.sku_list.pack(side="left")
|
||||
b2 = ttk.Frame(lf2)
|
||||
b2.pack(side="left", padx=4)
|
||||
# 中:颜色多选 + 添加按钮
|
||||
lf2 = ttk.Frame(sel, style="Card.TFrame")
|
||||
lf2.pack(side="left", padx=(12, 4))
|
||||
ttk.Label(lf2, text="当前款号颜色(点击多选)", style="Card.TLabel").pack(anchor="w")
|
||||
self.sku_list = tk.Listbox(lf2, selectmode="multiple", height=6, width=38,
|
||||
exportselection=False, bg="#FFFFFF", fg=self._C_TEXT,
|
||||
selectbackground="#DBEAFE", selectforeground="#1E3A8A",
|
||||
highlightthickness=1, highlightbackground=self._C_BORDER,
|
||||
highlightcolor=self._C_PRIMARY, font=self._FONT_SM,
|
||||
relief="solid", borderwidth=1)
|
||||
self.sku_list.pack(side="left", pady=(4, 0))
|
||||
b2 = ttk.Frame(lf2, style="Card.TFrame")
|
||||
b2.pack(side="left", padx=6)
|
||||
ttk.Button(b2, text="全选", command=self._select_all_colors).pack(fill="x")
|
||||
ttk.Button(b2, text="清空", command=lambda: self.sku_list.selection_clear(0, "end")).pack(fill="x", pady=2)
|
||||
ttk.Button(b2, text="+添加选品", command=self._add_task).pack(fill="x", pady=(4, 0))
|
||||
ttk.Label(b2, text="该款SPU/设计数(1-N):", anchor="w").pack(fill="x", pady=(6, 0))
|
||||
ttk.Button(b2, text="+添加选品", style="AccentBtn.TButton",
|
||||
command=self._add_task).pack(fill="x", pady=(4, 0))
|
||||
ttk.Label(b2, text="该款设计数:", style="CardSub.TLabel").pack(fill="x", pady=(6, 0))
|
||||
self.count_var = tk.StringVar(value="10")
|
||||
ttk.Entry(b2, textvariable=self.count_var, width=6).pack(fill="x")
|
||||
|
||||
# 右:任务清单
|
||||
lf3 = ttk.Frame(sel)
|
||||
lf3 = ttk.Frame(sel, style="Card.TFrame")
|
||||
lf3.pack(side="left", padx=4)
|
||||
ttk.Label(lf3, text="选品任务清单:").pack(anchor="w")
|
||||
ttk.Label(lf3, text="选品任务清单", style="Card.TLabel").pack(anchor="w")
|
||||
self.task_stat_var = tk.StringVar(value="0 个任务")
|
||||
ttk.Label(lf3, textvariable=self.task_stat_var, anchor="w",
|
||||
foreground="#993C1D").pack(anchor="w")
|
||||
self.task_list = tk.Listbox(lf3, height=5, width=40, exportselection=False)
|
||||
self.task_list.pack(side="left")
|
||||
b3 = ttk.Frame(lf3)
|
||||
b3.pack(side="left", padx=4)
|
||||
style="Accent.TLabel").pack(anchor="w")
|
||||
self.task_list = tk.Listbox(lf3, height=6, width=44, exportselection=False,
|
||||
bg="#FFFFFF", fg=self._C_TEXT,
|
||||
selectbackground="#DBEAFE", selectforeground="#1E3A8A",
|
||||
highlightthickness=1, highlightbackground=self._C_BORDER,
|
||||
highlightcolor=self._C_PRIMARY, font=self._FONT_SM,
|
||||
relief="solid", borderwidth=1)
|
||||
self.task_list.pack(side="left", pady=(4, 0))
|
||||
b3 = ttk.Frame(lf3, style="Card.TFrame")
|
||||
b3.pack(side="left", padx=6)
|
||||
ttk.Button(b3, text="删选中", command=self._del_task).pack(fill="x")
|
||||
ttk.Button(b3, text="清空", command=self._clear_tasks).pack(fill="x", pady=2)
|
||||
|
||||
# 加价百分比(供后续定价使用)+ 货号前缀
|
||||
lf4 = ttk.Frame(sel)
|
||||
lf4.pack(side="left", padx=(12, 0))
|
||||
ttk.Label(lf4, text="加价%:").pack(anchor="w")
|
||||
# 右侧:加价 / 货号前缀
|
||||
lf4 = ttk.Frame(sel, style="Card.TFrame")
|
||||
lf4.pack(side="left", padx=(16, 0))
|
||||
ttk.Label(lf4, text="加价 %", style="Card.TLabel").pack(anchor="w")
|
||||
self.markup_var = tk.StringVar(value="0")
|
||||
ttk.Entry(lf4, textvariable=self.markup_var, width=7).pack(anchor="w", pady=(2, 0))
|
||||
ttk.Label(lf4, text="按 SKU 最低价加价", anchor="w", foreground="#5F5E5A").pack(anchor="w", pady=(4, 0))
|
||||
ttk.Entry(lf4, textvariable=self.markup_var, width=7).pack(anchor="w", pady=(4, 0))
|
||||
ttk.Label(lf4, text="按 SKU 最低价加价", style="CardSub.TLabel").pack(anchor="w", pady=(4, 0))
|
||||
|
||||
lf5 = ttk.Frame(sel)
|
||||
lf5.pack(side="left", padx=(12, 0))
|
||||
ttk.Label(lf5, text="货号前缀:").pack(anchor="w")
|
||||
lf5 = ttk.Frame(sel, style="Card.TFrame")
|
||||
lf5.pack(side="left", padx=(16, 0))
|
||||
ttk.Label(lf5, text="货号前缀", style="Card.TLabel").pack(anchor="w")
|
||||
self.code_prefix_var = tk.StringVar(value="DG")
|
||||
ttk.Entry(lf5, textvariable=self.code_prefix_var, width=8).pack(anchor="w", pady=(2, 0))
|
||||
ttk.Label(lf5, text="前缀+3位计数(000起)\nOSS: 国家/时间戳/货号_4位随机", anchor="w",
|
||||
foreground="#5F5E5A").pack(anchor="w", pady=(4, 0))
|
||||
ttk.Entry(lf5, textvariable=self.code_prefix_var, width=8).pack(anchor="w", pady=(4, 0))
|
||||
ttk.Label(lf5, text="前缀+3位计数(000起)", style="CardSub.TLabel").pack(anchor="w", pady=(4, 0))
|
||||
|
||||
self._sku_map = {}
|
||||
self._tasks: list = []
|
||||
self._reload_spus()
|
||||
|
||||
bar = ttk.Frame(self, padding=(8, 0))
|
||||
# —— 操作栏 ——
|
||||
bar = ttk.Frame(self, padding=(12, 6))
|
||||
bar.pack(fill="x")
|
||||
self.run_btn = ttk.Button(bar, text="▶ 运行", style="Primary.TButton", command=self._on_run)
|
||||
self.run_btn.pack(side="left")
|
||||
self.fetch_btn = ttk.Button(bar, text="📥 采集热点", command=self._on_fetch)
|
||||
self.fetch_btn.pack(side="left")
|
||||
self.run_btn = ttk.Button(bar, text="▶ 运行", command=self._on_run)
|
||||
self.run_btn.pack(side="left", padx=6)
|
||||
ttk.Button(bar, text="打开产物目录", command=self._open_output).pack(side="left", padx=8)
|
||||
ttk.Button(bar, text="打开日志目录", command=self._open_logs).pack(side="left")
|
||||
ttk.Button(bar, text="清空日志", command=self._clear_log).pack(side="left")
|
||||
self.fetch_btn.pack(side="left", padx=(10, 0))
|
||||
ttk.Button(bar, text="打开产物目录", command=self._open_output).pack(side="left", padx=(10, 0))
|
||||
ttk.Button(bar, text="打开日志目录", command=self._open_logs).pack(side="left", padx=(10, 0))
|
||||
ttk.Button(bar, text="清空日志", command=self._clear_log).pack(side="left", padx=(10, 0))
|
||||
|
||||
ttk.Label(self, text="运行日志:").pack(anchor="w", padx=8)
|
||||
self.log_text = tk.Text(self, height=8, state="disabled")
|
||||
self.log_text.pack(fill="x", padx=8)
|
||||
# —— 运行日志(深色终端风格)——
|
||||
log_card = ttk.LabelFrame(self, text="运行日志", style="Card.TLabelframe", padding=6)
|
||||
log_card.pack(fill="x", padx=12, pady=(4, 4))
|
||||
self.log_text = tk.Text(log_card, height=7, state="disabled", wrap="word",
|
||||
bg="#0F172A", fg="#E2E8F0", font=("Consolas", 9),
|
||||
relief="flat", padx=8, pady=6, insertbackground="#E2E8F0")
|
||||
self.log_text.pack(fill="both", expand=True)
|
||||
|
||||
ttk.Label(self, text="设计简报(双击查看完整提示词):").pack(anchor="w", padx=8)
|
||||
# —— 设计简报表格 ——
|
||||
tree_card = ttk.LabelFrame(self, text="设计简报(双击查看完整提示词)",
|
||||
style="Card.TLabelframe", padding=6)
|
||||
tree_card.pack(fill="both", expand=True, padx=12, pady=(4, 10))
|
||||
cols = ("country", "topic", "category", "risk", "score")
|
||||
self.tree = ttk.Treeview(self, columns=cols, show="headings", height=12)
|
||||
self.tree = ttk.Treeview(tree_card, columns=cols, show="headings", height=12)
|
||||
for col, title, w, anchor in zip(
|
||||
cols, ["国家", "热点词", "类别", "风险", "分数"],
|
||||
[52, 340, 100, 70, 64],
|
||||
[70, 420, 180, 90, 80],
|
||||
["center", "w", "w", "center", "center"],
|
||||
):
|
||||
self.tree.heading(col, text=title)
|
||||
self.tree.column(col, width=w, anchor=anchor)
|
||||
self.tree.pack(fill="both", expand=True, padx=8, pady=(2, 8))
|
||||
self.tree.pack(fill="both", expand=True)
|
||||
self.tree.bind("<Double-1>", self._show_detail)
|
||||
|
||||
def _log(self, s: str):
|
||||
@@ -707,12 +809,6 @@ class App(tk.Tk):
|
||||
if colors:
|
||||
self.sku_list.selection_set(0) # 默认选中第一个颜色
|
||||
|
||||
def _current_spu(self):
|
||||
sel = self.spu_list.curselection()
|
||||
if sel and sel[0] < len(self._spu_list):
|
||||
return self._spu_list[sel[0]]["code"]
|
||||
return ""
|
||||
|
||||
def _add_task(self):
|
||||
"""把当前选中的【款号】加入任务清单(可多选、可重复新增)。
|
||||
每个款号一条「集合」:skus=选中颜色集合(多色用逗号分隔),
|
||||
@@ -863,7 +959,7 @@ class App(tk.Tk):
|
||||
self.run_btn.config(state="disabled")
|
||||
threading.Thread(
|
||||
target=fetch_keywords,
|
||||
args=(self.country_var.get(), self.provider_var.get(), ms, self._q,
|
||||
args=(self.country_var.get(), "openai", ms, self._q,
|
||||
self._oai_cfg()),
|
||||
daemon=True,
|
||||
).start()
|
||||
@@ -1023,18 +1119,12 @@ class App(tk.Tk):
|
||||
messagebox.showerror("参数错误", "加价%不能为负数")
|
||||
return
|
||||
|
||||
# 任务清单优先;空则用当前选中款号+颜色(保留旧行为)
|
||||
# 任务清单必须通过「+添加选品」显式添加后才允许运行;
|
||||
# 不允许用当前选中款号+颜色兜底(否则未添加选品也能启动,会按数量扩展生成大量简报)。
|
||||
tasks = list(self._tasks)
|
||||
if not tasks:
|
||||
spu = self._current_spu()
|
||||
if not spu:
|
||||
messagebox.showwarning("提示", "请先添加选品(款号 → 颜色 → +添加选品)")
|
||||
return
|
||||
sel_colors = [self.sku_list.get(i).split(" ")[0] for i in self.sku_list.curselection()]
|
||||
if not sel_colors:
|
||||
messagebox.showwarning("提示", "请至少选择一个颜色")
|
||||
return
|
||||
tasks = [{"spu": spu, "skus": ",".join(sel_colors)}]
|
||||
messagebox.showwarning("提示", "请先添加选品(款号 → 颜色 → +添加选品)")
|
||||
return
|
||||
|
||||
# ① 兜底校验:任务开始前检查所有款号颜色的 basemap 底图是否存在;
|
||||
# 存在缺失 → 直接报错提醒,不开始任何任务
|
||||
@@ -1063,7 +1153,7 @@ class App(tk.Tk):
|
||||
# —— 自定义图片模式硬校验:必须先选文件夹;有效图片数 ≥ 选品清单总数 ——
|
||||
if not custom_dir:
|
||||
self._busy = False
|
||||
self.run_btn.config(state="normal", text="运行")
|
||||
self.run_btn.config(state="normal", text="▶ 运行")
|
||||
self.fetch_btn.config(state="normal")
|
||||
messagebox.showwarning("自定义图片模式", "请先点击「📁 选图片文件夹…」选择上传的图片文件夹")
|
||||
return
|
||||
@@ -1076,7 +1166,7 @@ class App(tk.Tk):
|
||||
ok, valid_n, msg = False, 0, f"校验失败: {e}"
|
||||
if not ok:
|
||||
self._busy = False
|
||||
self.run_btn.config(state="normal", text="运行")
|
||||
self.run_btn.config(state="normal", text="▶ 运行")
|
||||
self.fetch_btn.config(state="normal")
|
||||
messagebox.showerror("自定义图片数量校验未通过", msg)
|
||||
self._log("[UI] 自定义图片数量校验未通过,任务未运行: " + msg + "\n")
|
||||
@@ -1084,7 +1174,7 @@ class App(tk.Tk):
|
||||
self._log(f"[UI] 自定义图片数量校验通过:有效图片 {valid_n} 张 ≥ 选品清单 {expanded}\n")
|
||||
threading.Thread(
|
||||
target=run_pinterest_pipeline,
|
||||
args=(countries, self.provider_var.get(), self._q,
|
||||
args=(countries, "openai", self._q,
|
||||
tasks, spu_count, self._oai_cfg(), markup,
|
||||
self.code_prefix_var.get().strip() or "DG",
|
||||
self.template_path_var.get().strip(),
|
||||
@@ -1094,7 +1184,7 @@ class App(tk.Tk):
|
||||
else:
|
||||
threading.Thread(
|
||||
target=run_pipeline,
|
||||
args=(countries, self.provider_var.get(), ms, self._q,
|
||||
args=(countries, "openai", ms, self._q,
|
||||
"", "", spu_count, tasks, self._oai_cfg(), markup,
|
||||
self.code_prefix_var.get().strip() or "DG",
|
||||
self.template_path_var.get().strip()),
|
||||
@@ -1176,7 +1266,7 @@ class App(tk.Tk):
|
||||
("国家", "country"), ("热点词", "topic"), ("类别", "design_category"),
|
||||
("风险", "risk_level"), ("风险原因", "risk_reasons"), ("概念", "concept"),
|
||||
("主体 motif", "motif"), ("艺术风格", "art_style"), ("配色", "color_palette"),
|
||||
("构图", "composition"), ("负向", "negative_prompt"),
|
||||
("构图", "composition"),
|
||||
("image_prompt", "image_prompt"), ("composite_prompt", "composite_prompt"),
|
||||
]:
|
||||
v = b.get(k)
|
||||
|
||||
Reference in New Issue
Block a user