from PySide6.QtWidgets import (QGroupBox, QVBoxLayout, QHBoxLayout, QCheckBox,
                             QPushButton, QDialog, QLineEdit, QFontComboBox,
                             QFileDialog, QMessageBox, QLabel, QWidget, 
                             QTextBrowser, QApplication, QCompleter, QFrame,
                             QListWidgetItem, QListWidget, QAbstractItemView,
                             QStackedWidget, QStackedLayout, QStyledItemDelegate,
                             QTreeView, QSplitter, QFileSystemModel, QScrollArea,
                             QToolTip, QGridLayout, QSizePolicy, QProgressDialog,
                             QButtonGroup, QInputDialog, QMenu, QComboBox, QDialogButtonBox,
                             QTextEdit, QStyle)
from PySide6.QtCore import (QDateTime,QTimer,QDateTime,Signal,QSettings, QPoint, QEvent,
                            QSortFilterProxyModel, QSize, QDir, QMimeData, QRunnable, QObject,
                            QThreadPool, QPoint, QRect, QUrl)
from PySide6.QtGui import (QPixmap, QDragEnterEvent, QDropEvent, Qt, QIcon, 
                           QFontMetrics, QStandardItem, QStandardItemModel,
                           QAction, QColor, QImageReader, QDrag, QCursor,
                           QPainter)
import json, sys, os, re, time

from info_core.Img_edit import DefectMarkEditor
from info_core.defines import *

