| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- import QtQuick 2.12
- import QtQuick.Layouts 1.12
- import QtQuick.Controls 2.12
- Rectangle {
- id: control
- // === 接口属性 ===
- property string controlId
- property int modelIndex
- property var model: [] // [{ text: string }]
- property var selectedIndices: [] // 单选时数组最多一个元素
- signal selectionChanged(var selectedIndices, var selectedData)
- signal signalSelectionChanged(int index, string id, var selectedIndices, var selectedData)
- property bool required: false
- property string requiredMsg
- property bool showRequiredMsg: false
- // === 状态属性 ===
- property bool enabled: true
- // === 样式属性 ===
- property bool backgroundVisible: true
- property real radius: 12
- property int fontSize: 15
- property color buttonColor: "transparent"
- property color hoverColor: Qt.darker(buttonColor, 1.2)
- property color textColor: "white"
- property color checkmarkColor: "#5a8fc4"
- property color borderColor: "#355a80"
- property color selectedBgColor: "#1a3d2e" // 选中项背景(偏绿与绿色勾一致)
- property color selectedBorderColor: "#4CAF50"
- property color checkmarkGreen: "#4CAF50" // 选中时的绿色勾
- property real pressedScale: 0.98
- property bool shadowEnabled: true
- property color shadowColor: theme.shadowColor
- // 布局尺寸(按钮形式:大点击区,左侧留勾图标位)
- property int verticalPadding: 12
- property int iconSize: 24
- property int spacingBetweenIconAndText: 10
- property int verticalSpacingBetweenButtons: 10
- property int buttonHeight: 48
- // === 隐藏文本用于测量最大宽度 ===
- Text {
- id: measureText
- visible: false
- font.pixelSize: control.fontSize
- }
- property real maxTextWidth: 0
- function updateMaxTextWidth() {
- var maxWidth = 0
- for (var i = 0; i < model.length; i++) {
- measureText.text = model[i].text
- if (measureText.width > maxWidth)
- maxWidth = measureText.width
- }
- maxTextWidth = maxWidth
- }
- Component.onCompleted: updateMaxTextWidth()
- onModelChanged: updateMaxTextWidth()
- // === 尺寸计算(大点击区) ===
- implicitWidth: model.length > 0 ? model.length * (buttonHeight + verticalSpacingBetweenButtons) - verticalSpacingBetweenButtons + 32 : 0
- implicitHeight: buttonHeight + 24
- width: implicitWidth
- height: implicitHeight
- color: "transparent"
- // === 背景(仅填充,不画外框;选项各自保留边框) ===
- Rectangle {
- id: background
- anchors.fill: parent
- clip: true
- radius: control.radius
- color: control.buttonColor
- visible: control.backgroundVisible
- border.width: 0
- layer.enabled: control.shadowEnabled && control.backgroundVisible
- // layer.effect: MultiEffect {
- // shadowEnabled: control.shadowEnabled
- // shadowColor: control.shadowColor
- // shadowBlur: theme.shadowBlur
- // shadowHorizontalOffset: theme.shadowXOffset
- // shadowVerticalOffset: theme.shadowYOffset
- // }
- }
- // === 按钮行 ===
- RowLayout {
- id: buttonsRow
- // anchors.top: parent.top
- // anchors.topMargin: 10
- // anchors.horizontalCenter: parent.horizontalCenter
- anchors.verticalCenter: parent.verticalCenter
- spacing: verticalSpacingBetweenButtons
- width: implicitWidth
- }
- // === 按钮 Repeater ===
- Repeater {
- model: control.model
- parent: buttonsRow
- delegate: Rectangle {
- id: btn
- implicitWidth: verticalPadding * 2 + iconSize + spacingBetweenIconAndText + label.implicitWidth + 24
- height: buttonHeight
- radius: 8
- border.width: checked ? 2.5 : 1.5
- border.color: checked ? (control.selectedBorderColor || theme.focusColor) : (hovered ? control.checkmarkColor : control.borderColor)
- // === 状态属性 ===
- property bool hovered: false
- property bool checked: control.selectedIndices.indexOf(index) !== -1
- // 工业感:选中整行有背景+边框,未选中时 hover 有轻微背景
- color: control.backgroundVisible
- ? (checked ? control.selectedBgColor : (hovered ? control.hoverColor : control.buttonColor))
- : "transparent"
- opacity: mouseArea.pressed ? 0.9 : 1.0
- Behavior on color { ColorAnimation { duration: 150 } }
- Behavior on border.color { ColorAnimation { duration: 150 } }
- Behavior on border.width { NumberAnimation { duration: 120 } }
- Behavior on opacity { NumberAnimation { duration: 100 } }
- // === 缩放动画 ===
- transform: Scale {
- id: scale
- origin.x: btn.width / 2
- origin.y: btn.height / 2
- }
- ParallelAnimation {
- id: restoreAnimation
- SpringAnimation { target: scale; property: "xScale"; to: 1.0; spring: 2.5; damping: 0.25 }
- SpringAnimation { target: scale; property: "yScale"; to: 1.0; spring: 2.5; damping: 0.25 }
- }
- // === 按钮内容布局:选中=变色+绿色勾,无圆形单选图标 ===
- RowLayout {
- anchors.fill: parent
- anchors.leftMargin: verticalPadding
- anchors.rightMargin: verticalPadding
- spacing: spacingBetweenIconAndText
- Layout.alignment: Qt.AlignVCenter
- // === 方框 + 勾在方框内(选中时显示绿色勾) ===
- Rectangle {
- id: checkBox
- width: iconSize
- height: iconSize
- radius: 4
- border.width: checked ? 2 : 1
- border.color: checked ? control.checkmarkGreen : control.checkmarkColor
- color: checked ? "#404CAF50" : "transparent"
- Layout.alignment: Qt.AlignVCenter
- Behavior on border.color { ColorAnimation { duration: 120 } }
- Behavior on color { ColorAnimation { duration: 120 } }
- Text {
- anchors.centerIn: parent
- text: "\uf00c"
- color: control.checkmarkGreen
- font.pixelSize: iconSize - 6
- font.family: iconFont.name
- visible: checked
- opacity: checked ? 1 : 0
- Behavior on opacity { NumberAnimation { duration: 120 } }
- }
- }
- // === 标签文本 ===
- Text {
- id: label
- text: modelData.text
- color: control.textColor
- font.pixelSize: control.fontSize
- font.bold: checked
- elide: Text.ElideRight
- Layout.preferredWidth: label.implicitWidth
- Layout.alignment: Qt.AlignVCenter
- }
- }
- // === 交互逻辑 ===
- MouseArea {
- id: mouseArea
- anchors.fill: parent
- hoverEnabled: true
- cursorShape: control.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
- enabled: control.enabled
- onEntered: btn.hovered = true
- onExited: btn.hovered = false
- onPressed: {
- scale.xScale = control.pressedScale
- scale.yScale = control.pressedScale
- btn.opacity = 0.85
- }
- onReleased: {
- restoreAnimation.restart()
- btn.opacity = 1.0
- // 单选逻辑
- var newSelection = []
- if (control.selectedIndices.length === 0 || control.selectedIndices[0] !== index) {
- newSelection.push(index)
- }
- control.selectedIndices = newSelection
- // 获取选中数据
- var selectedDataItems = []
- var selectedDataValue = []
- for (var i = 0; i < control.selectedIndices.length; i++) {
- selectedDataValue.push(control.model[control.selectedIndices[i]].text)
- selectedDataItems.push(control.model[control.selectedIndices[i]])
- }
- control.selectionChanged(control.selectedIndices, selectedDataItems)
- control.signalSelectionChanged(control.modelIndex, control.controlId, control.selectedIndices, selectedDataValue);
- }
- onCanceled: {
- restoreAnimation.restart()
- btn.opacity = 1.0
- }
- }
- }
- }
-
- // 禁用时降低透明度
- opacity: control.enabled ? 1.0 : 0.6
- Text {
- id: textRequiredMsg
- anchors.top: buttonsRow.bottom
- anchors.topMargin: 15
- anchors.left: buttonsRow.left
- visible: control.required && control.showRequiredMsg
- text: qsTr(requiredMsg)
- color: "red"
- font.pixelSize: control.fontSize
- // 普通文字不使用图标字体
- font.bold: true
- verticalAlignment: Text.AlignVCenter
- }
- function slotShowRequiredMsg(submit) {
- if (submit && control.selectedIndices.length === 0) {
- control.showRequiredMsg = true;
- }
- }
- }
|