""" 服装管理模块 - 处理服装款式和材料用量管理 """ 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, QStringListModel, QTimer from PyQt5.QtGui import QPixmap from database import get_db_connection class SearchableComboBox(QComboBox): """支持模糊搜索的下拉框""" def __init__(self, parent=None): super().__init__(parent) self.setEditable(True) self.setInsertPolicy(QComboBox.NoInsert) # 存储所有选项 self.all_items = [] self.all_data = [] self.is_filtering = False # 设置自动完成 self.completer = QCompleter(self) self.completer.setCompletionMode(QCompleter.PopupCompletion) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.completer.setFilterMode(Qt.MatchContains) self.setCompleter(self.completer) # 连接信号 self.lineEdit().textChanged.connect(self.on_text_changed) def addItem(self, text, userData=None): """添加选项""" # 临时断开信号连接,防止textChanged触发on_text_changed try: self.lineEdit().textChanged.disconnect() except TypeError: # 信号未连接或已被断开,忽略错误 pass super().addItem(text, userData) if text not in self.all_items: self.all_items.append(text) self.all_data.append(userData) self.update_completer() # 重新连接信号 try: self.lineEdit().textChanged.connect(self.on_text_changed) except: # 如果连接失败,忽略错误 pass def addItems(self, texts): """批量添加选项""" for text in texts: self.addItem(text) def clear(self): """清空所有选项""" if not self.is_filtering: super().clear() self.all_items.clear() self.all_data.clear() self.update_completer() def reset_items(self): """重置所有选项""" # 临时断开信号连接,防止textChanged触发on_text_changed try: self.lineEdit().textChanged.disconnect() except TypeError: # 信号未连接或已被断开,忽略错误 pass self.is_filtering = True super().clear() for i, item in enumerate(self.all_items): super().addItem(item, self.all_data[i] if i < len(self.all_data) else None) self.is_filtering = False # 重新连接信号 try: self.lineEdit().textChanged.connect(self.on_text_changed) except: # 如果连接失败,忽略错误 pass # # 确保当前索引设置为0(第一项) # if self.count() > 0: # super().setCurrentIndex(0) def update_completer(self): """更新自动完成列表""" model = QStringListModel(self.all_items) self.completer.setModel(model) def on_text_changed(self, text): """文本改变时的处理""" if self.is_filtering: return if not text or text in ["—— 选择型号 ——"]: self.reset_items() # 如果获得焦点且有选项,显示下拉列表 if self.hasFocus() and self.count() > 0: self.showPopup() return # 模糊搜索匹配 filtered_items = [] filtered_data = [] for i, item in enumerate(self.all_items): if text.lower() in item.lower(): filtered_items.append(item) filtered_data.append(self.all_data[i] if i < len(self.all_data) else None) # 更新下拉列表 self.is_filtering = True super().clear() for i, item in enumerate(filtered_items): super().addItem(item, filtered_data[i]) self.is_filtering = False # 如果有匹配项且获得焦点,显示下拉列表 if filtered_items and self.hasFocus(): self.showPopup() def setDefaultText(self, text): """设置默认文本""" # 临时断开信号连接,防止textChanged触发on_text_changed try: self.lineEdit().textChanged.disconnect() except TypeError: # 信号未连接或已被断开,忽略错误 pass self.lineEdit().setText(text) self.update_completer() # 重新连接信号 try: self.lineEdit().textChanged.connect(self.on_text_changed) except: # 如果连接失败,忽略错误 pass 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_default_btn = QPushButton("快速添加标准类目") add_default_btn.clicked.connect(self.add_default_categories) btn_layout.addWidget(add_default_btn) 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_default_categories(self): """添加默认类目""" defaults = [("A料", "", "米"), ("B料", "", "米"), ("C料", "", "米"), ("D料", "", "米"), ("花边", "", "码"), ("胸杯", "", "一对"), ("拉链", "", "个"), ("辅料", "", "个")] for cat, fabric_type, unit in defaults: self.add_material_row(cat, fabric_type, 0, unit) def add_material_row(self, category="", fabric_type="", usage=0.0, 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 = QComboBox() model_combo.setEditable(False) model_combo.addItem("—— 选择型号 ——") # 初始化时加载所有型号 try: with self.get_conn() as conn: cursor = conn.execute(""" SELECT DISTINCT model, color, unit FROM fabrics ORDER BY model """) 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) except Exception as e: pass if fabric_type: model_combo.setCurrentText(fabric_type) else: # 如果没有指定类型,默认选择"—— 选择类型 ——"(索引0) print("没有指定类型,默认选择'—— 选择类型 ——'(索引0)") print(model_combo.count()) print(model_combo.currentIndex()) # 打印所有选项 for i in range(model_combo.count()): print(model_combo.itemText(i)) model_combo.setCurrentIndex(0) self.material_table.setCellWidget(row, 2, model_combo) # 列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 _, r=row: self.material_table.removeRow(r)) 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.currentTextChanged.connect(lambda text, r=row: self.on_model_selected(text, r)) 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("—— 选择类型 ——") # 重新初始化型号下拉框,显示所有型号(阻止信号防止触发on_model_selected) model_combo.blockSignals(True) model_combo.clear() model_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)) # 加载该类目下的所有型号到型号下拉框 if category_text and category_text != "—— 自定义类目 ——": # 如果选择了具体类目,则只加载该类目下的型号 cursor = conn.execute(""" SELECT DISTINCT model, color, unit FROM fabrics WHERE category = ? ORDER BY model """, (category_text,)) else: # 如果是自定义类目,加载所有型号 cursor = conn.execute(""" SELECT DISTINCT model, color, unit FROM fabrics ORDER BY model """) 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) # 确保默认选中第一项("—— 选择型号 ——") model_combo.setCurrentIndex(0) except Exception as e: pass finally: # 恢复信号 model_combo.blockSignals(False) 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 = cursor.fetchone() if row and row[0]: category = row[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 # 重新初始化型号下拉框,显示该类型下的所有型号 model_combo.blockSignals(True) model_combo.clear() model_combo.addItem("—— 选择型号 ——") # 根据类型和类目过滤型号 try: with self.get_conn() as conn: # 获取当前选择的类目(可能已经更新) category_text = cat_combo.currentText() if cat_combo else "" # 构建查询条件 if type_text and type_text != "—— 选择类型 ——": # 如果选择了具体类型 if category_text and category_text != "—— 自定义类目 ——": # 同时根据类目和类型过滤 cursor = conn.execute(""" SELECT DISTINCT model, color, unit FROM fabrics WHERE category = ? AND fabric_type = ? ORDER BY model """, (category_text, type_text)) else: # 只根据类型过滤 cursor = conn.execute(""" SELECT DISTINCT model, color, unit FROM fabrics WHERE fabric_type = ? ORDER BY model """, (type_text,)) else: # 如果没有选择类型,根据类目过滤(如果有类目) if category_text and category_text != "—— 自定义类目 ——": cursor = conn.execute(""" SELECT DISTINCT model, color, unit FROM fabrics WHERE category = ? ORDER BY model """, (category_text,)) else: # 显示所有型号 cursor = conn.execute(""" SELECT DISTINCT model, color, unit FROM fabrics ORDER BY model """) 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) except Exception as e: pass # 确保默认选中第一项("—— 选择型号 ——") model_combo.setCurrentIndex(0) model_combo.blockSignals(False) 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 == "—— 选择型号 ——": # 获取当前类目和类型下的所有型号,阻止信号防止死循环 model_combo.blockSignals(True) try: model_combo.clear() model_combo.addItem("—— 选择型号 ——") try: with self.get_conn() as conn: if cat_combo.currentText() != "—— 自定义类目 ——" and type_combo.currentText() != "—— 选择类型 ——": cursor = conn.execute("SELECT DISTINCT model, color, unit FROM fabrics WHERE category = ? AND fabric_type = ? ORDER BY model", (cat_combo.currentText(), type_combo.currentText())) elif cat_combo.currentText() != "—— 自定义类目 ——" and type_combo.currentText() == "—— 选择类型 ——": cursor = conn.execute("SELECT DISTINCT model, color, unit FROM fabrics WHERE category = ? ORDER BY model", (cat_combo.currentText(),)) else: cursor = conn.execute("SELECT DISTINCT model, color, unit FROM fabrics ORDER BY model") 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) except Exception as e: pass model_combo.setCurrentIndex(0) finally: # 恢复信号 model_combo.blockSignals(False) return # 获取选中项的数据 current_index = model_combo.currentIndex() if current_index > 0: model = model_combo.itemData(current_index) if model: try: with self.get_conn() as conn: cursor = conn.execute("SELECT category, fabric_type, unit FROM fabrics WHERE model = ?", (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在finally中恢复了信号) # 这可以防止死循环 model_combo.blockSignals(True) # 重新设置选中的型号(因为on_category_changed会清空并重新加载型号下拉框) # 查找并选中对应的型号 for i in range(model_combo.count()): item_data = model_combo.itemData(i) if item_data == model: model_combo.setCurrentIndex(i) break # 设置类型 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))