class ConfigComboBoxGroup(QGroupBox):
    """可复用的配置下拉框组件（带完整样式）"""
    def __init__(self, title, allow_add=True, parent=None, is_project = True):
        super().__init__(title, parent)
        self.allow_add = allow_add
        self.is_project = is_project
        self.config_dir = os.path.join(os.getcwd(), "config", title)
        
        # 确保配置目录存在
        os.makedirs(self.config_dir, exist_ok=True)
        
        # 设置组框样式
        self.setStyleSheet(GROUP_BOX_STYLE)
        self.setMinimumSize(GROUP_BOX_MIN_WIDTH, GROUP_BOX_MIN_HEIGHT)
        
        # 创建UI
        self.init_ui()
        self.load_config_files()
        
    def init_ui(self):
        """初始化UI组件"""
        layout = QVBoxLayout()
        layout.setSpacing(GROUP_BOX_SPACING)
        layout.setContentsMargins(*GROUP_BOX_MARGINS)
        
        self.combo_box_init()
        # 按钮布局
        btn_layout = QHBoxLayout()
        btn_layout.addStretch()  # 将按钮推到右侧
        
        self.modify_btn = self.create_button("修改")
        self.modify_btn.clicked.connect(self.on_modify_clicked)
        btn_layout.addWidget(self.modify_btn)
        
        if self.allow_add:
            self.add_btn = self.create_button("添加")
            self.add_btn.clicked.connect(self.on_add_clicked)
            btn_layout.addWidget(self.add_btn)
        
        self.delete_btn = self.create_button("删除")
        self.delete_btn.clicked.connect(self.on_delete_clicked)
        btn_layout.addWidget(self.delete_btn)
        
        layout.addWidget(self.combo_box)
        layout.addLayout(btn_layout)
        self.setLayout(layout)
    
    def combo_box_init(self):
        # 下拉框
        self.combo_box = QFontComboBox()
        self.combo_box.setEditable(True)
        
        # 代理模型（支持中文模糊匹配）
        self.proxy_model = QSortFilterProxyModel()
        self.proxy_model.setSourceModel(self.combo_box.model())
        self.proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive)
        
        # 补全器
        self.completer = QCompleter(self.proxy_model, self.combo_box)
        self.completer.setFilterMode(Qt.MatchContains)
        self.combo_box.setCompleter(self.completer)
        
    def create_button(self, text):
        """创建统一风格的按钮"""
        btn = QPushButton(text)
        btn.setStyleSheet(BUTTON_STYLE)
        btn.setFixedSize(BUTTON_WIDTH, BUTTON_HEIGHT)
        return btn
    
    def load_config_files(self):
        """加载config文件夹中的JSON文件"""
        self.combo_box.clear()
        
        # 获取所有.json文件
        config_files = [f for f in os.listdir(self.config_dir) 
                      if f.endswith('.json')]
        
        # 添加到下拉框（不带.json后缀）
        for file in config_files:
            self.combo_box.addItem(file[:-5])
    
    def on_modify_clicked(self):
        """修改按钮点击事件"""
        if self.is_project:
            project_widget = AddProjectDialog(self.config_dir, self, os.path.join(self.config_dir, self.combo_box.currentText() + ".json"))
            project_widget.exec()
        else:
            person_widget = AddPersonnelDialog(self.config_dir, self, os.path.join(self.config_dir, self.combo_box.currentText() + ".json"))
            person_widget.exec()
        self.load_config_files()
        
    def on_add_clicked(self):
        """添加按钮点击,创建默认ConfigEditDialog"""
        if self.is_project:
            project_widget = AddProjectDialog(self.config_dir, self)
            project_widget.exec()
        else:
            person_widget = AddPersonnelDialog(self.config_dir, self)
            person_widget.exec()
        self.load_config_files()
        
    def on_delete_clicked(self):
        """删除选中的项目"""
        # 获取当前选中的项目名称
        selected_project_name = self.combo_box.currentText()
        
        if selected_project_name:
            # 确认删除操作
            confirm_message = f"你确定要删除项目 '{selected_project_name}' 吗？此操作不可恢复！"
            reply = QMessageBox.question(self, '警告', confirm_message, QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
            
            if reply == QMessageBox.Yes:
                # 构建项目文件的路径
                project_file_path = os.path.join(self.config_dir, selected_project_name + ".json")
                
                # 检查文件是否存在并删除
                if os.path.exists(project_file_path):
                    os.remove(project_file_path)
                else:
                    QMessageBox.warning(self, '警告', f"项目文件 '{selected_project_name}.json' 不存在。")
                
                # 从UI中移除该项目
                self.combo_box.removeItem(self.combo_box.currentIndex())
                
                # 重新加载配置文件列表
                self.load_config_files()
            else:
                QMessageBox.information(self, '信息', '删除操作已取消。')
        else:
            QMessageBox.warning(self, '警告', '没有选中任何项目。')
    def get_current_config_file(self):
        """获取当前选中的配置文件"""
        if self.combo_box.currentText():
            return os.path.join(self.config_dir, self.combo_box.currentText() + ".json")


class AddProjectDialog(QDialog):
    def __init__(self, dir, parent=None, path=None, combobox=None):
        super(AddProjectDialog, self).__init__(parent)
        self.setWindowTitle("添加/修改项目信息")
        self.resize(800, 400)  # 增加宽度以适应多列布局
        self.dir = dir
        self.path = path
        self.combobox = combobox
        
        # 定义所有表单项的配置
        self.form_fields = [
            {"key": "项目名称", "label": "项目名称:", "default": ""},
            {"key": "风场名", "label": "风场名:", "default": ""},
            {"key": "风场地址", "label": "风场地址:", "default": ""},
            {"key": "甲方公司", "label": "甲方公司:", "default": ""},
            {"key": "甲方负责人", "label": "甲方负责人:", "default": ""},
            {"key": "甲方负责人电话", "label": "甲方负责人电话:", "default": ""},
            {"key": "乙方公司", "label": "乙方公司:", "default": "武汉迪特聚能科技有限公司"},
            {"key": "乙方负责人", "label": "乙方负责人:", "default": ""},
            {"key": "乙方负责人电话", "label": "乙方负责人电话:", "default": ""},
            {"key": "项目规格", "label": "项目规格:", "default": ""},
            {"key": "项目工期", "label": "项目工期:", "default": ""},
        ]
        
        # 加载现有数据（如果有）
        self.data = {}
        if self.path:
            self.load_existing_data()
        
        self.init_ui()
    
    def load_existing_data(self):
        """加载现有项目数据"""
        try:
            with open(self.path, 'r', encoding='utf-8') as f:
                self.data = json.load(f)
        except json.JSONDecodeError:
            QMessageBox.critical(self, "错误", "JSON 文件格式错误！")
        except Exception as e:
            QMessageBox.critical(self, "错误", f"加载文件失败: {str(e)}")
    
    def init_ui(self):
        """初始化用户界面"""
        main_layout = QVBoxLayout()
        
        # 创建表单布局
        form_widget = QWidget()
        form_layout = QHBoxLayout(form_widget)
        
        # 创建两列布局
        column1 = QVBoxLayout()
        column2 = QVBoxLayout()
        
        # 将表单字段添加到列中
        self.fields = {}
        for i, field in enumerate(self.form_fields):
            # 创建标签和输入框
            label = QLabel(field["label"])
            edit = QLineEdit()
            
            # 设置默认值或从现有数据加载
            if self.path and field["key"] in self.data:
                edit.setText(self.data[field["key"]])
            else:
                edit.setText(field["default"])
            
            # 存储输入框引用
            self.fields[field["key"]] = edit
            
            # 根据索引决定添加到哪一列
            if i < len(self.form_fields) / 2:
                column1.addWidget(label)
                column1.addWidget(edit)
            else:
                column2.addWidget(label)
                column2.addWidget(edit)
        
        # 添加间距和弹性空间
        column1.addStretch()
        column2.addStretch()
        
        # 将列添加到表单布局
        form_layout.addLayout(column1)
        form_layout.addSpacing(20)  # 列间距
        form_layout.addLayout(column2)
        
        # 创建按钮
        self.save_button = QPushButton("保存")
        self.save_button.clicked.connect(self.on_save_button_clicked)
        
        # 添加到主布局
        main_layout.addWidget(form_widget)
        main_layout.addWidget(self.save_button)
        
        self.setLayout(main_layout)
    
    def on_save_button_clicked(self):
        """保存按钮点击事件"""
        # 收集所有字段数据
        project_info = {
            field["key"]: self.fields[field["key"]].text()
            for field in self.form_fields
        }
        project_info["json路径"] = self.dir
        
        # 验证保存目录
        if not os.path.exists(self.dir) or not os.path.isdir(self.dir):
            QMessageBox.warning(self, "警告", "请选择一个有效的保存项目信息的文件夹路径")
            return
        
        # 构建默认文件名
        default_filename = f"{project_info['项目名称']}.json"
        
        # 让用户选择文件名
        file_path, _ = QFileDialog.getSaveFileName(
            self, 
            "保存项目信息",
            os.path.join(self.dir, default_filename), 
            "JSON Files (*.json)"
        )
        
        if not file_path:
            QMessageBox.warning(self, "警告", "未选择文件名")
            return
        
        # 保存项目信息到文件
        try:
            with open(file_path, 'w', encoding='utf-8') as file:
                json.dump(project_info, file, ensure_ascii=False, indent=4)
        except Exception as e:
            QMessageBox.critical(self, "错误", f"保存文件失败: {str(e)}")
            return
        
        self.parent().load_config_files()
        
        # 提示用户保存成功
        QMessageBox.information(self, "信息", f"项目信息已成功保存到 {file_path}")
        
        # 关闭对话框
        self.close()

class AddPersonnelDialog(QDialog):
    def __init__(self, dir, parent=None, path=None, combobox=None):
        super(AddPersonnelDialog, self).__init__(parent)
        self.setWindowTitle("添加/修改单次检查信息")
        self.resize(800, 400)  # 增加宽度以适应多列布局
        self.dir = dir
        self.path = path
        self.combobox = combobox
        
        # 定义所有表单项的配置
        self.form_fields = [
            {"key": "检查人员", "label": "检查人员:", "default": ""},
            {"key": "厂家人员", "label": "厂家人员:", "default": ""},
            {"key": "业主人员", "label": "业主人员:", "default": ""},
            {"key": "数据处理人员", "label": "数据处理人员:", "default": ""},
            {"key": "报告编制人员", "label": "报告编制人员:", "default": ""},
            {"key": "机组型号", "label": "机组型号:", "default": ""},
            {"key": "机组厂家", "label": "机组厂家:", "default": ""},
            {"key": "叶片型号", "label": "叶片型号", "default": ""},
            {"key": "叶片厂家", "label": "叶片厂家:", "default": ""},
            {"key": "施工日期", "label": "施工日期", "default": "开始-结束"},
            #{"key": "", "label": "", "default": ""},
            # 可以轻松添加更多人员字段
        ]
        
        # 添加复选框字段
        self.checkbox_fields = [
            {"key": "外部检查", "label": "外部检查", "default": False},
            {"key": "内部检查", "label": "内部检查", "default": False},
            {"key": "防雷检查", "label": "防雷检查", "default": False}
        ]

        # 加载现有数据（如果有）
        self.data = {}
        if self.path:
            self.load_existing_data()
        
        self.init_ui()
    
    def load_existing_data(self):
        """加载现有人员数据"""
        try:
            with open(self.path, 'r', encoding='utf-8') as f:
                self.data = json.load(f)
        except json.JSONDecodeError:
            QMessageBox.critical(self, "错误", "JSON 文件格式错误！")
        except Exception as e:
            QMessageBox.critical(self, "错误", f"加载文件失败: {str(e)}")
    
    def init_ui(self):
        """初始化用户界面"""
        main_layout = QVBoxLayout()
        
        # 创建表单布局
        form_widget = QWidget()
        form_layout = QHBoxLayout(form_widget)
        
        # 创建两列布局
        column1 = QVBoxLayout()
        column2 = QVBoxLayout()
        
        # 将表单字段添加到列中
        self.fields = {}
        for i, field in enumerate(self.form_fields):
            # 创建标签和输入框
            label = QLabel(field["label"])
            edit = QLineEdit()
            
            # 设置默认值或从现有数据加载
            if self.path and field["key"] in self.data:
                edit.setText(self.data[field["key"]])
            else:
                edit.setText(field["default"])
            
            # 存储输入框引用
            self.fields[field["key"]] = edit
            
            # 根据索引决定添加到哪一列
            if i < len(self.form_fields) / 2:
                column1.addWidget(label)
                column1.addWidget(edit)
            else:
                column2.addWidget(label)
                column2.addWidget(edit)
        
        # 添加间距和弹性空间
        column1.addStretch()
        column2.addStretch()
        
        # 将列添加到表单布局
        form_layout.addLayout(column1)
        form_layout.addSpacing(20)  # 列间距
        form_layout.addLayout(column2)
        
        # 创建按钮
        self.save_button = QPushButton("保存")
        self.save_button.clicked.connect(self.on_save_button_clicked)
        
                # 创建复选框布局
        checkbox_group = QGroupBox("检查类型")
        checkbox_layout = QHBoxLayout()
        
        # 添加复选框
        self.checkboxes = {}
        for field in self.checkbox_fields:
            checkbox = QCheckBox(field["label"])
            if self.path and field["key"] in self.data:
                checkbox.setChecked(bool(self.data[field["key"]]))
            else:
                checkbox.setChecked(field["default"])
            self.checkboxes[field["key"]] = checkbox
            checkbox_layout.addWidget(checkbox)
        
        checkbox_group.setLayout(checkbox_layout)
        
        # 添加到主布局
        main_layout.addWidget(checkbox_group)
        main_layout.addWidget(form_widget)
        main_layout.addWidget(self.save_button)
        
        self.setLayout(main_layout)
    
    def on_save_button_clicked(self):
        """保存按钮点击事件"""
        # 收集所有字段数据
        personnel_info = {
            field["key"]: self.fields[field["key"]].text()
            for field in self.form_fields
        }
        # 添加复选框数据
        for key, checkbox in self.checkboxes.items():
            personnel_info[key] = checkbox.isChecked()
        
        personnel_info["json路径"] = self.dir
        
        # 验证保存目录
        if not os.path.exists(self.dir) or not os.path.isdir(self.dir):
            QMessageBox.warning(self, "警告", "请选择一个有效的保存人员信息的文件夹路径")
            return
        
        # 构建默认文件名
        default_filename = "人员信息.json"
        
        # 让用户选择文件名
        file_path, _ = QFileDialog.getSaveFileName(
            self, 
            "保存人员信息",
            os.path.join(self.dir, default_filename), 
            "JSON Files (*.json)"
        )
        
        if not file_path:
            QMessageBox.warning(self, "警告", "未选择文件名")
            return
        
        # 保存人员信息到文件
        try:
            with open(file_path, 'w', encoding='utf-8') as file:
                json.dump(personnel_info, file, ensure_ascii=False, indent=4)
        except Exception as e:
            QMessageBox.critical(self, "错误", f"保存文件失败: {str(e)}")
            return
        
        self.parent().load_config_files()
        
        # 提示用户保存成功
        QMessageBox.information(self, "信息", f"人员信息已成功保存到 {file_path}")
        
        # 关闭对话框
        self.close()

class SmartDropdown(QWidget):
    """
    支持动态同步的智能下拉框
    -全局可保存共享选择池
    -可编辑共享池
    存储形式：

    """
    shared_pool = {}  # 类变量存储共享选项
    
    class MyFontComboBox(QFontComboBox):
        def __init__(self, parent=None):
            super().__init__(parent)
            # 获取内部的QLineEdit部件
            self.line_edit = self.lineEdit()
            # 当获得焦点时全选文本
            self.line_edit.selectionChanged.connect(self.select_all_text)
        
        def select_all_text(self):
            if self.line_edit.hasFocus():
                self.line_edit.selectAll()
    
    @classmethod
    def update_shared_pool(cls, dropdown_type, options):
        """更新指定类别的共享选项"""
        if not options:  # 如果选项为空，添加默认选项
            options = ["未选中"]
            
        cls.shared_pool[dropdown_type] = list(options)
        cls.save_shared_pool()
        
        # 自动刷新所有该类型的下拉框
        for widget in QApplication.allWidgets():
            if isinstance(widget, SmartDropdown) and widget.dropdown_type == dropdown_type:
                widget._load_options()
        
    @classmethod
    def save_shared_pool(cls):
        """保存共享池到QSettings"""
        settings = QSettings()
        settings.setValue("SmartDropdown/shared_pool", cls.shared_pool)
    
    @classmethod
    def load_shared_pool(cls):
        """从QSettings加载共享池"""
        settings = QSettings()
        pool = settings.value("SmartDropdown/shared_pool", {})
        cls.shared_pool = {k: v for k, v in pool.items()} if pool else {}
        # 确保每个类别至少有一个默认选项
        for dropdown_type in cls.shared_pool:
            if not cls.shared_pool[dropdown_type]:
                cls.shared_pool[dropdown_type] = ["未选中"]

    def __init__(self, dropdown_type, messagebrowser, label_text=None, parent=None):
        super().__init__(parent)
        self.dropdown_type = dropdown_type
        self.label_text = label_text
        self.messagebrowser = messagebrowser
        
        # 初始化共享池
        if not self.shared_pool:
            self.load_shared_pool()
        
        # 确保当前类型在共享池中存在
        if self.dropdown_type not in self.shared_pool:
            self.shared_pool[self.dropdown_type] = ["未选中"]
            self.save_shared_pool()
        
        self._setup_ui()
        self._load_options()
        self._initialize_defect_type()
        self.messagebrowser.append(f"初始化下拉框: {dropdown_type}")
        
    @property
    def current_index(self):
        """当前选中索引（0表示未选中）"""
        return self.combo_box.currentIndex()
    
    @current_index.setter
    def current_index(self, index):
        if self.combo_box.count() > 0:
            clamped_index = max(0, min(index, self.combo_box.count()-1))
            self.combo_box.setCurrentIndex(clamped_index)
    
    def _initialize_defect_type(self):
        """初始化缺陷类型选项，如果选项不足则自动重置"""
        if self.dropdown_type == "缺陷类型":
            # 检查当前选项数量是否不足
            if len(self.shared_pool.get(self.dropdown_type, [])) <= 1:
                default_options = [
                    "未选中...", "前缘涂层损伤(风损)", "表面裂纹", "腻子脱落", "漆面脱落", 
                    "油污","布层破损(深层、不规则面状)", "裂纹(浅层线状)", "开裂(深层线带窄面状)",
                    "贯穿损伤", "鼓包", "凹陷", "雷击45d", "雷击zzw", "雷击ck",
                    "合模缝开裂", "透光", "雷击碳化", "褶皱", "脱胶", "缺胶",
                    "防雷线断裂", "开裂"
                ]
                
                self.update_shared_pool(self.dropdown_type, default_options)
                self.messagebrowser.append(
                    f"初始化提示: 检测到{self.dropdown_type}选项不足，已自动重置为默认选项集"
                    f"(原因: 第一次初始化或选项被清空)"
                )
    
    def _setup_ui(self):
        """初始化UI组件"""
        self.combo_box = QFontComboBox()
        self.combo_box.setEditable(True)
        
        # 操作按钮
        self.add_btn = QPushButton("+添加输入的类别")
        self.del_btn = QPushButton("-删除选中类别")
        self.refresh_btn = QPushButton()
        self.refresh_btn.setIcon(QIcon.fromTheme("view-refresh"))  # 使用系统刷新图标
        self.reset_btn = QPushButton("重置")
        
        # 设置按钮大小
        for btn in [self.add_btn, self.del_btn, self.reset_btn]:
            btn.setFixedWidth(150)
        self.refresh_btn.setFixedWidth(30)
        # 设置工具提示
        self.add_btn.setToolTip("添加选项到共享池")
        self.del_btn.setToolTip("从共享池删除当前选项")
        self.refresh_btn.setToolTip("同步共享池选项")
        self.reset_btn.setToolTip("重置为默认选项")
        
        # 按钮布局
        btn_layout = QHBoxLayout()
        btn_layout.addWidget(self.add_btn)
        btn_layout.addWidget(self.del_btn)
        btn_layout.addWidget(self.reset_btn)
        btn_layout.addWidget(self.refresh_btn)
        btn_layout.setSpacing(2)
        btn_layout.setContentsMargins(0, 0, 0, 0)
        
        # 主布局
        main_layout = QVBoxLayout()
        main_layout.setSpacing(5)
        if self.label_text:
            main_layout.addWidget(QLabel(self.label_text))
        main_layout.addWidget(self.combo_box)
        main_layout.addLayout(btn_layout)
        
        self.setLayout(main_layout)
        
        # 信号连接
        self.add_btn.clicked.connect(self._add_option)
        self.del_btn.clicked.connect(self._del_option)
        self.refresh_btn.clicked.connect(self._refresh_options)
        self.reset_btn.clicked.connect(self._reset_options)
        self.combo_box.currentIndexChanged.connect(self._update_buttons)
    
    def _reset_options(self):
        """重置选项为默认值"""
        if self.dropdown_type == "缺陷类型":
            default_options = [
                "未选中...", "前缘涂层损伤(风损)", "表面裂纹", "腻子脱落", "漆面脱落", 
                "油污","布层破损(深层、不规则面状)", "裂纹(浅层线状)", "开裂(深层线带窄面状)",
                "贯穿损伤", "鼓包", "凹陷", "雷击45d", "雷击zzw", "雷击ck",
                "合模缝开裂", "透光", "雷击碳化", "褶皱", "脱胶", "缺胶",
                "防雷线断裂", "开裂"
            ]
            
            reply = QMessageBox.question(
                self, '确认重置',
                '确定要重置缺陷类型为默认选项吗？此操作不可撤销！',
                QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
                QMessageBox.StandardButton.No)
            
            if reply == QMessageBox.StandardButton.Yes:
                self.update_shared_pool(self.dropdown_type, default_options)
                self.messagebrowser.append(f"已重置 {self.dropdown_type} 为默认选项")
        else:
            QMessageBox.information(
                self, '提示',
                f'当前类型 {self.dropdown_type} 没有预定义的重置选项')
    
    def _load_options(self):
        """加载选项（确保只有一个未选中选项）"""
        current_text = self.combo_box.currentText() if self.combo_box.count() > 0 else ""
        
        self.combo_box.blockSignals(True)  # 防止触发信号
        self.combo_box.clear()
        
        # 确保共享池中有当前类型的选项
        if (self.dropdown_type not in self.shared_pool or 
            not self.shared_pool[self.dropdown_type]):
            self.shared_pool[self.dropdown_type] = ["未选中"]
            self.save_shared_pool()
        
        # 直接从共享池加载选项
        self.combo_box.addItems(self.shared_pool[self.dropdown_type])
        
        # 恢复之前的选中项（如果有）
        if current_text:
            idx = self.combo_box.findText(current_text)
            if idx >= 0:
                self.combo_box.setCurrentIndex(idx)
        
        self.combo_box.blockSignals(False)
        self._update_buttons()
        
        self.messagebrowser.append(f"加载选项: {self.dropdown_type} - {self.shared_pool[self.dropdown_type]}")
    
    def _refresh_options(self):
        """手动刷新选项"""
        self.messagebrowser.append(f"手动刷新选项: {self.dropdown_type}")
        self._load_options()
    
    def _update_buttons(self):
        """根据当前状态更新按钮可用性"""
        has_options = len(self.shared_pool.get(self.dropdown_type, [])) > 0
        has_selection = self.current_index >= 0
        
        self.del_btn.setEnabled(has_options and has_selection)
        self.add_btn.setEnabled(True)
        self.refresh_btn.setEnabled(True)
        self.reset_btn.setEnabled(True)
    
    def _add_option(self):
        """添加新选项到共享池"""
        new_option = self.combo_box.currentText().strip()
        if not new_option:
            return
        
        if new_option not in self.shared_pool[self.dropdown_type]:
            if QMessageBox.Yes == QMessageBox.question(
                self, "确认添加", 
                f"添加 '{new_option}' 到 '{self.dropdown_type}' 选项池？",
                QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No):
                
                if self.dropdown_type not in self.shared_pool:
                    self.shared_pool[self.dropdown_type] = ["未选中"]

                self.shared_pool[self.dropdown_type].append(new_option)
                self.messagebrowser.append(f"添加选项: {new_option} 到 {self.dropdown_type}")
                self.update_shared_pool(self.dropdown_type, self.shared_pool[self.dropdown_type])
                self.combo_box.setCurrentIndex(self.combo_box.findText(new_option))
        else:
            self.messagebrowser.append(f"添加失败: 选项 {new_option} 已存在于 {self.dropdown_type} 选项池")
    
    def _del_option(self):
        """从共享池删除当前选项"""
        if self.current_index < 0:  # 无效选择
            return
            
        current_text = self.combo_box.currentText()
        
        if current_text == "未选中":
            self.messagebrowser.append("不能删除默认的'未选中'选项")
            return
            
        if QMessageBox.Yes == QMessageBox.question(
            self, "确认删除",
            f"从 '{self.dropdown_type}' 删除 '{current_text}'？",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No):
            
            try:
                self.shared_pool[self.dropdown_type].remove(current_text)
                
                # 如果删除后选项为空，恢复默认选项
                if not self.shared_pool[self.dropdown_type]:
                    self.shared_pool[self.dropdown_type] = ["未选中"]
                
                self.messagebrowser.append(f"删除选项: {current_text} 从 {self.dropdown_type}")
                self.update_shared_pool(self.dropdown_type, self.shared_pool[self.dropdown_type])
            except ValueError:
                self.messagebrowser.append(f"删除失败: 选项 {current_text} 不存在")

class FolderDropWidget(QWidget):
    selection_changed = Signal(list)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAcceptDrops(True)
        self.current_dir = None
        self.selected_folders = set()
        self.init_ui()
        self.apply_styles()

    def init_ui(self):
        main_layout = QVBoxLayout()
        main_layout.setSpacing(5)
        main_layout.setContentsMargins(0, 0, 0, 0)

        # 顶部按钮区域
        btn_layout = QHBoxLayout()
        self.reset_btn = QPushButton("重新选择项目文件夹")
        self.reset_btn.clicked.connect(self.reset_selection)
        self.reset_btn.setFixedSize(120, 40)
        self.reset_btn.setVisible(False)
        
        self.clear_btn = QPushButton("清空")
        self.clear_btn.clicked.connect(self.clear_selected)
        self.clear_btn.setFixedSize(120, 40)
        self.clear_btn.setVisible(False)
        
        self.full_select_btn = QPushButton("全选")
        self.full_select_btn.clicked.connect(self.select_all_folders)
        self.full_select_btn.setFixedSize(120, 40)
        self.full_select_btn.setVisible(False)

        # 添加返回按钮
        self.back_btn = QPushButton("返回项目选择")
        self.back_btn.clicked.connect(lambda: (self.stacked_layout.setCurrentIndex(1) or self.clear_selected() or self.reset_btn.setVisible(True) or self.back_btn.setVisible(False)))
        self.back_btn.setVisible(False)

        btn_layout.addWidget(self.reset_btn)
        btn_layout.addWidget(self.back_btn)
        btn_layout.addWidget(self.full_select_btn)
        btn_layout.addWidget(self.clear_btn)
        btn_layout.addStretch()
        main_layout.addLayout(btn_layout)

        # 主内容区域 - 使用堆叠布局管理不同状态
        self.stacked_layout = QStackedLayout()
        
        # 状态1: 未选择文件夹时的提示
        self.prompt_container = QWidget()
        prompt_layout = QVBoxLayout()
        self.prompt_label = QLabel("拖放文件夹到此处或点击选择文件夹")
        self.prompt_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.prompt_label.mousePressEvent = self.select_folder
        prompt_layout.addWidget(self.prompt_label)
        self.prompt_container.setLayout(prompt_layout)
        self.stacked_layout.addWidget(self.prompt_container)

        # 状态2: 显示第一层文件夹 (项目选择)
        self.level1_container = QWidget()
        level1_main_layout = QVBoxLayout()
        level1_main_layout.setSpacing(0)
        level1_main_layout.setContentsMargins(0, 0, 0, 0)
        
        # 添加状态标签
        self.level1_title = QLabel("项目选择")
        self.level1_title.setAlignment(Qt.AlignmentFlag.AlignCenter)
        level1_main_layout.addWidget(self.level1_title)
        
        self.level1_list = QListWidget()
        self.level1_list.setIconSize(QSize(48, 48))
        self.level1_list.setViewMode(QListWidget.ViewMode.IconMode)
        self.level1_list.setResizeMode(QListWidget.ResizeMode.Adjust)
        self.level1_list.setMovement(QListWidget.Movement.Static)
        self.level1_list.setSpacing(10)
        self.level1_list.itemDoubleClicked.connect(self.show_level2_folders)
        level1_main_layout.addWidget(self.level1_list)
        
        self.level1_container.setLayout(level1_main_layout)
        self.stacked_layout.addWidget(self.level1_container)

        # 状态3: 显示第二层文件夹 (机组选择)
        self.level2_container = QWidget()
        level2_main_layout = QVBoxLayout()
        level2_main_layout.setSpacing(0)
        level2_main_layout.setContentsMargins(0, 0, 0, 0)
        
        # 添加状态标签
        self.level2_title = QLabel("机组选择")
        self.level2_title.setAlignment(Qt.AlignmentFlag.AlignCenter)
        level2_main_layout.addWidget(self.level2_title)
        
        self.level2_list = QListWidget()
        self.level2_list.setIconSize(QSize(48, 48))
        self.level2_list.setViewMode(QListWidget.ViewMode.IconMode)
        self.level2_list.setResizeMode(QListWidget.ResizeMode.Adjust)
        self.level2_list.setMovement(QListWidget.Movement.Static)
        self.level2_list.setSpacing(10)
        level2_main_layout.addWidget(self.level2_list)
        
        self.level2_container.setLayout(level2_main_layout)
        self.stacked_layout.addWidget(self.level2_container)

        # 将堆叠布局添加到主布局
        stack_container = QWidget()
        stack_container.setLayout(self.stacked_layout)
        main_layout.addWidget(stack_container)

        # 已选目录列表 (始终显示在底部)
        self.selected_group = QGroupBox("已选机组")
        self.selected_group.setVisible(False)
        selected_layout = QVBoxLayout()
        self.selected_list = QListWidget()
        self.selected_list.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
        selected_layout.addWidget(self.selected_list)
        self.selected_group.setLayout(selected_layout)
        main_layout.addWidget(self.selected_group)

        self.setLayout(main_layout)

    def select_all_folders(self):
        self.selected_folders.update(
            set(item.data(Qt.ItemDataRole.UserRole) for item in self.level2_list.findItems("", Qt.MatchFlag.MatchContains))
        )
        self.update_checkbox_states()
        self.selection_changed.emit(self.get_selected_folders())

    def apply_styles(self):
        self.prompt_label.setStyleSheet(f"""
            {PATH_DISPLAY_STYLE}
            border: 2px dashed {SECONDARY_COLOR};
            min-height: 150px;
        """)
        
        self.level1_list.setStyleSheet(f"""
            QListWidget {{
                background-color: #f8f9fa;
                min-height: 150px;
            }}
        """)
        
        self.level2_list.setStyleSheet(f"""
            QListWidget {{
                background-color: #f8f9fa;
                min-height: 150px;
            }}
        """)
        
        self.level1_title.setStyleSheet(f"""
            {LABEL_STYLE}
            font-size: {TITLE_FONT_SIZE}pt;
            font-weight: bold;
            padding: 10px;
        """)
        
        self.level2_title.setStyleSheet(f"""
            {LABEL_STYLE}
            font-size: {TITLE_FONT_SIZE}pt;
            font-weight: bold;
            padding: 10px;
        """)
        
        self.selected_group.setStyleSheet(GROUP_BOX_STYLE)
        self.full_select_btn.setStyleSheet(BUTTON_STYLE)
        self.reset_btn.setStyleSheet(BUTTON_STYLE)
        self.clear_btn.setStyleSheet(BUTTON_STYLE)
        self.back_btn.setStyleSheet(BUTTON_STYLE)

    def select_folder(self, event=None):
        folder = QFileDialog.getExistingDirectory(self, "选择文件夹")
        if folder:
            self.load_folder(folder)

    def reset_selection(self):
        self.current_dir = None
        self.selected_folders.clear()
        self.level1_list.clear()
        self.level2_list.clear()
        self.selected_list.clear()
        self.stacked_layout.setCurrentIndex(0)  # 显示提示
        self.reset_btn.setVisible(False)
        self.clear_btn.setVisible(False)
        self.back_btn.setVisible(False)
        self.selected_group.setVisible(False)
        self.selection_changed.emit([])

    def clear_selected(self):
        self.selected_folders.clear()
        self.selected_list.clear()
        self.update_checkbox_states()
        self.selection_changed.emit([])

    def load_folder(self, path):
        self.current_dir = path
        self.load_level1_folders(path)
        self.stacked_layout.setCurrentIndex(1)  # 显示第一层
        self.reset_btn.setVisible(True)
        self.back_btn.setVisible(False)
        self.clear_btn.setVisible(True)
        self.full_select_btn.setVisible(True)
        self.selected_group.setVisible(True)

    def dragEnterEvent(self, event: QDragEnterEvent):
        if event.mimeData().hasUrls():
            event.acceptProposedAction()

    def dropEvent(self, event: QDropEvent):
        for url in event.mimeData().urls():
            path = url.toLocalFile()
            if os.path.isdir(path):
                self.load_folder(path)
                break

    def load_level1_folders(self, root_path):
        self.level1_list.clear()
        self.level2_list.clear()

        for item in os.listdir(root_path):
            item_path = os.path.join(root_path, item)
            if os.path.isdir(item_path):
                item_widget = QListWidgetItem(QIcon.fromTheme("folder"), item)
                item_widget.setData(Qt.ItemDataRole.UserRole, item_path)
                item_widget.setSizeHint(QSize(80, 80))
                item_widget.setTextAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignBottom)
                self.level1_list.addItem(item_widget)

    def show_level2_folders(self, item):
        self.level2_list.clear()
        self.reset_btn.setVisible(False)
        self.back_btn.setVisible(True)
        folder_path = item.data(Qt.ItemDataRole.UserRole)

        for sub_item in os.listdir(folder_path):
            sub_item_path = os.path.join(folder_path, sub_item)
            if os.path.isdir(sub_item_path):
                list_item = QListWidgetItem()
                list_item.setSizeHint(QSize(100, 80))
                list_item.setData(Qt.ItemDataRole.UserRole, sub_item_path)
                
                widget = QWidget()
                layout = QVBoxLayout()
                layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
                
                icon_label = QLabel()
                icon_label.setPixmap(QIcon.fromTheme("folder").pixmap(48, 48))
                icon_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
                layout.addWidget(icon_label)
                
                name_check_layout = QHBoxLayout()
                checkbox = QCheckBox(sub_item)
                checkbox.setStyleSheet(f"""
                    {PRIMARY_CHECKBOX_STYLE}
                    QCheckBox::indicator:checked {{
                        background-color: {PRIMARY_COLOR};
                        border: 1px solid {PRIMARY_COLOR};
                    }}
                """)
                checkbox.stateChanged.connect(lambda state, path=sub_item_path: self.toggle_folder_selection(path, state))
                checkbox.setChecked(sub_item_path in self.selected_folders)
                name_check_layout.addWidget(checkbox)
                layout.addLayout(name_check_layout)
                
                widget.setLayout(layout)
                
                list_item.setSizeHint(widget.sizeHint())
                self.level2_list.addItem(list_item)
                self.level2_list.setItemWidget(list_item, widget)
        
        self.stacked_layout.setCurrentIndex(2)  # 显示第二层

    def toggle_folder_selection(self, folder_path, state):
        if state == Qt.CheckState.Checked.value:
            self.selected_folders.add(folder_path)
            item = QListWidgetItem(folder_path)
            self.selected_list.addItem(item)
        else:
            self.selected_folders.discard(folder_path)
            items = self.selected_list.findItems(folder_path, Qt.MatchFlag.MatchExactly)
            for item in items:
                self.selected_list.takeItem(self.selected_list.row(item))
        
        self.selection_changed.emit(self.get_selected_folders())

    def update_checkbox_states(self):
        for i in range(self.level2_list.count()):
            item = self.level2_list.item(i)
            widget = self.level2_list.itemWidget(item)
            if widget:
                checkbox = widget.findChild(QCheckBox)
                if checkbox:
                    path = item.data(Qt.ItemDataRole.UserRole)
                    checkbox.setChecked(path in self.selected_folders)

    def get_selected_folders(self):
        return list(self.selected_folders)
    
