feat(ai-outfit): apply approved relayout to the panel (§19.3)

- 数据源/输出 become inline label+path+browse rows (_inline_path_row);
  removed the left group boxes to free vertical space.
- 通用话术 + 最终提示词预览 enlarged and stretch with window height.
- 生成设置 grid -> 3 columns (2 rows); AI 模型 dropdown moved under it in the
  right column. Splitter sizes left360/center/right400; right minWidth 360.
- Verified offscreen against the mockup (real Qt grab); full suite (12) green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 11:08:25 +08:00
co-authored by Claude Opus 4.8
parent 8e6c347d91
commit 093e8f7f90
+27 -31
View File
@@ -162,7 +162,7 @@ class AiOutfitPanel(QWidget):
splitter.setStretchFactor(0, 0) splitter.setStretchFactor(0, 0)
splitter.setStretchFactor(1, 1) splitter.setStretchFactor(1, 1)
splitter.setStretchFactor(2, 0) splitter.setStretchFactor(2, 0)
splitter.setSizes([400, 600, 300]) splitter.setSizes([360, 540, 400])
outer.addWidget(splitter) outer.addWidget(splitter)
def _build_left(self): def _build_left(self):
@@ -171,37 +171,25 @@ class AiOutfitPanel(QWidget):
# AsNeeded (not AlwaysOff): if a child ever exceeds the column it scrolls # AsNeeded (not AlwaysOff): if a child ever exceeds the column it scrolls
# instead of being clipped under the center panel (docs/11 §10.1). # instead of being clipped under the center panel (docs/11 §10.1).
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
scroll.setMinimumWidth(360) scroll.setMinimumWidth(320)
inner = QWidget() inner = QWidget()
col = QVBoxLayout(inner) col = QVBoxLayout(inner)
col.setContentsMargins(12, 12, 12, 12) col.setContentsMargins(12, 12, 12, 12)
col.setSpacing(12) col.setSpacing(10)
# 数据源 # 数据源 / 输出:行内一行(标签 + 路径 + 浏览),省纵向空间给话术/预览
src = QGroupBox("数据源(Excel)")
sv = QVBoxLayout(src)
self._excel_edit = QLineEdit() self._excel_edit = QLineEdit()
self._excel_edit.setPlaceholderText("选择商品表 .xlsx") self._excel_edit.setPlaceholderText("选择商品表 .xlsx")
sv.addLayout(self._path_row(self._excel_edit, self._browse_excel)) col.addLayout(self._inline_path_row("Excel", self._excel_edit, self._browse_excel))
col.addWidget(src)
# 输出 + 模型
out = QGroupBox("输出")
ov = QVBoxLayout(out)
self._output_edit = QLineEdit() self._output_edit = QLineEdit()
self._output_edit.setPlaceholderText("默认:程序旁的「合并后的图片」") self._output_edit.setPlaceholderText("默认:程序旁的「合并后的图片」")
ov.addLayout(self._path_row(self._output_edit, self._browse_output)) col.addLayout(self._inline_path_row("输出", self._output_edit, self._browse_output))
ov.addWidget(QLabel("AI 模型"))
self._model_combo = QComboBox()
self._compact_combo(self._model_combo)
ov.addWidget(self._model_combo)
col.addWidget(out)
# 通用话术 # 通用话术(加大,随窗口高度拉伸)
prm = QGroupBox("通用话术") prm = QGroupBox("通用话术")
pv = QVBoxLayout(prm) pv = QVBoxLayout(prm)
self._prompt_edit = QPlainTextEdit() self._prompt_edit = QPlainTextEdit()
self._prompt_edit.setFixedHeight(120) self._prompt_edit.setMinimumHeight(150)
self._prompt_edit.textChanged.connect(self._refresh_preview) self._prompt_edit.textChanged.connect(self._refresh_preview)
pv.addWidget(self._prompt_edit) pv.addWidget(self._prompt_edit)
prow = QHBoxLayout() prow = QHBoxLayout()
@@ -213,9 +201,9 @@ class AiOutfitPanel(QWidget):
save_btn.clicked.connect(self._save_prompt) save_btn.clicked.connect(self._save_prompt)
prow.addWidget(save_btn) prow.addWidget(save_btn)
pv.addLayout(prow) pv.addLayout(prow)
col.addWidget(prm) col.addWidget(prm, stretch=2)
# 最终提示词预览(内嵌、实时;替换样本行的占位符) # 最终提示词预览(加大;内嵌、实时;替换样本行占位符 + 附加输出要求)
prev = QGroupBox("最终提示词预览") prev = QGroupBox("最终提示词预览")
pvw = QVBoxLayout(prev) pvw = QVBoxLayout(prev)
srow = QHBoxLayout() srow = QHBoxLayout()
@@ -231,11 +219,10 @@ class AiOutfitPanel(QWidget):
pvw.addWidget(self._preview_warn) pvw.addWidget(self._preview_warn)
self._preview_view = QPlainTextEdit() self._preview_view = QPlainTextEdit()
self._preview_view.setReadOnly(True) self._preview_view.setReadOnly(True)
self._preview_view.setMinimumHeight(140) self._preview_view.setMinimumHeight(200)
pvw.addWidget(self._preview_view) pvw.addWidget(self._preview_view)
col.addWidget(prev) col.addWidget(prev, stretch=3)
col.addStretch()
scroll.setWidget(inner) scroll.setWidget(inner)
return scroll return scroll
@@ -270,15 +257,20 @@ class AiOutfitPanel(QWidget):
grid = QGridLayout() grid = QGridLayout()
grid.setHorizontalSpacing(10) grid.setHorizontalSpacing(10)
grid.setVerticalSpacing(8) grid.setVerticalSpacing(8)
grid.setColumnStretch(0, 1) for c in range(3):
grid.setColumnStretch(1, 1) grid.setColumnStretch(c, 1)
for i, (label, widget) in enumerate(pairs): for i, (label, widget) in enumerate(pairs):
grid.addWidget(self._field(label, widget), i // 2, i % 2) grid.addWidget(self._field(label, widget), i // 3, i % 3)
gv.addLayout(grid) gv.addLayout(grid)
# AI 模型 下拉(移到生成设置下方)
self._model_combo = QComboBox()
self._compact_combo(self._model_combo)
gv.addWidget(self._field("AI 模型", self._model_combo))
return gen return gen
def _field(self, label_text, widget): def _field(self, label_text, widget):
"""A compact label-above-control cell for the 2-column settings grid.""" """A compact label-above-control cell for the settings grid."""
cell = QWidget() cell = QWidget()
v = QVBoxLayout(cell) v = QVBoxLayout(cell)
v.setContentsMargins(0, 0, 0, 0) v.setContentsMargins(0, 0, 0, 0)
@@ -294,8 +286,12 @@ class AiOutfitPanel(QWidget):
combo.setMinimumContentsLength(6) combo.setMinimumContentsLength(6)
combo.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Preferred) combo.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Preferred)
def _path_row(self, line_edit, on_browse): def _inline_path_row(self, label_text, line_edit, on_browse):
"""Label + path field + 浏览 button on one row."""
row = QHBoxLayout() row = QHBoxLayout()
lbl = QLabel(label_text)
lbl.setFixedWidth(40)
row.addWidget(lbl)
row.addWidget(line_edit, stretch=1) row.addWidget(line_edit, stretch=1)
btn = QPushButton("浏览…") btn = QPushButton("浏览…")
btn.clicked.connect(on_browse) btn.clicked.connect(on_browse)
@@ -333,7 +329,7 @@ class AiOutfitPanel(QWidget):
def _build_right(self): def _build_right(self):
wrap = QWidget() wrap = QWidget()
wrap.setMinimumWidth(300) wrap.setMinimumWidth(360)
col = QVBoxLayout(wrap) col = QVBoxLayout(wrap)
col.setContentsMargins(12, 12, 12, 12) col.setContentsMargins(12, 12, 12, 12)
col.setSpacing(10) col.setSpacing(10)