LoginScreen.qml 7.14 KB
import QtQuick
import QtQuick.Controls.Basic
import QtQuick.Layouts
import "components"
import "." as App

Rectangle {
    id: root
    color: App.Theme.bgCanvas
    focus: true

    // 提交期间禁止重复点击 / 回车
    property bool submitting: false
    // 用户是否在密码框输入过 — 决定走 login(明文) 还是 loginWithSavedPassword(已存 hash)
    property bool passwordChanged: false

    Component.onCompleted: {
        usernameField.text = auth.lastUser
        // 上次记住的用户 → 焦点到密码框;否则到用户名
        if (usernameField.text.length > 0 && auth.hasSavedPassword) {
            passwordField.focus = true
        } else {
            usernameField.focus = true
        }
    }

    function doLogin() {
        if (submitting) return
        var username = usernameField.text.trim()
        if (username.length === 0) {
            errorLabel.text = "请输入用户名"
            return
        }

        // 决定走哪条路径:
        //   passwordChanged && text != "" → 明文 login
        //   !passwordChanged && hasSavedPassword → 用已存 hash 登录
        //   其他(密码框空 + 没有已存 hash)→ 报错
        var useSaved = !passwordChanged && auth.hasSavedPassword
        if (!useSaved && passwordField.text.length === 0) {
            errorLabel.text = "请输入密码"
            return
        }

        errorLabel.text = ""
        submitting = true
        var ok
        if (useSaved) {
            ok = auth.loginWithSavedPassword(username)
        } else {
            ok = auth.login(username, passwordField.text)
        }
        submitting = false

        // 登录成功 → 按勾选保存凭据。失败时 auth 已发 loginFailed 由 Connections 接
        if (ok) {
            // 没改过密码时传空字符串,桥层会保留旧 hash 不动
            auth.saveCredentials(
                passwordChanged ? passwordField.text : "",
                rememberUser.checked,
                rememberPassword.checked
            )
        }
    }

    Keys.onReturnPressed: doLogin()
    Keys.onEnterPressed:  doLogin()

    Connections {
        target: auth
        function onLoginFailed(message) {
            errorLabel.text = message
        }
    }

    ColumnLayout {
        anchors.centerIn: parent
        width: 360
        spacing: 0

        Label {
            text: "登录"
            font.family: App.Theme.fontFamily
            font.pointSize: App.Theme.fontXxl
            font.weight: Font.Bold
            color: App.Theme.textPrimary
            Layout.alignment: Qt.AlignHCenter
            Layout.bottomMargin: 4
        }

        Label {
            text: "珠宝壹佰图像生成器"
            font.family: App.Theme.fontFamily
            font.pointSize: App.Theme.fontSm
            color: App.Theme.textSecondary
            Layout.alignment: Qt.AlignHCenter
            Layout.bottomMargin: App.Theme.space5
        }

        CaptionLabel {
            text: "用户名"
            Layout.bottomMargin: App.Theme.space2
        }
        ThemedTextField {
            id: usernameField
            text: ""
            placeholderText: "请输入用户名"
            enabled: !root.submitting
            Layout.fillWidth: true
            Layout.bottomMargin: App.Theme.space4
        }

        CaptionLabel {
            text: "密码"
            Layout.bottomMargin: App.Theme.space2
        }
        ThemedTextField {
            id: passwordField
            echoMode: TextInput.Password
            // 已存密码时显示 8 个圆点占位(与旧 LoginDialog 一致)
            placeholderText: auth.hasSavedPassword ? "••••••••" : "请输入密码"
            enabled: !root.submitting
            Layout.fillWidth: true
            Layout.bottomMargin: App.Theme.space2
            onTextEdited: root.passwordChanged = true
        }

        RowLayout {
            spacing: App.Theme.space4
            Layout.fillWidth: true
            Layout.bottomMargin: App.Theme.space5

            CheckBox {
                id: rememberUser
                text: "记住用户名"
                checked: auth.lastUser.length > 0
                enabled: !root.submitting
                font.family: App.Theme.fontFamily
                font.pointSize: App.Theme.fontSm
                contentItem: Text {
                    text: parent.text
                    font: parent.font
                    color: App.Theme.textPrimary
                    leftPadding: parent.indicator.width + 6
                    verticalAlignment: Text.AlignVCenter
                }
                indicator: Rectangle {
                    implicitWidth: 18
                    implicitHeight: 18
                    radius: 4
                    border.width: 1
                    border.color: parent.checked ? App.Theme.accent : App.Theme.borderStrong
                    color: parent.checked ? App.Theme.accent : App.Theme.bgSurface

                    Text {
                        anchors.centerIn: parent
                        text: "✓"
                        color: "white"
                        font.pixelSize: 12
                        font.weight: Font.Bold
                        visible: parent.parent.checked
                    }
                }
            }

            CheckBox {
                id: rememberPassword
                text: "记住密码"
                checked: auth.hasSavedPassword
                enabled: !root.submitting
                font.family: App.Theme.fontFamily
                font.pointSize: App.Theme.fontSm
                contentItem: Text {
                    text: parent.text
                    font: parent.font
                    color: App.Theme.textPrimary
                    leftPadding: parent.indicator.width + 6
                    verticalAlignment: Text.AlignVCenter
                }
                indicator: Rectangle {
                    implicitWidth: 18
                    implicitHeight: 18
                    radius: 4
                    border.width: 1
                    border.color: parent.checked ? App.Theme.accent : App.Theme.borderStrong
                    color: parent.checked ? App.Theme.accent : App.Theme.bgSurface

                    Text {
                        anchors.centerIn: parent
                        text: "✓"
                        color: "white"
                        font.pixelSize: 12
                        font.weight: Font.Bold
                        visible: parent.parent.checked
                    }
                }
            }

            Item { Layout.fillWidth: true }
        }

        PrimaryButton {
            text: root.submitting ? "登录中…" : "登录"
            enabled: !root.submitting
            Layout.fillWidth: true
            onClicked: doLogin()
        }

        Label {
            id: errorLabel
            text: ""
            font.family: App.Theme.fontFamily
            font.pointSize: App.Theme.fontXs
            color: App.Theme.danger
            wrapMode: Text.Wrap
            horizontalAlignment: Text.AlignHCenter
            Layout.fillWidth: true
            Layout.topMargin: App.Theme.space3
        }
    }
}