class DraggableLine(QFrame):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setFrameShape(QFrame.HLine)
        self.setFrameShadow(QFrame.Sunken)
        self.setLineWidth(2)
        self.setFixedHeight(10)
        self.dragging = False
        self.setCursor(Qt.SizeVerCursor)
        
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.dragging = True
            self.start_pos = event.globalPos()
            
    def mouseMoveEvent(self, event):
        if self.dragging:
            delta = event.globalPos() - self.start_pos
            self.start_pos = event.globalPos()
            # 通知父窗口调整布局
            self.parent().adjust_row_height(delta.y())
            
    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.dragging = False

class OutputDirSelector(QWidget):
    # 定义一个信号，当路径改变时发出
    path_changed = Signal(str)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.output_dir = ""
        self.init_ui()
        self.set_style()

    def init_ui(self):
        # 主布局
        layout = QVBoxLayout(self)
        layout.setSpacing(10)
        layout.setContentsMargins(0, 0, 0, 0)

        # 组框
        self.group_box = QFrame()
        self.group_box.setFrameShape(QFrame.StyledPanel)
        group_layout = QVBoxLayout(self.group_box)
        group_layout.setSpacing(GROUP_BOX_SPACING)
        group_layout.setContentsMargins(*GROUP_BOX_MARGINS)

        # 标题标签
        self.setWindowTitle("选择输出目录")

        # 路径显示标签
        self.path_label = QLabel("未选择文件夹")
        self.path_label.setWordWrap(True)
        self.path_label.setAlignment(Qt.AlignLeft | Qt.AlignTop)
        
        # 选择按钮
        self.select_button = QPushButton("选择输出文件夹")
        self.select_button.clicked.connect(self.select_output_dir)
        
        # 添加部件到布局
        group_layout.addWidget(self.path_label)
        group_layout.addWidget(self.select_button)
        
        # 添加组框到主布局
        layout.addWidget(self.group_box)

    def set_style(self):
        # 设置组框样式
        self.group_box.setStyleSheet(GROUP_BOX_STYLE)
        
        # 设置路径显示样式
        self.path_label.setStyleSheet(PATH_DISPLAY_STYLE)
        
        # 设置按钮样式
        self.select_button.setStyleSheet(PRIMARY_BUTTON_STYLE)
        
        # 设置最小尺寸
        self.group_box.setMinimumSize(GROUP_BOX_MIN_WIDTH, GROUP_BOX_MIN_HEIGHT)

    def select_output_dir(self):
        """打开文件夹选择对话框"""
        dir_path = QFileDialog.getExistingDirectory(
            self, 
            "选择输出文件夹",
            "",  # 默认路径为空，使用系统默认
            QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks
        )
        
        if dir_path:  # 如果用户选择了文件夹
            self.output_dir = dir_path
            self.path_label.setText(dir_path)
            self.path_changed.emit(dir_path)  # 发出路径改变信号

    def get_output_dir(self):
        """获取输出路径"""
        return self.output_dir

    def set_output_dir(self, path):
        """设置输出路径"""
        if path:
            self.output_dir = path
            self.path_label.setText(path)
            self.path_changed.emit(path)



