StyleDesignerTab.qml 10.6 KB
import QtQuick
import QtQuick.Controls.Basic
import QtQuick.Layouts
import "components"
import "." as App

Item {
    id: tab

    // ===== 状态 =====
    // 8 字段表单值(与 jewelry.categories 顺序一致)
    property var formData: ({
        "主石形状": "",
        "主石材质": "",
        "金属": "",
        "花头形式": "",
        "戒臂结构": "",
        "戒臂处理": "",
        "辅石镶嵌": "",
        "特殊元素": "",
    })
    property string assembledPrompt: ""
    property string currentTaskId: ""
    property string lastResultPath: ""
    property string statusText: "● 就绪"
    property color statusColor: App.Theme.success

    function updateField(category, value) {
        var copy = Object.assign({}, tab.formData)
        copy[category] = value || ""
        tab.formData = copy
        tab.assembledPrompt = jewelry.previewPrompt(copy)
    }

    function reassemble() {
        tab.assembledPrompt = jewelry.previewPrompt(tab.formData)
    }

    function submit() {
        if (tab.currentTaskId !== "") return
        if (tab.assembledPrompt.trim().length === 0) {
            tab.statusText = "● 选几个字段先"
            tab.statusColor = App.Theme.danger
            return
        }
        try {
            // submitTask 同步返回 task_id,自己记下来给信号过滤用
            var taskId = imageGen.submitTask(
                tab.assembledPrompt,
                [],            // 款式设计不带参考图
                "1:1",         // 珠宝出图常用比例
                "2K",
                "慢速模式"     // Pro 模型,质量优先
            )
            tab.currentTaskId = taskId
            tab.statusText = "● 已提交"
            tab.statusColor = App.Theme.accent
        } catch (e) {
            tab.statusText = "● " + e
            tab.statusColor = App.Theme.danger
        }
    }

    Component.onCompleted: reassemble()

    Connections {
        target: jewelry
        function onLibraryChanged(category) {
            optionsRepeater.reloadAll()
        }
    }

    // 监听生成信号 — 只对自己提交的 task 做反应(taskId 唯一可区分本 tab vs 图片生成 tab)
    Connections {
        target: imageGen

        function onTaskProgress(taskId, progress, msg) {
            if (taskId !== tab.currentTaskId) return
            tab.statusText = "● " + (msg || "生成中…")
            tab.statusColor = App.Theme.accent
        }
        function onTaskCompleted(taskId, resultPath, prompt, model) {
            if (taskId !== tab.currentTaskId) return
            tab.lastResultPath = resultPath
            tab.currentTaskId = ""
            tab.statusText = "● 已完成"
            tab.statusColor = App.Theme.success
        }
        function onTaskFailed(taskId, error) {
            if (taskId !== tab.currentTaskId) return
            tab.currentTaskId = ""
            tab.statusText = "● " + (error || "失败")
            tab.statusColor = App.Theme.danger
        }
    }

    RowLayout {
        anchors.fill: parent
        anchors.margins: App.Theme.space5
        spacing: App.Theme.space4

        // ===== 左侧:8 字段表单 =====
        Card {
            Layout.preferredWidth: 360
            Layout.fillHeight: true

            ColumnLayout {
                anchors.fill: parent
                anchors.margins: App.Theme.space4
                spacing: App.Theme.space3

                CaptionLabel {
                    text: "款式字段"
                    font.pointSize: App.Theme.fontLg
                }

                ScrollView {
                    Layout.fillWidth: true
                    Layout.fillHeight: true
                    clip: true

                    ColumnLayout {
                        width: parent.width
                        spacing: App.Theme.space3

                        Repeater {
                            id: optionsRepeater
                            model: jewelry.categories  // 8 个类别名

                            // task #15b 词库变化时重新拉
                            function reloadAll() {
                                // QML 自动重新评估 model:,主动 reset
                                model = []
                                model = jewelry.categories
                            }

                            delegate: ColumnLayout {
                                Layout.fillWidth: true
                                spacing: 4

                                CaptionLabel {
                                    text: modelData
                                    font.pointSize: App.Theme.fontBase
                                }
                                ThemedComboBox {
                                    Layout.fillWidth: true
                                    // 头插一个 "(不选)" 让用户能清空字段
                                    model: ["(不选)"].concat(jewelry.getOptions(modelData))
                                    onActivated: {
                                        var v = currentText === "(不选)" ? "" : currentText
                                        tab.updateField(modelData, v)
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

        // ===== 右侧:Prompt 预览 + 生成 + 结果 =====
        ColumnLayout {
            Layout.fillWidth: true
            Layout.fillHeight: true
            spacing: App.Theme.space4

            // Prompt 预览卡
            Card {
                Layout.fillWidth: true
                Layout.preferredHeight: 200

                ColumnLayout {
                    anchors.fill: parent
                    anchors.margins: App.Theme.space4
                    spacing: App.Theme.space3

                    RowLayout {
                        spacing: App.Theme.space3
                        CaptionLabel {
                            text: "Prompt 预览"
                            font.pointSize: App.Theme.fontLg
                        }
                        Item { Layout.fillWidth: true }
                        Label {
                            text: tab.assembledPrompt.length + " 字"
                            font.family: App.Theme.fontFamily
                            font.pointSize: App.Theme.fontXs
                            color: App.Theme.textTertiary
                        }
                    }

                    ScrollView {
                        Layout.fillWidth: true
                        Layout.fillHeight: true

                        Rectangle {
                            anchors.fill: parent
                            color: App.Theme.bgSubtle
                            radius: App.Theme.radiusMd

                            Text {
                                anchors.fill: parent
                                anchors.margins: App.Theme.space3
                                text: tab.assembledPrompt
                                color: App.Theme.textPrimary
                                font.family: App.Theme.fontFamily
                                font.pointSize: App.Theme.fontSm
                                wrapMode: Text.Wrap
                            }
                        }
                    }
                }
            }

            // 操作行
            RowLayout {
                spacing: App.Theme.space3
                Layout.fillWidth: true

                PrimaryButton {
                    text: tab.currentTaskId !== "" ? "生成中…" : "生成图片"
                    enabled: tab.currentTaskId === ""
                    onClicked: tab.submit()
                }
                SecondaryButton {
                    text: "重置字段"
                    onClicked: {
                        var fresh = {}
                        for (var i = 0; i < jewelry.categories.length; i++) {
                            fresh[jewelry.categories[i]] = ""
                        }
                        tab.formData = fresh
                        tab.reassemble()
                    }
                }
                Label {
                    text: tab.statusText
                    font.family: App.Theme.fontFamily
                    font.pointSize: App.Theme.fontSm
                    color: tab.statusColor
                }
                Item { Layout.fillWidth: true }
            }

            // 预览大图
            Card {
                Layout.fillWidth: true
                Layout.fillHeight: true
                Layout.minimumHeight: 240

                ColumnLayout {
                    anchors.fill: parent
                    anchors.margins: App.Theme.space4
                    spacing: App.Theme.space3

                    CaptionLabel {
                        text: "预览"
                        font.pointSize: App.Theme.fontLg
                    }

                    Rectangle {
                        Layout.fillWidth: true
                        Layout.fillHeight: true
                        color: App.Theme.bgSubtle
                        radius: App.Theme.radiusMd

                        Image {
                            anchors.fill: parent
                            anchors.margins: App.Theme.space3
                            source: tab.lastResultPath ? "file:///" + tab.lastResultPath : ""
                            fillMode: Image.PreserveAspectFit
                            smooth: true
                            asynchronous: true
                            cache: false
                            visible: tab.lastResultPath !== ""

                            MouseArea {
                                anchors.fill: parent
                                cursorShape: Qt.PointingHandCursor
                                onDoubleClicked: {
                                    if (tab.lastResultPath) {
                                        Qt.openUrlExternally("file:///" + tab.lastResultPath)
                                    }
                                }
                            }
                        }

                        Text {
                            anchors.centerIn: parent
                            visible: tab.lastResultPath === ""
                            text: "选择字段后点 \"生成图片\""
                            color: App.Theme.textTertiary
                            font.family: App.Theme.fontFamily
                            font.pointSize: App.Theme.fontSm
                        }
                    }
                }
            }
        }
    }
}