- 修复删除类目时删除错误行的问题 - 移除"快速添加标准类目"功能 - 统一界面文案:"自定义类目"改为"选择类目" - 简化按钮文案:"新增/编辑款号"改为"新增款号" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
750 lines
32 KiB
Python
750 lines
32 KiB
Python
"""
|
||
服装管理模块 - 处理服装款式和材料用量管理
|
||
"""
|
||
|
||
import os
|
||
from datetime import datetime
|
||
from PIL import Image
|
||
from PyQt5.QtWidgets import (
|
||
QDialog, QVBoxLayout, QHBoxLayout, QGridLayout, QLabel, QLineEdit,
|
||
QPushButton, QComboBox, QTableWidget, QTableWidgetItem, QHeaderView,
|
||
QMessageBox, QFileDialog, QDoubleSpinBox, QWidget, QCompleter
|
||
)
|
||
from PyQt5.QtCore import Qt, QTimer, QSortFilterProxyModel
|
||
from PyQt5.QtGui import QPixmap
|
||
from database import get_db_connection
|
||
|
||
|
||
class SearchableComboBox(QComboBox):
|
||
"""支持模糊搜索的下拉框 - 基于 QSortFilterProxyModel 和 QCompleter"""
|
||
|
||
def __init__(self, parent=None):
|
||
super(SearchableComboBox, self).__init__(parent)
|
||
|
||
self.setFocusPolicy(Qt.StrongFocus)
|
||
self.setEditable(True)
|
||
self.setInsertPolicy(QComboBox.NoInsert)
|
||
|
||
# 添加过滤模型来过滤匹配项
|
||
self.pFilterModel = QSortFilterProxyModel(self)
|
||
self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive)
|
||
self.pFilterModel.setSourceModel(self.model())
|
||
|
||
# 添加自动完成器,使用过滤模型
|
||
self.completer = QCompleter(self.pFilterModel, self)
|
||
# 始终显示所有(过滤后的)完成项
|
||
self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
|
||
self.setCompleter(self.completer)
|
||
|
||
# 连接信号
|
||
self.lineEdit().textEdited.connect(self.pFilterModel.setFilterFixedString)
|
||
self.completer.activated.connect(self.on_completer_activated)
|
||
|
||
# 当从自动完成器中选择项时,从组合框中选择对应的项
|
||
def on_completer_activated(self, text):
|
||
if text:
|
||
index = self.findText(text)
|
||
self.setCurrentIndex(index)
|
||
self.activated[str].emit(self.itemText(index))
|
||
|
||
# 当模型改变时,同时更新过滤器和自动完成器的模型
|
||
def setModel(self, model):
|
||
super(SearchableComboBox, self).setModel(model)
|
||
self.pFilterModel.setSourceModel(model)
|
||
self.completer.setModel(self.pFilterModel)
|
||
|
||
# 当模型列改变时,同时更新过滤器和自动完成器的模型列
|
||
def setModelColumn(self, column):
|
||
self.completer.setCompletionColumn(column)
|
||
self.pFilterModel.setFilterKeyColumn(column)
|
||
super(SearchableComboBox, self).setModelColumn(column)
|
||
|
||
def setDefaultText(self, text):
|
||
"""设置默认文本 - 兼容方法"""
|
||
line_edit = self.lineEdit()
|
||
if line_edit:
|
||
line_edit.blockSignals(True)
|
||
line_edit.setText(text)
|
||
line_edit.blockSignals(False)
|
||
# 尝试找到匹配的项
|
||
index = self.findText(text)
|
||
if index >= 0:
|
||
self.setCurrentIndex(index)
|
||
else:
|
||
self.setCurrentIndex(-1)
|
||
|
||
class GarmentLibraryDialog(QDialog):
|
||
"""服装库管理对话框"""
|
||
|
||
def __init__(self, db_path):
|
||
super().__init__()
|
||
self.db_path = db_path
|
||
self.setWindowTitle("衣服款号管理")
|
||
self.resize(1300, 750)
|
||
|
||
self.setup_ui()
|
||
self.load_garments()
|
||
|
||
def setup_ui(self):
|
||
"""设置用户界面"""
|
||
layout = QVBoxLayout(self)
|
||
|
||
# 操作按钮区域
|
||
op_layout = QHBoxLayout()
|
||
op_layout.addWidget(QLabel("搜索款号:"))
|
||
self.search_input = QLineEdit()
|
||
self.search_input.textChanged.connect(self.load_garments)
|
||
op_layout.addWidget(self.search_input)
|
||
|
||
add_btn = QPushButton("新增款号")
|
||
add_btn.clicked.connect(self.edit_garment)
|
||
op_layout.addWidget(add_btn)
|
||
|
||
del_btn = QPushButton("删除选中款号")
|
||
del_btn.clicked.connect(self.delete_garment)
|
||
op_layout.addWidget(del_btn)
|
||
|
||
refresh_btn = QPushButton("刷新")
|
||
refresh_btn.clicked.connect(self.load_garments)
|
||
op_layout.addWidget(refresh_btn)
|
||
|
||
layout.addLayout(op_layout)
|
||
|
||
# 服装表格
|
||
self.garment_table = QTableWidget()
|
||
self.garment_table.setColumnCount(3)
|
||
self.garment_table.setHorizontalHeaderLabels(["款号", "类目数量", "款式图预览"])
|
||
self.garment_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||
self.garment_table.itemDoubleClicked.connect(self.edit_garment_from_table)
|
||
layout.addWidget(self.garment_table)
|
||
|
||
def get_conn(self):
|
||
"""获取数据库连接"""
|
||
return get_db_connection(self.db_path)
|
||
|
||
def load_garments(self):
|
||
"""加载服装列表"""
|
||
keyword = self.search_input.text().strip()
|
||
try:
|
||
with self.get_conn() as conn:
|
||
query = "SELECT style_number, image_path FROM garments"
|
||
params = []
|
||
if keyword:
|
||
query += " WHERE style_number LIKE ?"
|
||
params = ["%" + keyword + "%"]
|
||
query += " ORDER BY style_number"
|
||
|
||
cursor = conn.execute(query, params)
|
||
rows = cursor.fetchall()
|
||
|
||
self.garment_table.setRowCount(len(rows))
|
||
for i in range(len(rows)):
|
||
self.garment_table.setRowHeight(i, 140)
|
||
|
||
for row_idx, (style_number, image_path) in enumerate(rows):
|
||
self.garment_table.setItem(row_idx, 0, QTableWidgetItem(style_number))
|
||
|
||
# 查询材料数量
|
||
with self.get_conn() as conn:
|
||
cursor2 = conn.execute("SELECT COUNT(*) FROM garment_materials WHERE style_number = ?", (style_number,))
|
||
count = cursor2.fetchone()[0]
|
||
self.garment_table.setItem(row_idx, 1, QTableWidgetItem(str(count)))
|
||
|
||
# 显示图片预览
|
||
image_item = QTableWidgetItem()
|
||
image_item.setTextAlignment(Qt.AlignCenter)
|
||
|
||
if image_path and os.path.exists(image_path):
|
||
try:
|
||
pixmap = QPixmap(image_path).scaled(130, 130, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||
image_item.setData(Qt.DecorationRole, pixmap)
|
||
except:
|
||
image_item.setText("加载失败")
|
||
else:
|
||
image_item.setText("无图片")
|
||
|
||
self.garment_table.setItem(row_idx, 2, image_item)
|
||
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "加载失败", "错误: " + str(e))
|
||
|
||
def edit_garment_from_table(self):
|
||
"""从表格编辑服装"""
|
||
row = self.garment_table.currentRow()
|
||
if row >= 0:
|
||
style_number = self.garment_table.item(row, 0).text()
|
||
self.edit_garment(style_number)
|
||
|
||
def edit_garment(self, style_number=None):
|
||
"""编辑服装"""
|
||
dialog = GarmentEditDialog(self.db_path, style_number)
|
||
if dialog.exec_():
|
||
self.load_garments()
|
||
|
||
def delete_garment(self):
|
||
"""删除服装"""
|
||
row = self.garment_table.currentRow()
|
||
if row < 0:
|
||
QMessageBox.warning(self, "提示", "请先选中一款号")
|
||
return
|
||
style_number = self.garment_table.item(row, 0).text()
|
||
reply = QMessageBox.question(self, "确认", f"删除款号 '{style_number}' 及其所有信息?")
|
||
if reply == QMessageBox.Yes:
|
||
try:
|
||
with self.get_conn() as conn:
|
||
conn.execute("DELETE FROM garment_materials WHERE style_number = ?", (style_number,))
|
||
conn.execute("DELETE FROM garments WHERE style_number = ?", (style_number,))
|
||
conn.commit()
|
||
self.load_garments()
|
||
QMessageBox.information(self, "成功", "删除完成")
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "错误", "删除失败: " + str(e))
|
||
|
||
|
||
class GarmentEditDialog(QDialog):
|
||
"""服装编辑对话框"""
|
||
|
||
def __init__(self, db_path, style_number=None):
|
||
super().__init__()
|
||
self.db_path = db_path
|
||
self.style_number = style_number
|
||
self.current_image_path = None
|
||
self.setWindowTitle("编辑款号" if style_number else "新增款号")
|
||
self.resize(1300, 850)
|
||
|
||
self.setup_ui()
|
||
if style_number:
|
||
self.load_garment_data()
|
||
|
||
def setup_ui(self):
|
||
"""设置用户界面"""
|
||
layout = QVBoxLayout(self)
|
||
|
||
# 基本信息区域
|
||
basic_layout = QGridLayout()
|
||
basic_layout.addWidget(QLabel("款号:"), 0, 0, Qt.AlignRight)
|
||
self.style_input = QLineEdit()
|
||
if self.style_number:
|
||
self.style_input.setText(self.style_number)
|
||
self.style_input.setEnabled(not self.style_number)
|
||
basic_layout.addWidget(self.style_input, 0, 1)
|
||
|
||
basic_layout.addWidget(QLabel("款式图:"), 1, 0, Qt.AlignRight)
|
||
self.image_label = QLabel("无图片")
|
||
self.image_label.setFixedSize(300, 300)
|
||
self.image_label.setStyleSheet("border: 1px solid gray;")
|
||
self.image_label.setAlignment(Qt.AlignCenter)
|
||
self.image_label.setScaledContents(True)
|
||
basic_layout.addWidget(self.image_label, 1, 1, 5, 1)
|
||
|
||
upload_btn = QPushButton("上传/更换图片")
|
||
upload_btn.clicked.connect(self.upload_image)
|
||
basic_layout.addWidget(upload_btn, 1, 2)
|
||
|
||
layout.addLayout(basic_layout)
|
||
|
||
# 材料用量区域
|
||
layout.addWidget(QLabel("材料用量(单件):"))
|
||
|
||
self.material_table = QTableWidget()
|
||
self.material_table.setColumnCount(6)
|
||
self.material_table.setHorizontalHeaderLabels(["类目", "类型", "型号", "单件用量", "单位", "删除"])
|
||
self.material_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||
layout.addWidget(self.material_table)
|
||
|
||
# 按钮区域
|
||
btn_layout = QHBoxLayout()
|
||
add_custom_btn = QPushButton("添加类目")
|
||
add_custom_btn.clicked.connect(lambda: self.add_material_row())
|
||
btn_layout.addWidget(add_custom_btn)
|
||
|
||
layout.addLayout(btn_layout)
|
||
|
||
# 保存/取消按钮
|
||
buttons = QHBoxLayout()
|
||
save_btn = QPushButton("保存")
|
||
save_btn.clicked.connect(self.save_garment)
|
||
buttons.addWidget(save_btn)
|
||
|
||
cancel_btn = QPushButton("取消")
|
||
cancel_btn.clicked.connect(self.reject)
|
||
buttons.addWidget(cancel_btn)
|
||
|
||
layout.addLayout(buttons)
|
||
|
||
def get_conn(self):
|
||
"""获取数据库连接"""
|
||
return get_db_connection(self.db_path)
|
||
|
||
def upload_image(self):
|
||
"""上传图片"""
|
||
file_path, _ = QFileDialog.getOpenFileName(self, "选择图片", "", "Images (*.png *.jpg *.jpeg *.bmp)")
|
||
if file_path:
|
||
try:
|
||
img = Image.open(file_path).convert("RGB")
|
||
img.thumbnail((800, 800))
|
||
os.makedirs("images", exist_ok=True)
|
||
filename = os.path.basename(file_path)
|
||
save_path = os.path.join("images", filename)
|
||
img.save(save_path, "JPEG", quality=85)
|
||
|
||
self.current_image_path = save_path
|
||
pixmap = QPixmap(save_path).scaled(300, 300, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||
self.image_label.setPixmap(pixmap)
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "错误", "上传图片失败: " + str(e))
|
||
|
||
def load_garment_data(self):
|
||
"""加载服装数据"""
|
||
try:
|
||
with self.get_conn() as conn:
|
||
cursor = conn.execute("SELECT image_path FROM garments WHERE style_number = ?", (self.style_number,))
|
||
row = cursor.fetchone()
|
||
if row and row[0] and os.path.exists(row[0]):
|
||
self.current_image_path = row[0]
|
||
pixmap = QPixmap(row[0]).scaled(300, 300, Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||
self.image_label.setPixmap(pixmap)
|
||
self.load_materials()
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "错误", "加载失败: " + str(e))
|
||
|
||
def load_materials(self):
|
||
"""加载材料列表"""
|
||
try:
|
||
with self.get_conn() as conn:
|
||
cursor = conn.execute("SELECT category, fabric_type, model, usage_per_piece, unit FROM garment_materials WHERE style_number = ? ORDER BY id", (self.style_number,))
|
||
for category, fabric_type, model, usage, unit in cursor.fetchall():
|
||
# 直接使用数据库中的三个字段
|
||
display_category = category or ""
|
||
display_type = fabric_type or ""
|
||
display_model = model or ""
|
||
|
||
self.add_material_row(display_category, display_type, usage or 0, unit or "米", display_model)
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "错误", "加载材料失败: " + str(e))
|
||
|
||
def add_material_row(self, category="", fabric_type="", usage=0.0, unit="米", model=""):
|
||
"""添加材料行"""
|
||
print("add_material_row", category, fabric_type, usage, unit, model)
|
||
row = self.material_table.rowCount()
|
||
self.material_table.insertRow(row)
|
||
|
||
# 列0: 类目下拉框
|
||
cat_combo = QComboBox()
|
||
cat_combo.setEditable(False)
|
||
# 最后添加自定义选项
|
||
cat_combo.addItem("—— 选择类目 ——")
|
||
|
||
# 先添加所有类目选项
|
||
try:
|
||
with self.get_conn() as conn:
|
||
cursor = conn.execute("""
|
||
SELECT DISTINCT category
|
||
FROM fabrics
|
||
WHERE category IS NOT NULL AND category != ''
|
||
ORDER BY category
|
||
""")
|
||
categories = set()
|
||
for cat_row in cursor.fetchall():
|
||
if cat_row[0] and cat_row[0].strip():
|
||
categories.add(cat_row[0])
|
||
|
||
for cat in sorted(categories):
|
||
cat_combo.addItem(cat)
|
||
except:
|
||
# 如果查询失败,使用默认类目
|
||
cat_combo.addItem("布料")
|
||
cat_combo.addItem("辅料")
|
||
cat_combo.addItem("其他")
|
||
|
||
if category:
|
||
cat_combo.setCurrentText(category)
|
||
else:
|
||
# 如果没有指定类目,默认选择"—— 选择类目 ——"(索引0)
|
||
print("没有指定类目,默认选择'—— 选择类目 ——'(索引0)")
|
||
print(cat_combo.count())
|
||
print(cat_combo.currentIndex())
|
||
# 打印所有选项
|
||
for i in range(cat_combo.count()):
|
||
print(cat_combo.itemText(i))
|
||
cat_combo.setCurrentIndex(0)
|
||
|
||
self.material_table.setCellWidget(row, 0, cat_combo)
|
||
|
||
# 列1: 类型下拉框
|
||
type_combo = QComboBox()
|
||
type_combo.setEditable(False)
|
||
# 最后添加选择提示
|
||
type_combo.addItem("—— 选择类型 ——")
|
||
# 先添加所有类型选项
|
||
try:
|
||
with self.get_conn() as conn:
|
||
cursor = conn.execute("""
|
||
SELECT DISTINCT fabric_type
|
||
FROM fabrics
|
||
WHERE fabric_type IS NOT NULL AND fabric_type != ''
|
||
ORDER BY fabric_type
|
||
""")
|
||
|
||
types = cursor.fetchall()
|
||
for type_row in types:
|
||
if type_row[0] and type_row[0].strip():
|
||
type_combo.addItem(type_row[0])
|
||
except:
|
||
pass
|
||
|
||
|
||
if fabric_type:
|
||
type_combo.setCurrentText(fabric_type)
|
||
else:
|
||
# 如果没有指定类型,默认选择"—— 选择类型 ——"(索引0)
|
||
print("没有指定类型,默认选择'—— 选择类型 ——'(索引0)")
|
||
print(type_combo.count())
|
||
print(type_combo.currentIndex())
|
||
# 打印所有选项
|
||
for i in range(type_combo.count()):
|
||
print(type_combo.itemText(i))
|
||
type_combo.setCurrentIndex(0)
|
||
|
||
self.material_table.setCellWidget(row, 1, type_combo)
|
||
|
||
# 列2: 型号下拉框(支持模糊搜索)
|
||
model_combo = SearchableComboBox()
|
||
model_combo.addItem("—— 选择型号 ——")
|
||
# 先将下拉框添加到表格中,这样update_model_combo_range才能获取到类目和类型
|
||
self.material_table.setCellWidget(row, 2, model_combo)
|
||
|
||
# 初始化时根据类目和类型加载对应的型号
|
||
self.update_model_combo_range(model_combo, row)
|
||
|
||
# 列3: 单件用量
|
||
usage_spin = QDoubleSpinBox()
|
||
usage_spin.setRange(0, 1000)
|
||
usage_spin.setValue(usage)
|
||
usage_spin.setDecimals(3)
|
||
self.material_table.setCellWidget(row, 3, usage_spin)
|
||
|
||
# 列4: 单位
|
||
unit_combo = QComboBox()
|
||
unit_combo.setEditable(True)
|
||
unit_combo.addItems(["米", "码", "公斤", "一对", "个", "条"])
|
||
unit_combo.setCurrentText(unit)
|
||
self.material_table.setCellWidget(row, 4, unit_combo)
|
||
|
||
# 列5: 删除按钮
|
||
del_btn = QPushButton("删除")
|
||
del_btn.clicked.connect(lambda: self.delete_material_row(del_btn))
|
||
self.material_table.setCellWidget(row, 5, del_btn)
|
||
|
||
# 初始化类型和型号选项
|
||
# self.on_category_changed(cat_combo.currentText(), row)
|
||
|
||
# 如果没有选择具体类目,初始化时显示全部型号
|
||
# if cat_combo.currentText() == "—— 选择类目 ——":
|
||
# self.on_type_changed("—— 选择类型 ——", row)
|
||
|
||
# 如果有指定的型号,需要在初始化完成后设置
|
||
# if model:
|
||
# # 先设置类型(如果有的话)
|
||
# if fabric_type:
|
||
# type_combo.setCurrentText(fabric_type)
|
||
# self.on_type_changed(fabric_type, row)
|
||
|
||
# # 然后设置型号
|
||
# model_combo = self.material_table.cellWidget(row, 2)
|
||
# if model_combo:
|
||
# # 确保型号在选项列表中
|
||
# found = False
|
||
# for i in range(model_combo.count()):
|
||
# item_data = model_combo.itemData(i)
|
||
# item_text = model_combo.itemText(i)
|
||
# if item_data == model or item_text == model:
|
||
# model_combo.setCurrentIndex(i)
|
||
# found = True
|
||
# break
|
||
cat_combo.currentTextChanged.connect(lambda text, r=row: self.on_category_changed(text, r))
|
||
type_combo.currentTextChanged.connect(lambda text, r=row: self.on_type_changed(text, r))
|
||
# 当用户真正选择了某个型号(包括通过模糊搜索补全选择),再联动类目和类型
|
||
model_combo.activated[str].connect(lambda text, r=row: self.on_model_selected(text, r))
|
||
|
||
def delete_material_row(self, button):
|
||
"""删除材料行 - 通过按钮找到实际行号"""
|
||
for row in range(self.material_table.rowCount()):
|
||
if self.material_table.cellWidget(row, 5) == button:
|
||
self.material_table.removeRow(row)
|
||
break
|
||
|
||
def update_model_combo_range(self, model_combo, row):
|
||
print("update_model_combo_range", model_combo, row)
|
||
"""更新型号下拉框的搜索范围"""
|
||
cat_combo = self.material_table.cellWidget(row, 0)
|
||
type_combo = self.material_table.cellWidget(row, 1)
|
||
|
||
category_text = cat_combo.currentText() if cat_combo else ""
|
||
type_text = type_combo.currentText() if type_combo else ""
|
||
|
||
# 阻止信号防止触发on_model_selected
|
||
model_combo.blockSignals(True)
|
||
line_edit = model_combo.lineEdit()
|
||
if line_edit:
|
||
line_edit.blockSignals(True)
|
||
|
||
try:
|
||
with self.get_conn() as conn:
|
||
# 根据类目和类型构建查询条件
|
||
if category_text and category_text != "—— 选择类目 ——" and type_text and type_text != "—— 选择类型 ——":
|
||
# 同时根据类目和类型过滤
|
||
cursor = conn.execute("""
|
||
SELECT DISTINCT model, color, unit
|
||
FROM fabrics
|
||
WHERE category = ? AND fabric_type = ?
|
||
ORDER BY model
|
||
""", (category_text, type_text))
|
||
elif category_text and category_text != "—— 选择类目 ——":
|
||
# 只根据类目过滤
|
||
cursor = conn.execute("""
|
||
SELECT DISTINCT model, color, unit
|
||
FROM fabrics
|
||
WHERE category = ?
|
||
ORDER BY model
|
||
""", (category_text,))
|
||
elif type_text and type_text != "—— 选择类型 ——":
|
||
# 只根据类型过滤
|
||
cursor = conn.execute("""
|
||
SELECT DISTINCT model, color, unit
|
||
FROM fabrics
|
||
WHERE fabric_type = ?
|
||
ORDER BY model
|
||
""", (type_text,))
|
||
else:
|
||
# 显示所有型号
|
||
cursor = conn.execute("""
|
||
SELECT DISTINCT model, color, unit
|
||
FROM fabrics
|
||
ORDER BY model
|
||
""")
|
||
|
||
# 保存当前用户输入的文本(如果有)
|
||
current_text = line_edit.text() if line_edit else ""
|
||
cursor_position = line_edit.cursorPosition() if line_edit else 0
|
||
print("current_text", current_text)
|
||
print("cursor_position", cursor_position)
|
||
# 清空并重新填充型号下拉框
|
||
model_combo.clear()
|
||
models = cursor.fetchall()
|
||
for model_row in models:
|
||
model, color, unit = model_row
|
||
# 显示格式:型号-颜色(如果有颜色的话)
|
||
display_text = model
|
||
if color and color.strip():
|
||
display_text = f"{model}-{color}"
|
||
model_combo.addItem(display_text, model)
|
||
|
||
# 恢复用户输入的文本,如果没有输入则设置默认文本
|
||
if line_edit:
|
||
if current_text and current_text != "—— 选择型号 ——":
|
||
line_edit.setText(current_text)
|
||
line_edit.setCursorPosition(cursor_position)
|
||
else:
|
||
if isinstance(model_combo, SearchableComboBox):
|
||
model_combo.setDefaultText("—— 选择型号 ——")
|
||
else:
|
||
model_combo.setCurrentIndex(0)
|
||
except Exception as e:
|
||
pass
|
||
finally:
|
||
if line_edit:
|
||
line_edit.blockSignals(False)
|
||
model_combo.blockSignals(False)
|
||
|
||
def on_category_changed(self, category_text, row):
|
||
"""当类目改变时,更新类型下拉框"""
|
||
print("on_category_changed", category_text, row)
|
||
type_combo = self.material_table.cellWidget(row, 1)
|
||
model_combo = self.material_table.cellWidget(row, 2)
|
||
|
||
# 清空类型下拉框
|
||
type_combo.clear()
|
||
type_combo.addItem("—— 选择类型 ——")
|
||
|
||
try:
|
||
with self.get_conn() as conn:
|
||
# 加载所有类型
|
||
if category_text and category_text != "—— 选择类目 ——":
|
||
# 如果选择了具体类目,则过滤
|
||
cursor = conn.execute("""
|
||
SELECT DISTINCT fabric_type
|
||
FROM fabrics
|
||
WHERE category = ? AND fabric_type IS NOT NULL AND fabric_type != ''
|
||
ORDER BY fabric_type
|
||
""", (category_text,))
|
||
else:
|
||
# 加载所有类型
|
||
cursor = conn.execute("""
|
||
SELECT DISTINCT fabric_type
|
||
FROM fabrics
|
||
WHERE fabric_type IS NOT NULL AND fabric_type != ''
|
||
ORDER BY fabric_type
|
||
""")
|
||
|
||
types = cursor.fetchall()
|
||
for type_row in types:
|
||
if type_row[0] and type_row[0].strip():
|
||
type_combo.addItem(type_row[0])
|
||
|
||
# 连接类型改变事件
|
||
type_combo.currentTextChanged.connect(lambda text, r=row: self.on_type_changed(text, r))
|
||
|
||
# 更新型号下拉框的搜索范围
|
||
self.update_model_combo_range(model_combo, row)
|
||
except Exception as e:
|
||
pass
|
||
|
||
def on_type_changed(self, type_text, row):
|
||
"""当类型改变时,更新类目和型号下拉框"""
|
||
print("on_type_changed", type_text, row)
|
||
cat_combo = self.material_table.cellWidget(row, 0)
|
||
model_combo = self.material_table.cellWidget(row, 2)
|
||
|
||
if not model_combo:
|
||
return
|
||
|
||
# 如果选择了具体类型,自动选中该类型对应的类目
|
||
if cat_combo and type_text and type_text != "—— 选择类型 ——":
|
||
try:
|
||
with self.get_conn() as conn:
|
||
# 查询该类型对应的类目
|
||
cursor = conn.execute("""
|
||
SELECT DISTINCT category
|
||
FROM fabrics
|
||
WHERE fabric_type = ? AND category IS NOT NULL AND category != ''
|
||
ORDER BY category
|
||
LIMIT 1
|
||
""", (type_text,))
|
||
row_db = cursor.fetchone()
|
||
if row_db and row_db[0]:
|
||
category = row_db[0].strip()
|
||
# 在类目下拉框中查找并选中该类目
|
||
index = cat_combo.findText(category)
|
||
if index >= 0:
|
||
cat_combo.blockSignals(True)
|
||
cat_combo.setCurrentIndex(index)
|
||
cat_combo.blockSignals(False)
|
||
except Exception as e:
|
||
pass
|
||
|
||
# 更新型号下拉框的搜索范围
|
||
self.update_model_combo_range(model_combo, row)
|
||
|
||
def on_model_selected(self, model_text, row):
|
||
"""当型号选择时,自动设置单位并填充类目和类型"""
|
||
print("on_model_selected", model_text, row)
|
||
# 先获取所有需要的控件
|
||
cat_combo = self.material_table.cellWidget(row, 0)
|
||
type_combo = self.material_table.cellWidget(row, 1)
|
||
model_combo = self.material_table.cellWidget(row, 2)
|
||
unit_combo = self.material_table.cellWidget(row, 4)
|
||
|
||
if not model_text or model_text == "—— 选择型号 ——":
|
||
# 空选择,不做联动
|
||
return
|
||
|
||
# 从显示文本中解析真实型号(例如 "M001-红" -> "M001")
|
||
display_text = model_text.strip()
|
||
base_model = display_text.split("-", 1)[0] if "-" in display_text else display_text
|
||
|
||
try:
|
||
with self.get_conn() as conn:
|
||
cursor = conn.execute(
|
||
"SELECT category, fabric_type, unit FROM fabrics WHERE model = ? AND (is_deleted IS NULL OR is_deleted = 0)",
|
||
(base_model,)
|
||
)
|
||
row_db = cursor.fetchone()
|
||
if row_db:
|
||
category, fabric_type, unit = row_db
|
||
|
||
# 自动填充单位
|
||
if unit:
|
||
unit_combo.setCurrentText(unit)
|
||
|
||
# 自动填充类目和类型,阻止信号以避免触发默认更新逻辑
|
||
if category:
|
||
# 阻止类目、类型和型号的信号,避免触发默认更新逻辑
|
||
cat_combo.blockSignals(True)
|
||
type_combo.blockSignals(True)
|
||
model_combo.blockSignals(True)
|
||
|
||
# 设置类目
|
||
cat_combo.setCurrentText(category)
|
||
|
||
# 更新类型下拉框选项(直接调用,不会触发信号因为已经阻止了)
|
||
self.on_category_changed(category, row)
|
||
|
||
# 重新阻止信号(因为on_category_changed内部可能恢复了信号)
|
||
model_combo.blockSignals(True)
|
||
|
||
# 设置类型
|
||
if fabric_type:
|
||
type_combo.setCurrentText(fabric_type)
|
||
|
||
# 恢复信号
|
||
cat_combo.blockSignals(False)
|
||
type_combo.blockSignals(False)
|
||
model_combo.blockSignals(False)
|
||
except:
|
||
pass
|
||
|
||
def save_garment(self):
|
||
"""保存服装"""
|
||
style_number = self.style_input.text().strip()
|
||
if not style_number:
|
||
QMessageBox.warning(self, "错误", "请输入款号")
|
||
return
|
||
|
||
try:
|
||
with self.get_conn() as conn:
|
||
conn.execute('INSERT OR REPLACE INTO garments (style_number, image_path) VALUES (?, ?)',
|
||
(style_number, self.current_image_path))
|
||
|
||
conn.execute("DELETE FROM garment_materials WHERE style_number = ?", (style_number,))
|
||
|
||
for row in range(self.material_table.rowCount()):
|
||
# 获取各列的值
|
||
category_widget = self.material_table.cellWidget(row, 0) # 类目
|
||
type_widget = self.material_table.cellWidget(row, 1) # 类型
|
||
model_widget = self.material_table.cellWidget(row, 2) # 型号
|
||
usage_widget = self.material_table.cellWidget(row, 3) # 单件用量
|
||
unit_widget = self.material_table.cellWidget(row, 4) # 单位
|
||
|
||
category = category_widget.currentText().strip()
|
||
fabric_type = type_widget.currentText().strip()
|
||
model = model_widget.currentText().strip()
|
||
|
||
# 处理类目和类型
|
||
if category == "—— 选择类目 ——":
|
||
category = ""
|
||
if fabric_type == "—— 选择类型 ——":
|
||
fabric_type = ""
|
||
|
||
# 如果选择了具体型号,获取型号的实际值
|
||
final_model = ""
|
||
if model and model != "—— 选择型号 ——":
|
||
model_data = model_widget.itemData(model_widget.currentIndex())
|
||
final_model = model_data if model_data else model
|
||
|
||
# 至少需要有类目或型号
|
||
if not category and not final_model:
|
||
continue
|
||
|
||
usage = usage_widget.value()
|
||
unit = unit_widget.currentText().strip() or "米"
|
||
|
||
# 分别存储类目、类型和型号到对应字段
|
||
conn.execute("INSERT INTO garment_materials (style_number, category, fabric_type, model, usage_per_piece, unit) VALUES (?, ?, ?, ?, ?, ?)",
|
||
(style_number, category, fabric_type, final_model, usage, unit))
|
||
|
||
conn.commit()
|
||
QMessageBox.information(self, "成功", "保存完成")
|
||
self.accept()
|
||
except Exception as e:
|
||
QMessageBox.critical(self, "错误", "保存失败: " + str(e)) |