class JsonFileHandler:
    """处理JSON文件的读写操作"""
    
    @staticmethod
    def read_json(file_path):
        """读取JSON文件"""
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            print(f"Error reading JSON file {file_path}: {e}")
            return None
    
    @staticmethod
    def write_json(file_path, data):
        try:
            # 检查目录是否可写
            dir_path = os.path.dirname(file_path)
            if not os.access(dir_path, os.W_OK):
                print(f"错误：目录不可写 {dir_path}")
                return False
                
            with open(file_path, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=4)
            print(f"成功写入文件：{file_path}")
            return True
        except Exception as e:
            print(f"写入文件错误：{str(e)}")
            return False

class TreeModelManager:
    """管理树形模型的数据操作"""
    
    # 中英文映射字典
    TRANSLATION_MAP = {
        # 左侧树
        "Directory Structure": "目录结构",
        
        # 右侧树
        "Information": "信息",
        "Value": "值",
        "turbinecode": "机组编号",
        "part1": "叶片1",
        "part2": "叶片2",
        "part3": "叶片3",
        "code": "部件代码",
        "defect_picture_dict": "缺陷图字典",
        "typical_picture_dict": "典型图字典"
    }
    
    @staticmethod
    def translate_text(key):
        """根据映射表翻译文本"""
        return TreeModelManager.TRANSLATION_MAP.get(key, key)
    
    @staticmethod
    def reverse_translate_text(chinese_text):
        """根据中文反向查找英文键"""
        reverse_map = {v: k for k, v in TreeModelManager.TRANSLATION_MAP.items()}
        return reverse_map.get(chinese_text, chinese_text)
    
    @staticmethod
    def set_item_color(item, color=QColor(255, 0, 0)):
        """设置树节点的文本颜色"""
        if item is not None:
            item.setForeground(color)

    @staticmethod
    def create_left_tree_model(folder_list, required_child_count=3):
        """创建左侧文件夹树模型
        Args:
            folder_list: 文件夹路径列表
            required_child_count: 要求的子目录数量，默认为3
        """
        model = QStandardItemModel()
        model.setHorizontalHeaderLabels(["目录结构"])
        folder_map = {}
        
        for folder_path in folder_list:
            folder_name = os.path.basename(folder_path)
            folder_map[folder_name] = folder_path
            
            parent_item = QStandardItem(folder_name)
            model.appendRow(parent_item)
            
            try:
                child_dirs = []
                for child in os.listdir(folder_path):
                    child_path = os.path.join(folder_path, child)
                    folder_map[child] = child_path

                    if os.path.isdir(child_path):
                        child_dirs.append(child)
                        child_item = QStandardItem(child)
                        parent_item.appendRow(child_item)
                
                # 检查子目录数量是否满足要求
                if len(child_dirs) < required_child_count:
                    TreeModelManager.set_item_color(parent_item)  # 不满足则标红
                    
            except Exception as e:
                print(f"Error reading folder {folder_path}: {e}")
                TreeModelManager.set_item_color(parent_item)  # 读取错误也标红
        
        return model, folder_map
    
    @staticmethod
    def create_right_tree_model(folder_names):
        """创建右侧可编辑树模型"""
        model = QStandardItemModel()
        model.setHorizontalHeaderLabels([
            TreeModelManager.translate_text("Information"),
            TreeModelManager.translate_text("Value")
        ])
        
        json_data = {}
        for folder_name in folder_names:
            parent_item = QStandardItem(folder_name)
            parent_item.setEditable(False)
            
            # 添加机组编号
            turbine_item = QStandardItem(TreeModelManager.translate_text("turbinecode"))
            turbine_value = QStandardItem(folder_name.replace("机组", "").strip())
            turbine_value.setEditable(True)
            parent_item.appendRow([turbine_item, turbine_value])
            
            # 添加默认的三个子部件
            for i in range(1, 4):
                part_item = QStandardItem(TreeModelManager.translate_text(f"part{i}"))
                part_item.setEditable(False)
                
                # 部件代码
                code_item = QStandardItem(TreeModelManager.translate_text("code"))
                code_value = QStandardItem("001")
                code_value.setEditable(True)
                part_item.appendRow([code_item, code_value])
                
                # 缺陷图字典
                defect_pic_item = QStandardItem(TreeModelManager.translate_text("defect_picture_dict"))
                defect_pic_item.setEditable(False)
                part_item.appendRow([defect_pic_item, QStandardItem("{}")])
                
                # 典型图字典
                typical_pic_item = QStandardItem(TreeModelManager.translate_text("typical_picture_dict"))
                typical_pic_item.setEditable(False)
                part_item.appendRow([typical_pic_item, QStandardItem("{}")])
                
                parent_item.appendRow(part_item)
            
            model.appendRow(parent_item)
            
            # 初始化JSON数据（保持英文键）
            json_data[folder_name] = {
                "turbinecode": folder_name.replace("机组", "").strip(),
                "part1": {
                    "code": "001",
                    "defect_picture_dict": {},
                    "typical_picture_dict": {}
                },
                "part2": {
                    "code": "001",
                    "defect_picture_dict": {},
                    "typical_picture_dict": {}
                },
                "part3": {
                    "code": "001",
                    "defect_picture_dict": {},
                    "typical_picture_dict": {}
                }
            }
        
        return model, json_data
    
    @staticmethod
    def update_model_from_json(model, folder_name, json_data):
        """根据JSON数据更新模型"""
        parent_item = None
        for i in range(model.rowCount()):
            item = model.item(i)
            if item and item.text() == folder_name:
                parent_item = item
                break
        
        if parent_item and folder_name in json_data:
            data = json_data[folder_name]
            
            # 更新机组编号
            turbine_item = parent_item.child(0)
            if turbine_item and turbine_item.child(0, 1):
                turbine_item.child(0, 1).setText(data.get("turbinecode", ""))
            
            # 更新部件信息
            for j in range(1, 4):
                part_item = parent_item.child(j)
                if part_item:
                    part_name = TreeModelManager.reverse_translate_text(part_item.text())  # 反向查找英文键
                    if part_name in data:
                        # 更新部件代码
                        code_item = part_item.child(0, 1)
                        if code_item:
                            code_item.setText(data[part_name].get("code", "001"))
                        
                        # 更新缺陷图字典
                        defect_pic_item = part_item.child(1, 1)
                        if defect_pic_item:
                            defect_pic_dict = data[part_name].get("defect_picture_dict", {})
                            defect_pic_item.setText(str(defect_pic_dict))
                        
                        # 更新典型图字典
                        typical_pic_item = part_item.child(2, 1)
                        if typical_pic_item:
                            typical_pic_dict = data[part_name].get("typical_picture_dict", {})
                            typical_pic_item.setText(str(typical_pic_dict))
    
    @staticmethod
    def update_json_from_model(model, json_data):
        """从模型更新JSON数据"""
        for i in range(model.rowCount()):
            folder_index = model.index(i, 0)
            folder_name = model.data(folder_index)
            
            if not folder_name:
                continue
                
            # 确保数据结构存在
            if folder_name not in json_data:
                json_data[folder_name] = {
                    "turbinecode": "",
                    "part1": {"code": "001", "defect_picture_dict": {}, "typical_picture_dict": {}},
                    "part2": {"code": "001", "defect_picture_dict": {}, "typical_picture_dict": {}},
                    "part3": {"code": "001", "defect_picture_dict": {}, "typical_picture_dict": {}}
                }
            
            # 获取 turbinecode 值
            turbine_index = model.index(0, 1, folder_index)
            if turbine_index.isValid():
                new_value = model.data(turbine_index)
                json_data[folder_name]["turbinecode"] = new_value
            
            # 更新parts
            for part_num in range(1, 4):
                part_index = model.index(part_num, 0, folder_index)
                if part_index.isValid():
                    part_name = TreeModelManager.reverse_translate_text(model.data(part_index))  # 反向查找英文键
                    
                    # 更新部件代码
                    code_index = model.index(0, 1, part_index)
                    if code_index.isValid():
                        json_data[folder_name][part_name]["code"] = model.data(code_index, Qt.DisplayRole)
                    
                    # 更新缺陷图字典
                    defect_pic_index = model.index(1, 1, part_index)
                    if defect_pic_index.isValid():
                        defect_pic_str = model.data(defect_pic_index, Qt.DisplayRole)
                        try:
                            json_data[folder_name][part_name]["defect_picture_dict"] = eval(defect_pic_str)
                        except:
                            json_data[folder_name][part_name]["defect_picture_dict"] = {}
                    
                    # 更新典型图字典
                    typical_pic_index = model.index(2, 1, part_index)
                    if typical_pic_index.isValid():
                        typical_pic_str = model.data(typical_pic_index, Qt.DisplayRole)
                        try:
                            json_data[folder_name][part_name]["typical_picture_dict"] = eval(typical_pic_str)
                        except:
                            json_data[folder_name][part_name]["typical_picture_dict"] = {}

class DirectSaveDelegate(QStyledItemDelegate):
    """直接保存的委托实现"""
    def __init__(self, save_callback, parent=None):
        super().__init__(parent)
        self.save_callback = save_callback
        
    def setModelData(self, editor, model, index):
        try:
            # 提交数据前验证索引
            if not index.isValid():
                print("⚠️ 无效的模型索引")
                return
                
            old_value = index.data(Qt.DisplayRole)
            super().setModelData(editor, model, index)
            new_value = index.data(Qt.DisplayRole)
            
            print(f"📝 数据变更: {old_value} → {new_value}")
            
            # 获取顶层文件夹索引
            top_index = index
            while top_index.parent().isValid():
                top_index = top_index.parent()
                
            if model.hasIndex(top_index.row(), top_index.column(), top_index.parent()):
                folder_name = model.data(top_index, Qt.DisplayRole)
                self.save_callback(folder_name)
            
        except Exception as e:
            print(f"❌ 委托错误: {str(e)}")
            import traceback
            traceback.print_exc()

class FolderBrowser(QWidget):
    """文件夹浏览编辑主窗口"""
    data_changed = Signal(dict)  # 数据变更信号
    folder_selected = Signal(str) 
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.output_path = QDir.currentPath()  # 默认输出路径
        self.json_data = {}     # 存储所有JSON数据
        self.folder_map = {}    # 文件夹路径映射
        self.current_image_dir = ""  # 当前显示的图片目录
        self.init_ui()
        self.setup_connections()
        
    def init_ui(self):
        """初始化用户界面"""
        main_layout = QVBoxLayout(self)
        main_layout.setContentsMargins(5, 5, 5, 5)
        
        # 上下分割的主区域
        main_splitter = QSplitter(Qt.Vertical)
        
        # 上半部分 - 左右分割的树视图
        tree_splitter = QSplitter(Qt.Horizontal)
        
        # 左侧文件夹树视图
        self.left_model = QStandardItemModel()
        self.left_model.setHorizontalHeaderLabels(["目录结构"])
        self.left_tree = QTreeView()
        self.left_tree.setModel(self.left_model)
        self.left_tree.setEditTriggers(QTreeView.NoEditTriggers)
        
        # 右侧可编辑树视图
        self.right_model = QStandardItemModel()
        self.right_model.setHorizontalHeaderLabels(["信息", "值"])
        self.right_tree = QTreeView()
        self.right_tree.setModel(self.right_model)
        self.right_tree.setItemDelegate(
            DirectSaveDelegate(self.handle_immediate_save, self.right_tree)
        )
        
        # 添加到水平分割器
        tree_splitter.addWidget(self.left_tree)
        tree_splitter.addWidget(self.right_tree)
        tree_splitter.setSizes([300, 500])
        
        # 下半部分 - 图片浏览器和字典浏览器
        image_splitter = QSplitter(Qt.Horizontal)
        
        # 左侧图片浏览器
        self.image_browser = ImageBrowser()
        
        # 右侧字典浏览器
        self.dictionary_browser = DictionaryBrowser()
        
        # 添加到水平分割器
        image_splitter.addWidget(self.image_browser)
        image_splitter.addWidget(self.dictionary_browser)
        image_splitter.setSizes([400, 400])
        
        # 添加到垂直分割器
        main_splitter.addWidget(tree_splitter)
        main_splitter.addWidget(image_splitter)
        main_splitter.setSizes([400, 400])
        
        main_layout.addWidget(main_splitter)
        
        # 设置拖放接收
        self.dictionary_browser.setAcceptDrops(True)
    
    def setup_connections(self):
        """设置信号和槽的连接"""
        self.right_model.dataChanged.connect(self.on_right_data_changed)
        self.left_tree.expanded.connect(self.sync_right_tree_expand)
        self.left_tree.collapsed.connect(self.sync_right_tree_collapse)
        self.left_tree.clicked.connect(self.on_left_tree_clicked)
        self.folder_selected.connect(self.image_browser.show_images)

        self.right_tree.expanded.connect(self.sync_left_tree_expand)
        self.right_tree.collapsed.connect(self.sync_left_tree_collapse)
        self.right_tree.clicked.connect(self.on_right_tree_clicked)

        self.dictionary_browser.dictionary_updated.connect(self.handle_dictionary_update)

    def handle_dictionary_update(self, folder_name, part_name, dict_type_en, dict_data):
        """处理字典更新"""
        print(f"🔧 字典更新 {self.json_data}:\n[{folder_name}][{part_name}][{dict_type_en}] -> \n{dict_data}")
        # 1. 更新JSON数据
        if folder_name in self.json_data and part_name in self.json_data[folder_name]:
            self.json_data[folder_name][part_name][dict_type_en] = dict_data
            
            # 2. 更新树模型
            self.update_right_tree(folder_name)
            
            # 3. 保存到文件
            self.save_to_json(folder_name)

    def update_right_tree(self, folder_name):
        """更新右侧树视图"""
        # 找到对应的文件夹项
        for row in range(self.right_model.rowCount()):
            folder_item = self.right_model.item(row, 0)
            if folder_item.text() == folder_name:
                # 更新整个子树
                TreeModelManager.update_model_from_json(
                    self.right_model, 
                    folder_name, 
                    self.json_data
                )
                # 展开该节点
                self.right_tree.expand(folder_item.index())
                break

    def on_right_tree_clicked(self, index):
        """处理右侧树视图点击事件"""
        if not index.isValid():
            return
            
        item = self.right_model.itemFromIndex(index)
        text = item.text()  # 获取显示文本(中文)
        print(f"点击了右侧树节点: {text}")
        # 检查是否点击了字典节点(中文)
        if text in ["缺陷图字典", "典型图字典"]:
            # 获取part名称和原文件夹名
            part_item = item.parent()
            folder_item = part_item.parent() if part_item else None
            
            if part_item and folder_item:
                folder_name = folder_item.text()
                part_name = part_item.text()
                # 转换为英文键名获取数据
                dict_type = DictionaryBrowser.DICT_TYPE_MAPPING.get(text, "")
                if dict_type:
                    dict_data = self.json_data.get(folder_name, {}).get(DictionaryBrowser.DICT_TYPE_MAPPING.get(part_name, "")).get(dict_type, {})
                    
                    # 传递中文显示名称
                    self.dictionary_browser.show_dictionary(folder_name, part_name, text, dict_data)

    def get_item_path(self, item):
        """获取树节点路径"""
        path = []
        while item is not None:
            path.append(item.text())
            item = item.parent()
        return "/".join(reversed(path))
    
    def update_blade_name(self, folder_path):
        """从文件夹路径中提取叶片名称并更新字典浏览器"""
        # 假设叶片名称是文件夹路径的最后一部分
        blade_name = os.path.basename(folder_path.rstrip('/\\'))
        self.dictionary_browser.set_current_blade(blade_name)

    def on_left_tree_clicked(self, index):
        """处理左侧树视图选择变化事件"""
        if not index.isValid():
            return
        item = self.left_model.itemFromIndex(index)
        relative_path = self.folder_map.get(item.text(), "")
        print(f"🔍 选择文件夹: {relative_path}")
        self.folder_selected.emit(relative_path)

    def handle_immediate_save(self, folder_name):
        print(f"🔧 保存触发 [{folder_name}]")
        
        try:
            # 安全获取文件夹索引
            matches = self.right_model.match(
                self.right_model.index(0, 0),
                Qt.DisplayRole,
                folder_name,
                hits=1,
                flags=Qt.MatchExactly
            )
            
            if not matches:
                print(f"❌ 找不到文件夹: {folder_name}")
                return
                
            folder_index = matches[0]
            
            # 获取 turbinecode 值（使用标准模型访问方式）
            turbine_index = self.right_model.index(0, 1, folder_index)
            turbine_value = self.right_model.data(turbine_index, Qt.DisplayRole)
            
            print(f"📊 当前值 - turbinecode: {turbine_value or '<空>'}")
            
            # 更新数据
            TreeModelManager.update_json_from_model(self.right_model, self.json_data)
            self.save_to_json(folder_name)
            
        except Exception as e:
            print(f"❌ 保存错误: {str(e)}")

    def refresh_views(self):
        """刷新左右视图显示"""
        self.left_tree.setModel(None)  # 先重置模型
        self.left_tree.setModel(self.left_model)
        
        self.right_tree.setModel(None)
        self.right_tree.setModel(self.right_model)
        
        # 展开第一层节点
        for i in range(self.left_model.rowCount()):
            self.left_tree.expand(self.left_model.index(i, 0))
            self.right_tree.expand(self.right_model.index(i, 0))
    
    def copy_left_tree_text(self):
        """复制左侧树选中的文本"""
        index = self.left_tree.currentIndex()
        if index.isValid():
            text = self.left_model.data(index, Qt.DisplayRole)
            QApplication.clipboard().setText(text)
    
    def sync_right_tree_expand(self, index):
        """同步右侧树的展开状态（仅第一层）"""
        if not index.parent().isValid():  # 只处理第一层
            right_index = self.right_model.index(index.row(), 0)
            self.right_tree.expand(right_index)
    
    def sync_right_tree_collapse(self, index):
        """同步右侧树的折叠状态（仅第一层）"""
        if not index.parent().isValid():  # 只处理第一层
            right_index = self.right_model.index(index.row(), 0)
            self.right_tree.collapse(right_index)
    
    def sync_left_tree_expand(self, index):
        """同步左侧树的展开状态（仅第一层）"""
        if not index.parent().isValid():  # 只处理第一层
            left_index = self.left_model.index(index.row(), 0)
            self.left_tree.expand(left_index)
    
    def sync_left_tree_collapse(self, index):
        """同步左侧树的折叠状态（仅第一层）"""
        if not index.parent().isValid():  # 只处理第一层
            left_index = self.left_model.index(index.row(), 0)
            self.left_tree.collapse(left_index)
    
    def set_output_path(self, path):
        """设置JSON文件输出路径"""
        self.output_path = path
        if not os.path.exists(path):
            os.makedirs(path)
    
    def set_initial_folders(self, folder_list):
        """初始化文件夹结构"""
        self.left_model.clear()
        self.right_model.clear()
        self.left_model.setHorizontalHeaderLabels(["目录结构"])
        self.right_model.setHorizontalHeaderLabels(["信息", "值"])
        self.json_data = {}
        self.folder_map = {}
        
        if not folder_list:  # 添加空列表检查
            print("Warning: folder_list is empty")
            return
        
        # 创建左侧树模型
        self.left_model, self.folder_map = TreeModelManager.create_left_tree_model(folder_list)
        
        # 创建右侧树模型
        folder_names = [os.path.basename(f) for f in folder_list]
        self.right_model, self.json_data = TreeModelManager.create_right_tree_model(folder_names)
        
        # 初始化JSON文件
        for folder_name in folder_names:
            self.initialize_json_file(folder_name)
        
        self.refresh_views()  # 添加视图刷新
    
    def initialize_json_file(self, folder_name):
        """初始化JSON文件"""
        if not hasattr(self, 'output_path') or not self.output_path:
            QMessageBox.warning(self, "Warning", "Output path not set!")
            return
        
        json_filename = self.get_json_file_path(folder_name)
        
        # 如果文件已存在，则读取现有数据
        if os.path.exists(json_filename):
            existing_data = JsonFileHandler.read_json(json_filename)
            if existing_data:
                self.json_data[folder_name].update(existing_data)
                print(f"加载已有json文件: {json_filename},{existing_data}")
                TreeModelManager.update_model_from_json(
                    self.right_model, folder_name, self.json_data
                )
        else:
            # 创建新JSON文件
            self.save_to_json(folder_name)
    
    def get_json_file_path(self, folder_name):
        """
        获取JSON文件路径
        规则: 使用原始文件夹名 + .json后缀，保存到输出目录
        """
        # 确保文件夹名是有效的文件名
        safe_name = folder_name.strip()
        # 只添加.json后缀，不做其他修改
        if not safe_name.lower().endswith('.json'):
            safe_name += '.json'
        return os.path.join(self.output_path, safe_name)
    
    def on_right_data_changed(self, top_left, bottom_right):
        folder_item = self.right_model.itemFromIndex(top_left)
        while folder_item and folder_item.parent() is not None:
            folder_item = folder_item.parent()
        print(f"开始更改")
        if folder_item:
            folder_name = folder_item.text()
            print(f"变更检测到文件夹：{folder_name}")
            
            # 调试：打印变更前的数据
            print("变更前数据:", self.json_data.get(folder_name))
            
            # 更新数据
            TreeModelManager.update_json_from_model(self.right_model, self.json_data)
            
            # 调试：打印变更后的数据
            print("变更后数据:", self.json_data.get(folder_name))
            
            self.save_to_json(folder_name)
            self.data_changed.emit(self.json_data)
    
    def save_to_json(self, folder_name):
        """将类中现在的self.json_data保存到JSON文件

        Args:
            folder_name (str): 文件夹名称(要保存的json文件名，文件名为机组号)
        """
        json_path = self.get_json_file_path(folder_name)
        print(f"🛠️ 准备保存到: {json_path}")
        
        # 验证路径
        if os.path.isdir(json_path):
            print(f"❌ 错误：路径是目录，自动添加.json后缀")
            json_path += '.json'
        
        try:
            # 确保目录存在
            os.makedirs(os.path.dirname(json_path), exist_ok=True)
            
            # 写入文件
            with open(json_path, 'w', encoding='utf-8') as f:
                json.dump(self.json_data[folder_name], f, ensure_ascii=False, indent=4)
            print(f"✅ 成功保存: {json_path}")
            return True
        except Exception as e:
            print(f"❌ 保存失败: {str(e)}")
            return False

    def batch_update_folders(self, folder_mapping):
        """批量更新文件夹结构"""
        for folder_name, new_data in folder_mapping.items():
            if folder_name in self.json_data:
                self.json_data[folder_name].update(new_data)
                TreeModelManager.update_model_from_json(
                    self.right_model, folder_name, self.json_data
                )
                self.save_to_json(folder_name)
        
        self.data_changed.emit(self.json_data)
    
    def get_output_path(self):
        """获取当前输出路径"""
        return self.output_path
    
    def get_current_data(self):
        """获取当前所有数据"""
        return self.json_data


class ImageLoaderSignals(QObject):
    """加载信号"""
    image_loaded = Signal(str, QPixmap)  # 文件路径, 加载好的图片
    loading_finished = Signal()          # 加载完成
    progress_updated = Signal(int)       # 进度更新信号

class ImageLoaderWorker(QRunnable):
    """单个图片加载任务"""
    def __init__(self, file_path, signals, thumbnail_size = 300):
        super().__init__()
        self.file_path = file_path
        self.signals = signals
        self.thumbnail_size = thumbnail_size
    
    def run(self):
        """加载图片"""
        try:
            # 方法1: 直接读取并缩放为缩略图 (最快)
            reader = QImageReader(self.file_path)
            
            # 设置缩放尺寸以提高读取速度
            size = reader.size()
            if size.width() > 1000 or size.height() > 1000:
                reader.setScaledSize(QSize(1000, 1000))
            
            # 读取图像 (不进行高质量转换)
            image = reader.read()
            if image.isNull():
                return
            
            # 快速生成缩略图 (低质量)
            thumb = image.scaled(
                self.thumbnail_size, self.thumbnail_size,
                Qt.KeepAspectRatio, 
                Qt.FastTransformation  # 使用快速转换模式
            )
            
            # 转换为QPixmap
            pixmap = QPixmap.fromImage(thumb)
            if not pixmap.isNull():
                self.signals.image_loaded.emit(self.file_path, pixmap)
                
        except Exception as e:
            print(f"加载图片出错: {self.file_path}, 错误: {str(e)}")

class ImageLoader(QObject):
    """图片加载管理器"""
    def __init__(self, image_files, parent=None):
        super().__init__(parent)
        self.image_files = image_files
        self.signals = ImageLoaderSignals()
        self.thread_pool = QThreadPool.globalInstance()
        self.batch_size = 40  # 每批加载数量
        self.current_index = 0
        self.timer = QTimer()
        self.timer.timeout.connect(self.load_next_batch)
        self.cancel_requested = False
        
        # 暴露信号
        self.image_loaded = self.signals.image_loaded
        self.loading_finished = self.signals.loading_finished
        self.progress_updated = self.signals.progress_updated
    
    def start_loading(self):
        """开始加载图片"""
        self.current_index = 0
        self.cancel_requested = False
        self.load_next_batch()
    
    def load_next_batch(self):
        """加载下一批图片"""
        if self.cancel_requested:
            self.signals.loading_finished.emit()
            return
            
        try:
            end_index = min(self.current_index + self.batch_size, len(self.image_files))
            
            # 批量提交任务
            for i in range(self.current_index, end_index):
                file_path = self.image_files[i]
                worker = ImageLoaderWorker(file_path, self.signals)
                self.thread_pool.start(worker)
            
            self.current_index = end_index
            
            # 计算并发射进度
            progress = int((self.current_index / len(self.image_files)) * 100)
            self.signals.progress_updated.emit(progress)
            
            if self.current_index < len(self.image_files):
                # 更短的间隔加载下一批
                self.timer.start(50)  # 50ms后加载下一批
            else:
                # 全部加载完成
                self.signals.loading_finished.emit()
                self.signals.progress_updated.emit(100)
                self.timer.stop()
        except Exception as e:
            print(f"加载过程中出错: {str(e)}")
            self.signals.loading_finished.emit()
            self.timer.stop()

    def cancel(self):
        """取消加载"""
        self.cancel_requested = True
        self.timer.stop()


class ImageBrowser(QWidget):
    """主显示窗口"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.current_dir = ""
        self.image_labels = {}  # key: 文件路径, value: QLabel
        self.image_loader = None  # 图片加载器实例
        self.cols_per_row = 4  # 默认每行列数
        self.image_files = []  # 存储当前目录下的图片文件路径
        self.progress_dialog = None  # 进度条对话框
        self.thumbnail_size = 100  # 缩略图大小
        self.init_ui()
        
    def init_ui(self):
        """初始化界面"""
        self.setMinimumHeight(200)
        
        # 主布局
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        
        # 分割线
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        layout.addWidget(line)
        
        # 图片浏览区域
        self.scroll_area = QScrollArea()
        self.scroll_area.setWidgetResizable(True)
        
        # 使用网格布局
        self.image_container = QWidget()
        self.image_layout = QGridLayout(self.image_container)
        self.image_layout.setContentsMargins(5, 5, 5, 5)
        self.image_layout.setSpacing(10)
        self.image_layout.setAlignment(Qt.AlignTop)
        
        self.scroll_area.setWidget(self.image_container)
        layout.addWidget(self.scroll_area)
        
        # 初始状态提示
        self.placeholder_label = QLabel("请点击左侧目录查看图片")
        self.placeholder_label.setAlignment(Qt.AlignCenter)
        self.placeholder_label.setStyleSheet("color: gray; font-size: 14px;")
        self.scroll_area.setWidget(self.placeholder_label)
        
        # 创建简单的加载中图标
        self.loading_pixmap = self.create_loading_pixmap()
    
    def create_loading_pixmap(self):
        """创建加载中图标"""
        pixmap = QPixmap(50, 50)
        pixmap.fill(Qt.transparent)
        painter = QPainter(pixmap)
        painter.setPen(QColor(200, 200, 200))
        painter.drawEllipse(5, 5, 40, 40)
        painter.drawText(QRect(5, 5, 40, 40), Qt.AlignCenter, "...")
        painter.end()
        return pixmap
    
    def show_images(self, dir_path):
        """显示指定目录下的图片"""
        self.current_dir = dir_path
        self.clear_images()
        
        if not dir_path:
            self.scroll_area.setWidget(self.placeholder_label)
            return
        
        # 读取目录中的图片文件
        dir = QDir(dir_path)
        self.image_files = [dir.filePath(f) for f in dir.entryList(
            ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.JPG", "*.JPEG", "*.PNG", "*.BMP"], 
            QDir.Files, QDir.Name
        )]
        
        if not self.image_files:
            no_image_label = QLabel(f"该目录没有图片文件: {dir.path()}")
            no_image_label.setAlignment(Qt.AlignCenter)
            no_image_label.setStyleSheet("color: gray; font-size: 14px;")
            self.scroll_area.setWidget(no_image_label)
            return
        
        # 创建图片容器
        self.image_container = QWidget()
        self.image_layout = QGridLayout(self.image_container)
        self.image_layout.setContentsMargins(5, 5, 5, 5)
        self.image_layout.setSpacing(5)
        self.image_layout.setAlignment(Qt.AlignTop)
        
        self.scroll_area.setWidget(self.image_container)
        
        # 先创建所有占位符
        for i, file_path in enumerate(self.image_files):
            self.add_placeholder(file_path, i)
        
        # 创建并启动图片加载器
        if self.image_loader:
            self.image_loader.deleteLater()
        
        # 创建进度对话框
        self.progress_dialog = QProgressDialog("正在加载图片...", "取消", 0, 100, self)
        self.progress_dialog.setWindowTitle("图片加载进度")
        self.progress_dialog.setWindowModality(Qt.WindowModal)
        self.progress_dialog.setAutoClose(True)
        self.progress_dialog.setAutoReset(True)
        self.progress_dialog.canceled.connect(self.cancel_loading)
        
        self.image_loader = ImageLoader(self.image_files)
        self.image_loader.image_loaded.connect(self.update_image)
        self.image_loader.loading_finished.connect(self.on_loading_finished)
        self.image_loader.progress_updated.connect(self.update_progress)
        self.image_loader.start_loading()
    
    def cancel_loading(self):
        """取消加载"""
        if self.image_loader:
            self.image_loader.cancel()
            self.image_loader.deleteLater()
            self.image_loader = None
        
        if self.progress_dialog:
            self.progress_dialog.close()
            self.progress_dialog = None

    def update_progress(self, value):
        """更新进度条"""
        if self.progress_dialog:
            self.progress_dialog.setValue(value)

    def on_loading_finished(self):
        """图片加载完成后的处理"""
        if self.progress_dialog:
            self.progress_dialog.close()
            self.progress_dialog = None
        
        if self.image_loader:
            self.image_loader.deleteLater()
            self.image_loader = None
    
    def add_placeholder(self, file_path, index):
        """添加占位符到指定位置"""
        self.update_columns_count()
        row = index // self.cols_per_row
        col = index % self.cols_per_row
        
        placeholder = self.create_placeholder(file_path)
        self.image_layout.addWidget(placeholder, row, col)
        self.image_labels[file_path] = placeholder
    
    def create_placeholder(self, file_path):
        """创建单个占位符 - 添加双击事件和拖动支持"""
        file_name = QDir(file_path).dirName()
        
        placeholder = QLabel()
        placeholder.setFixedSize(self.thumbnail_size, self.thumbnail_size)
        placeholder.setAlignment(Qt.AlignCenter)
        placeholder.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        placeholder.setStyleSheet("""
            QLabel { 
                border: 1px solid #eee; 
                background: white;
                qproperty-alignment: 'AlignCenter';
            }
            QLabel:hover { 
                border: 1px solid #aaa; 
                background: #f5f5f5;
            }
        """)
        placeholder.setPixmap(self.loading_pixmap)
        placeholder.setProperty("file_path", file_path)
        placeholder.setToolTip(f"加载中: {file_name}")
        
        # 安装事件过滤器处理鼠标事件
        placeholder.installEventFilter(self)
        
        # 存储拖动相关状态
        placeholder.setProperty("drag_start_pos", None)
        placeholder.setProperty("drag_threshold", 5)  # 拖动触发阈值(像素)
        
        return placeholder
    
    def update_image(self, file_path, pixmap):
        """更新图片显示"""
        if file_path in self.image_labels:
            label = self.image_labels[file_path]
            label.setPixmap(pixmap.scaled(
                self.thumbnail_size, self.thumbnail_size,
                Qt.KeepAspectRatio, Qt.SmoothTransformation
            ))
            file_name = QDir(file_path).dirName()
            label.setToolTip(f"文件名: {file_name}")
            label.setStyleSheet("""
                QLabel { border: 1px solid #ddd; background: white; }
                QLabel:hover { border: 1px solid #aaa; background: #f0f0f0; }
            """)

    def open_image(self, file_path):
        """用系统默认程序打开图片"""
        try:
            if os.name == 'nt':  # Windows
                os.startfile(file_path)
            elif os.name == 'posix':  # macOS/Linux
                opener = "open" if sys.platform == "darwin" else "xdg-open"
                import subprocess
                subprocess.call([opener, file_path])
        except Exception as e:
            QMessageBox.warning(self, "打开失败", f"无法打开图片: {str(e)}")

    def update_columns_count(self):
        """根据当前宽度更新每行列数"""
        if not self.image_container or not self.isVisible():
            return
        
        try:
            # 获取容器实际可用宽度（减去左右边距和间距）
            margins = self.image_layout.contentsMargins()
            spacing = self.image_layout.spacing()
            
            # 可用宽度 = 容器宽度 - 左边距 - 右边距
            available_width = self.image_container.width() - margins.left() - margins.right()
            
            # 每个项目需要的宽度 = 缩略图宽度 + 间距
            item_width = self.thumbnail_size + spacing
            
            # 计算最大列数（至少1列）
            self.cols_per_row = max(1, available_width // item_width)
        except RuntimeError:
            # 对象已被删除，忽略错误
            pass
    
    def resizeEvent(self, event):
        """窗口大小改变时重新计算列数"""
        super().resizeEvent(event)
        if not self.current_dir or not self.image_labels:
            return
        
        try:
            self.update_columns_count()
            QTimer.singleShot(100, self.recalculate_layout)
        except RuntimeError:
            # 对象已被删除，忽略错误
            pass
    
    def recalculate_layout(self):
        """重新计算布局"""
        try:
            if not hasattr(self, 'image_files') or not self.image_files or not self.image_container:
                return
                
            # 保存当前滚动位置
            scroll_bar = self.scroll_area.verticalScrollBar()
            old_scroll = scroll_bar.value()
            
            # 清除现有布局
            for i in reversed(range(self.image_layout.count())): 
                widget = self.image_layout.itemAt(i).widget()
                if widget:
                    self.image_layout.removeWidget(widget)
            
            # 更新列数
            self.update_columns_count()
            
            # 重新添加已加载的图片
            for i, file_path in enumerate(self.image_files):
                if file_path in self.image_labels:
                    row = i // self.cols_per_row
                    col = i % self.cols_per_row
                    self.image_layout.addWidget(self.image_labels[file_path], row, col)
            
            # 设置列拉伸，使最后一列填充剩余空间
            for c in range(self.cols_per_row):
                self.image_layout.setColumnStretch(c, 1)
            
            # 恢复滚动位置
            scroll_bar.setValue(old_scroll)
        except RuntimeError:
            # 对象已被删除，忽略错误
            pass
    
    def clear_images(self):
        """清除所有图片"""
        for label in self.image_labels.values():
            try:
                label.deleteLater()
            except RuntimeError:
                pass
        self.image_labels.clear()
        
        if self.image_loader:
            try:
                self.image_loader.deleteLater()
            except RuntimeError:
                pass
            self.image_loader = None
    
    def eventFilter(self, obj, event):
        """处理鼠标事件以实现双击和拖动"""
        try:
            if isinstance(obj, QLabel) and obj in self.image_labels.values():
                file_path = obj.property("file_path")
                
                if event.type() == QEvent.MouseButtonPress:
                    if event.button() == Qt.LeftButton:
                        # 记录鼠标按下位置和时间
                        obj.setProperty("drag_start_pos", event.pos())
                        obj.setProperty("press_time", QDateTime.currentMSecsSinceEpoch())
                        return True
                
                elif event.type() == QEvent.MouseMove:
                    if obj.property("drag_start_pos") is not None:
                        # 检查是否达到拖动阈值
                        start_pos = obj.property("drag_start_pos")
                        if (event.pos() - start_pos).manhattanLength() > obj.property("drag_threshold"):
                            # 触发拖动
                            self.start_drag(obj, event)
                            obj.setProperty("drag_start_pos", None)
                            return True
                
                elif event.type() == QEvent.MouseButtonRelease:
                    if event.button() == Qt.LeftButton and obj.property("drag_start_pos") is not None:
                        # 只清除状态，不触发任何动作
                        obj.setProperty("drag_start_pos", None)
                        return True
                
                elif event.type() == QEvent.MouseButtonDblClick:
                    if event.button() == Qt.LeftButton:
                        self.open_image(file_path)
                        return True
                    
        except RuntimeError:
            pass
        
        return super().eventFilter(obj, event)
    
    def open_image(self, file_path):
        """用默认方式打开图片"""
        try:
            import os
            if os.name == 'nt':  # Windows
                os.startfile(file_path)
            elif os.name == 'posix':  # macOS/Linux
                import subprocess
                opener = "open" if sys.platform == "darwin" else "xdg-open"
                subprocess.call([opener, file_path])
        except Exception as e:
            QMessageBox.warning(self, "打开失败", f"无法打开图片: {str(e)}")
    
    def start_drag(self, label, event):
        """开始拖动图片"""
        try:
            if label.pixmap() and not label.pixmap().isNull():
                file_path = label.property("file_path")
                
                drag = QDrag(self)
                mime_data = QMimeData()
                
                # 设置多种MIME类型以支持不同场景
                url = QUrl.fromLocalFile(file_path)
                mime_data.setUrls([url])
                mime_data.setText(file_path)
                mime_data.setImageData(label.pixmap().toImage())
                
                drag.setMimeData(mime_data)
                
                # 设置拖动时的缩略图
                thumb = label.pixmap().scaled(
                    200, 200, Qt.KeepAspectRatio, Qt.SmoothTransformation
                )
                drag.setPixmap(thumb)
                
                # 设置热点位置
                drag.setHotSpot(QPoint(
                    thumb.width() // 2,
                    thumb.height() // 2
                ))
                
                # 执行拖动操作
                drag.exec(Qt.CopyAction | Qt.MoveAction)
        except Exception as e:
            print(f"拖动失败: {str(e)}")

class OverlayLabel(QLabel):
    """支持叠加图标的标签控件"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self._overlay_icon = None
    
    def set_overlay_icon(self, standard_pixmap):
        """设置叠加图标"""
        self._overlay_icon = standard_pixmap
        self.update()
    
    def paintEvent(self, event):
        """重绘事件，绘制图标叠加"""
        super().paintEvent(event)
        
        if self._overlay_icon and self.pixmap():
            # 获取系统标准图标
            icon = self.style().standardIcon(self._overlay_icon)
            pixmap = icon.pixmap(24, 24)  # 图标大小
            
            # 在右下角绘制图标
            painter = QPainter(self)
            margin = 5
            x = self.width() - pixmap.width() - margin
            y = self.height() - pixmap.height() - margin
            painter.drawPixmap(x, y, pixmap)
            painter.end()

class DictionaryBrowser(QWidget):
    """字典浏览编辑窗口"""
    dictionary_updated = Signal(str, str, str, dict)  # 原文件夹名, part名称, 字典类型(英文), 字典数据
    
    # 中英文映射字典
    DICT_TYPE_MAPPING = {
        "缺陷图字典": "defect_picture_dict",
        "典型图字典": "typical_picture_dict",
        "叶片1" : "part1",
        "叶片2" : "part2",
        "叶片3" : "part3",
    }
    
    REVERSE_DICT_MAPPING = {v: k for k, v in DICT_TYPE_MAPPING.items()}

    def __init__(self, parent=None):
        super().__init__(parent)
        self.original_folder_name = ""  # 原文件夹名
        self.current_part = ""         # 当前part名称(part1/part2/part3)
        self.current_dict_type = ""    # 当前字典类型(英文)
        self.current_dict_data = {}    # 当前字典数据
        self.init_ui()

    def init_ui(self):
        """初始化界面"""
        self.setMinimumWidth(300)
        layout = QVBoxLayout(self)
        layout.setContentsMargins(5, 5, 5, 5)
        
        # 标题标签
        self.title_label = QLabel("请选择字典节点")
        self.title_label.setAlignment(Qt.AlignCenter)
        self.title_label.setStyleSheet("font-weight: bold; font-size: 14px;")
        layout.addWidget(self.title_label)
        
        # 图片浏览区域
        self.scroll_area = QScrollArea()
        self.scroll_area.setWidgetResizable(True)
        layout.addWidget(self.scroll_area)
        
        # 初始状态提示
        self.set_placeholder("请点击右侧JSON树中的字典节点")
        
        # 设置拖放接收
        self.setAcceptDrops(True)

    def set_placeholder(self, text):
        """设置占位文本"""
        label = QLabel(text)
        label.setAlignment(Qt.AlignCenter)
        label.setStyleSheet("color: gray; font-size: 14px;")
        self.scroll_area.setWidget(label)
    
    def show_dictionary(self, folder_name, part_name, dict_type_display, dict_data):
        """显示指定字典内容
        :param dict_type_display: 显示用的字典类型名称(中文)
        """
        self.original_folder_name = folder_name
        self.current_part = self.DICT_TYPE_MAPPING.get(part_name, "")
        # 转换为英文键名存储
        self.current_dict_type = self.DICT_TYPE_MAPPING.get(dict_type_display, "")
        self.current_dict_data = dict_data or {}
        
        self.update_title()
        self.refresh_view()
    
    def update_title(self):
        """更新标题显示(使用中文)"""
        if not all([self.original_folder_name, self.current_part, self.current_dict_type]):
            self.title_label.setText("请选择字典节点")
        else:
            # 转换为中文显示
            dict_name = self.REVERSE_DICT_MAPPING.get(self.current_dict_type, self.current_dict_type)
            self.title_label.setText(f"{self.original_folder_name} - {self.current_part} - {dict_name}")
    
    def refresh_view(self):
        """刷新视图"""
        # 安全清除旧内容
        old_widget = self.scroll_area.takeWidget()
        if old_widget:
            old_widget.deleteLater()
        
        if not all([self.original_folder_name, self.current_part, self.current_dict_type]):
            self.set_placeholder("请选择有效的字典节点")
            return
        
        if not self.current_dict_data:
            self.set_placeholder("该字典没有数据")
            return
        
        # 创建新的容器
        container = QWidget()
        layout = QVBoxLayout(container)  # 改为垂直布局
        layout.setContentsMargins(5, 5, 5, 5)
        layout.setSpacing(5)
        
        # 创建水平流式布局的容器
        flow_widget = QWidget()
        flow_layout = QHBoxLayout(flow_widget)
        flow_layout.setContentsMargins(0, 0, 0, 0)
        flow_layout.setSpacing(5)
        flow_layout.setAlignment(Qt.AlignLeft | Qt.AlignTop)  # 左对齐且顶部对齐
        
        # 添加图片项
        for key, img_path in self.current_dict_data.items():
            self._add_image_item(flow_layout, img_path, key)
        
        # 添加流式布局到主布局
        layout.addWidget(flow_widget)
        layout.addStretch(1)  # 添加拉伸因子使内容保持在顶部
        
        self.scroll_area.setWidget(container)

    def _check_description_format(self, key):
        """检查描述是否符合标准格式"""
        if not key or "_" not in key:
            return False
        
        parts = key.split("_")
        # 检查是否有足够的字段（序号+6个标准字段+建议）
        return len(parts) >= 8

    def _add_image_item(self, parent_layout, img_path, key):
        """添加单个图片项 - 只显示图片，符合格式的显示对勾"""
        thumbnail_size = 150
        
        # 创建容器widget
        item_widget = QWidget()
        
        # 设置悬停提示
        hover_text = f"图片路径: {img_path}\n\n描述详情:\n{self.get_description_display(key)}"
        item_widget.setToolTip(hover_text)
        
        item_layout = QVBoxLayout(item_widget)
        item_layout.setContentsMargins(5, 5, 5, 5)
        item_layout.setSpacing(0)
        
        # 图片标签 - 使用自定义的OverlayLabel类
        img_label = OverlayLabel()
        img_label.setFixedSize(thumbnail_size, thumbnail_size)
        img_label.setAlignment(Qt.AlignCenter)
        
        # 检查描述是否符合格式
        is_valid_format = self._check_description_format(key)
        
        # 加载图片
        if os.path.exists(img_path):
            pixmap = QPixmap(img_path)
            if not pixmap.isNull():
                img_label.setPixmap(pixmap.scaled(
                    thumbnail_size, thumbnail_size,
                    Qt.KeepAspectRatio, Qt.SmoothTransformation
                ))
                # 如果格式正确，显示对勾
                if is_valid_format:
                    img_label.set_overlay_icon(QStyle.SP_DialogApplyButton)
            else:
                img_label.setText("无效图片")
                img_label.setStyleSheet("color: red;")
        else:
            img_label.setText("图片不存在")
            img_label.setStyleSheet("color: red;")
        
        item_layout.addWidget(img_label)
        
        # 添加上下文菜单
        item_widget.setContextMenuPolicy(Qt.CustomContextMenu)
        item_widget.customContextMenuRequested.connect(
            lambda pos, path=img_path, k=key: self.show_context_menu(pos, path, k))
        
        parent_layout.addWidget(item_widget)
    
    def get_description_display(self, key):
        """获取描述信息的显示文本 - 用于悬停提示"""
        if not key or "_" not in key:
            return "⚠ 无描述信息"
        
        parts = key.split("_")
        if len(parts) < 8:
            return f"⚠ 描述格式不完整\n当前描述: {key}"
        
        # 格式化显示
        description = (
            f"✔ 已编辑描述\n"
            f"缺陷类型: {parts[1]}\n"
            f"位置: {parts[2]}\n"
            f"尺寸: {parts[3]}\n"
            f"可见程度: {parts[4]}\n"
            f"紧急程度: {parts[5]}\n"
            f"危重等级: {parts[6]}\n"
            f"维修建议: {'_'.join(parts[7:])}"
        )
        
        return description


    def dragEnterEvent(self, event):
        """拖拽进入事件"""
        if event.mimeData().hasUrls():
            event.acceptProposedAction()
    
    def dropEvent(self, event):
        """处理拖放事件"""
        if not all([self.original_folder_name, self.current_part, self.current_dict_type]):
            QMessageBox.warning(self, "警告", "请先选择有效的字典节点")
            return
        
        urls = event.mimeData().urls()
        if urls:
            img_path = urls[0].toLocalFile()
            if os.path.isfile(img_path):
                # 生成新键(使用当前最大数字+1)
                keys = [int(k) for k in self.current_dict_data.keys() if k.isdigit()]
                max_key = max(keys) if keys else -1
                new_key = str(max_key + 1)
                
                # 更新字典数据
                self.current_dict_data[new_key] = img_path
                
                # 刷新显示
                self.refresh_view()
                
                # 发射信号通知更新 (包含英文字典类型)
                self.dictionary_updated.emit(
                    self.original_folder_name,
                    self.current_part,
                    self.current_dict_type,  # 英文键名
                    self.current_dict_data
                )
        
    def show_context_menu(self, pos, img_path, key):
        """显示上下文菜单"""
        menu = QMenu(self)
        
        # 删除图片动作
        delete_action = QAction("删除图片", self)
        delete_action.triggered.connect(lambda: self.delete_image(key))
        
        # 添加编辑描述动作 (仅当是缺陷图字典时显示)
        edit_desc_action = None
        if self.current_dict_type == "defect_picture_dict":
            edit_desc_action = QAction("编辑缺陷描述", self)
            edit_desc_action.triggered.connect(lambda: self.edit_defect_description(key, img_path))
            menu.addAction(edit_desc_action)
        
        menu.addAction(delete_action)
        menu.exec_(QCursor.pos())

    def edit_defect_description(self, key, img_path):
        """编辑缺陷图描述"""
        # 获取当前描述 (如果没有则使用默认格式)
        current_desc = self.current_dict_data.get(key, "") if key in self.current_dict_data else ""
        
        # 创建并显示编辑对话框
        editor = DefectDescriptionEditor(self, current_description=current_desc, img_path=img_path)
        if editor.exec_() == QDialog.Accepted:
            # 获取新描述
            new_desc = editor.get_description()
            
            # 更新字典数据 - 删除旧键并添加新键
            if key in self.current_dict_data:
                # 保留图片路径，只更新键名
                img_path = self.current_dict_data.pop(key)
                self.current_dict_data[new_desc] = img_path
            
            # 刷新显示
            self.refresh_view()
            
            # 发射信号通知更新
            self.dictionary_updated.emit(
                self.original_folder_name,
                self.current_part,
                self.current_dict_type,
                self.current_dict_data
            )


    def delete_image(self, key):
        """删除指定图片"""
        reply = QMessageBox.question(
            self, "确认删除", 
            f"确定要从字典中删除Key为 {key} 的图片吗?", 
            QMessageBox.Yes | QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            # 从字典中删除
            self.current_dict_data.pop(key, None)
            
            # 刷新显示
            self.refresh_view()
            
            # 发射信号通知更新
            self.dictionary_updated.emit(
                self.original_folder_name,
                self.current_part,
                self.current_dict_type,
                self.current_dict_data
            )

class DefectDescriptionEditor(QDialog):
    def __init__(self, parent=None, current_description="", img_path=""):
        super().__init__(parent)
        self.setWindowTitle("编辑缺陷图描述")
        self.setMinimumSize(1000, 700)  # 增大窗口尺寸以适应两个编辑器
        self.setStyleSheet(DEFECT_EDIT_WINDOW_STYLE)
        
        self.current_description = current_description
        self.img_path = img_path
        self.init_ui()
        
    def init_ui(self):
        main_layout = QHBoxLayout(self)
        main_layout.setContentsMargins(5, 5, 5, 5)
        
        # 左侧 - 图片编辑器 (使用DefectMarkEditor)
        self.image_editor = DefectMarkEditor(image_path=self.img_path)
        self.image_editor.setMinimumSize(600, 600)
        main_layout.addWidget(self.image_editor, stretch=2)
        
        # 右侧 - 描述编辑器
        self.init_description_editor()
        main_layout.addWidget(self.desc_editor_widget, stretch=1)
        
    def init_description_editor(self):
        self.desc_editor_widget = QWidget()
        layout = QVBoxLayout(self.desc_editor_widget)
        layout.setContentsMargins(5, 0, 0, 0)
        
        # 创建一个简单的 QTextEdit 作为消息浏览器
        self.message_browser = QTextEdit()
        self.message_browser.setReadOnly(True)
        layout.addWidget(self.message_browser)
        
        # 解析当前描述
        parts = self.parse_current_description()
        
        # 1. 缺陷类型
        type_group = QGroupBox("缺陷类型")
        type_group.setStyleSheet(GROUP_BOX_STYLE)
        type_layout = QHBoxLayout(type_group)
        
        # 使用 SmartDropdown 作为缺陷类型选择器
        self.defect_type_dropdown = SmartDropdown(
            dropdown_type="缺陷类型",
            messagebrowser=self.message_browser,
            parent=self
        )
        if "type" in parts:
            self.defect_type_dropdown.combo_box.setCurrentText(parts["type"])
        type_layout.addWidget(self.defect_type_dropdown)
        
        layout.addWidget(type_group)
        
        # 2. 缺陷位置
        location_group = QGroupBox("缺陷位置")
        location_group.setStyleSheet(GROUP_BOX_STYLE)
        location_layout = QVBoxLayout(location_group)
        
        self.location_edit = QLineEdit()
        self.location_edit.setStyleSheet(LINE_EDIT_STYLE)
        self.location_edit.setText(parts.get("location", ""))
        location_layout.addWidget(self.location_edit)
        
        layout.addWidget(location_group)
        
        # 3. 缺陷尺寸
        size_group = QGroupBox("缺陷尺寸")
        size_group.setStyleSheet(GROUP_BOX_STYLE)
        size_layout = QVBoxLayout(size_group)
        
        # 轴向尺寸
        axial_container = QWidget()
        axial_container.setStyleSheet(DIMENSION_CONTAINER_STYLE)
        axial_layout = QHBoxLayout(axial_container)
        axial_layout.setContentsMargins(0, 0, 0, 0)
        
        axial_label = QLabel("轴向尺寸:")
        axial_label.setStyleSheet(DIMENSION_LABEL_STYLE)
        axial_layout.addWidget(axial_label)
        
        self.axial_edit = QLineEdit()
        self.axial_edit.setStyleSheet(DIMENSION_EDIT_STYLE)
        self.axial_edit.setText(parts.get("axial_size", ""))
        axial_layout.addWidget(self.axial_edit)
        
        # 添加单位标签
        axial_unit = QLabel("mm")
        axial_unit.setStyleSheet(LABEL_STYLE)
        axial_layout.addWidget(axial_unit)
        
        axial_layout.addStretch()
        size_layout.addWidget(axial_container)
        
        # 弦向尺寸
        chord_container = QWidget()
        chord_container.setStyleSheet(DIMENSION_CONTAINER_STYLE)
        chord_layout = QHBoxLayout(chord_container)
        chord_layout.setContentsMargins(0, 0, 0, 0)
        
        chord_label = QLabel("弦向尺寸:")
        chord_label.setStyleSheet(DIMENSION_LABEL_STYLE)
        chord_layout.addWidget(chord_label)
        
        self.chord_edit = QLineEdit()
        self.chord_edit.setStyleSheet(DIMENSION_EDIT_STYLE)
        self.chord_edit.setText(parts.get("chord_size", ""))
        chord_layout.addWidget(self.chord_edit)
        
        # 添加单位标签
        chord_unit = QLabel("mm")
        chord_unit.setStyleSheet(LABEL_STYLE)
        chord_layout.addWidget(chord_unit)
        
        chord_layout.addStretch()
        size_layout.addWidget(chord_container)
        
        layout.addWidget(size_group)
        
        # 4. 其他属性
        attr_group = QGroupBox("其他属性")
        attr_group.setStyleSheet(GROUP_BOX_STYLE)
        attr_layout = QGridLayout(attr_group)
        
        # 可见程度
        visibility_label = QLabel("可见程度:")
        visibility_label.setStyleSheet(LABEL_STYLE)
        self.visibility_combo = QComboBox()
        self.visibility_combo.setStyleSheet(COMBO_BOX_STYLE)
        self.visibility_combo.addItems(["轻微", "一般", "重要", "严重"])
        if "visibility" in parts:
            self.visibility_combo.setCurrentText(parts["visibility"])
        attr_layout.addWidget(visibility_label, 0, 0)
        attr_layout.addWidget(self.visibility_combo, 0, 1)
        
        # 紧急程度
        urgency_label = QLabel("紧急程度:")
        urgency_label.setStyleSheet(LABEL_STYLE)
        self.urgency_combo = QComboBox()
        self.urgency_combo.setStyleSheet(COMBO_BOX_STYLE)
        self.urgency_combo.addItems(["不紧急", "一般", "紧急", "非常紧急"])
        if "urgency" in parts:
            self.urgency_combo.setCurrentText(parts["urgency"])
        attr_layout.addWidget(urgency_label, 1, 0)
        attr_layout.addWidget(self.urgency_combo, 1, 1)
        
        # 危重等级
        severity_label = QLabel("危重等级:")
        severity_label.setStyleSheet(LABEL_STYLE)
        self.severity_combo = QComboBox()
        self.severity_combo.setStyleSheet(COMBO_BOX_STYLE)
        self.severity_combo.addItems(["轻微", "一般", "重要", "严重"])
        if "severity" in parts:
            self.severity_combo.setCurrentText(parts["severity"])
        attr_layout.addWidget(severity_label, 2, 0)
        attr_layout.addWidget(self.severity_combo, 2, 1)
        
        layout.addWidget(attr_group)
        
        # 5. 维修建议
        suggestion_group = QGroupBox("维修建议")
        suggestion_group.setStyleSheet(GROUP_BOX_STYLE)
        suggestion_layout = QVBoxLayout(suggestion_group)
        
        self.suggestion_edit = QLineEdit()
        self.suggestion_edit.setStyleSheet(LINE_EDIT_STYLE)
        self.suggestion_edit.setText(parts.get("suggestion", ""))
        suggestion_layout.addWidget(self.suggestion_edit)
        
        layout.addWidget(suggestion_group)
        
        # 按钮
        button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        button_box.setStyleSheet(PRIMARY_BUTTON_STYLE)
        button_box.accepted.connect(self.accept)
        button_box.rejected.connect(self.reject)
        layout.addWidget(button_box)
    
    def parse_current_description(self):
        """解析当前描述字符串"""
        parts = {
            "type": "",
            "location": "",
            "axial_size": "",
            "chord_size": "",
            "visibility": "一般",
            "urgency": "一般",
            "severity": "一般",
            "suggestion": ""
        }
        
        if not self.current_description:
            return parts
            
        # 尝试解析格式: 1_缺陷类型_缺陷位置_缺陷尺寸_可见程度_紧急程度_危重等级_维修建议
        desc_parts = self.current_description.split("_")
        if len(desc_parts) >= 8:
            parts["type"] = desc_parts[1]
            parts["location"] = desc_parts[2]
            
            # 解析尺寸 (格式: 轴向XXmm弦向XXmm)
            size_str = desc_parts[3]
            axial_match = re.search(r"轴向(\d+)mm", size_str)
            chord_match = re.search(r"弦向(\d+)mm", size_str)
            if axial_match:
                parts["axial_size"] = axial_match.group(1)
            if chord_match:
                parts["chord_size"] = chord_match.group(1)
            
            if len(desc_parts) > 4:
                parts["visibility"] = desc_parts[4]
            if len(desc_parts) > 5:
                parts["urgency"] = desc_parts[5]
            if len(desc_parts) > 6:
                parts["severity"] = desc_parts[6]
            if len(desc_parts) > 7:
                parts["suggestion"] = "_".join(desc_parts[7:])
        
        return parts
    
    def get_description(self):
        """生成描述字符串"""
        # 生成序号 (使用当前时间戳的最后4位确保唯一性)
        seq = str(int(time.time()) % 10000)
        
        # 获取各字段值
        defect_type = self.defect_type_dropdown.combo_box.currentText()
        location = self.location_edit.text().strip()
        axial_size = self.axial_edit.text().strip()
        chord_size = self.chord_edit.text().strip()
        visibility = self.visibility_combo.currentText()
        urgency = self.urgency_combo.currentText()
        severity = self.severity_combo.currentText()
        suggestion = self.suggestion_edit.text().strip()
        
        # 构建尺寸字符串
        size_str = ""
        if axial_size or chord_size:
            size_parts = []
            if axial_size:
                size_parts.append(f"轴向{axial_size}mm")
            if chord_size:
                size_parts.append(f"弦向{chord_size}mm")
            size_str = "".join(size_parts)
        
        # 构建完整描述
        description = f"{seq}_{defect_type}_{location}_{size_str}_{visibility}_{urgency}_{severity}_{suggestion}"
        return description