import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15 import QtMultimedia 5.15 import HuskarUI.Basic 1.0 import QmlTool 1.0 import FileTransfer 1.0 import "../MyGlobals" 1.0 import "../Component" 1.0 HusWindow { id:plugininfo_goods width: 1110 height: 630 visible: true title: qsTr("插件详情页") captionBar.topButtonVisible: true captionBar.winIconDelegate: Image { id: name width: 20 height: 20 source: "qrc:/image/logo.png" } //原始数据 property var p_basedata: null //插件名 property string p_name : "未定名插件" //作者名 property string p_author : "未定名作者" //版本号 property string p_version: "0.0.1" //描述 property string p_description: "暂无描述" //售价 property int p_price: 0 //插件文件数组 property var p_filelist : [] //是否是凌众插件 property bool p_isrindro : false //图像 property string p_image:"qrc:/image/logo2.png" Component.onCompleted: { } Shortcut { sequences: ["Esc"] onActivated: close() } function init(index){ //全局数据插件列表存在 if(!GlobalVars.serverPluginexMap)return; var Data = GlobalVars.serverPluginexMap[index] p_basedata = Data; p_name = Data.ProjectName p_author = Data.ProjectAuthor p_version = Data.ProjectVersion p_description = Data.ProjectDescribe //如果有插件文件数组 if(Data.ProjectFiles)p_filelist = Data.ProjectFiles //如果有插件的详细描述 if(Data.ProjectDetails)details.model = Data.ProjectDetails //如果有售价才设置售价 if(Data.ProjectPrice)p_price = Data.ProjectPrice else p_price = 0 } function isLocalIP(ip) { // 检查是否为空或无效IP if (!ip || typeof ip !== 'string') return false; // 常见本地IP模式 const localIPPatterns = [ /^127\./, // 127.0.0.0-127.255.255.255 /^10\./, // 10.0.0.0-10.255.255.255 /^172\.(1[6-9]|2[0-9]|3[0-1])\./, // 172.16.0.0-172.31.255.255 /^192\.168\./ // 192.168.0.0-192.168.255.255 ]; // 检查是否匹配任何本地IP模式 return localIPPatterns.some(pattern => pattern.test(ip)); } function buyPlugin(){ var RealBuyIp = GlobalVars.accServerList[GlobalVars.selectServer].serverIp; if(isLocalIP(RealBuyIp)){ GlobalVars.msg_control.error("暂不支持单机服务器使用双端插件!"); close() return; } GlobalVars.buyExPlugin(p_name,RealBuyIp) close() } Rectangle{ id:title anchors.left: parent.left anchors.leftMargin: 10 anchors.right: parent.right anchors.rightMargin: 15 anchors.top: parent.top anchors.topMargin: 45 height:78 + headerdiv.height + description.height radius:8 border.color: HusTheme.isDark ? "#23272e" : "#f0f4f7" border.width: 2 color:"transparent" Image { id: logo source: "qrc:/image/logo.png" anchors.left: parent.left anchors.leftMargin: 10 anchors.top: parent.top anchors.topMargin: 15 width: 48 height: 48 } HusDivider { id:headerdiv anchors.top: logo.bottom anchors.left: parent.left anchors.leftMargin: 1 width: parent.width - 1 height: 30 } Text { id:description anchors.left: parent.left anchors.leftMargin: 10 anchors.right: parent.right anchors.rightMargin: 10 anchors.top: headerdiv.bottom text: p_description wrapMode: Text.WordWrap font { pixelSize: 14 family: HusTheme.Primary.fontPrimaryFamily } color: HusTheme.Primary.colorTextBase } Text { id:pluginname anchors.left: logo.right anchors.leftMargin: 10 anchors.top: parent.top anchors.topMargin: 14 text: p_name font { pixelSize: 20 family: HusTheme.Primary.fontPrimaryFamily } color: HusTheme.Primary.colorTextBase } Text { anchors.left: logo.right anchors.leftMargin: 10 anchors.top: pluginname.bottom anchors.topMargin: 2 text: "作者: " + p_author font { pixelSize: 14 family: HusTheme.Primary.fontPrimaryFamily } color: HusTheme.Primary.colorTextBase } HusTag { id:version_tag anchors.left: pluginname.right anchors.leftMargin: 15 anchors.top: pluginname.top anchors.topMargin: 2 text:"Ver:" + p_version presetColor:"green" } HusTag { id:price_tag anchors.top: parent.top anchors.topMargin: 15 anchors.right: parent.right anchors.rightMargin: 10 text:"所需贡献点:" + p_price presetColor:"green" } HusButton { id:buy_button anchors.right: parent.right anchors.rightMargin: 10 anchors.top: price_tag.bottom anchors.topMargin: 2 height: 26 text: "购买" onClicked: { //我这里临时先写免费服务端插件的安装 buyPlugin(); } } } Rectangle{ id:content anchors.left: parent.left anchors.leftMargin: 10 anchors.right: parent.right anchors.rightMargin: 15 anchors.top: title.bottom anchors.topMargin: 10 anchors.bottom:parent.bottom anchors.bottomMargin: 10 radius:8 border.color: HusTheme.isDark ? "#23272e" : "#f0f4f7" border.width: 2 color:"transparent" clip: true ScrollView { anchors.fill: parent anchors.margins: 10 Column { id: contentColumn anchors.fill: parent spacing: 10 Repeater { id:details delegate: Column { // 直接使用 Column 作为根元素 width: parent.width spacing: 0 Loader { width: parent.width sourceComponent: { switch(modelData.type) { case "str": return textComponent; case "img": return imageComponent; case "mov": return videoComponent; default: return null; } } // 动态绑定组件属性 property var itemData: modelData } } } } } } // 定义组件映射 Component { id: textComponent; Text { width: parent.width - 20 text: itemData.content wrapMode: Text.Wrap font { pixelSize: 14 family: HusTheme.Primary.fontPrimaryFamily } color: HusTheme.Primary.colorTextBase } } Component { id: imageComponent; Image { source: GlobalVars.server_url + "/rindro/getimg/" + p_name + "/" + itemData.content fillMode: Image.PreserveAspectFit asynchronous: true cache: true // 动态计算宽度 width: Math.min(implicitWidth, parent.width) // 让图像靠左边显示 anchors.left: parent.left // 确保父级有明确宽度传递 property real maxWidth: parent ? parent.width : 0 // 异步加载完成后更新尺寸 onStatusChanged: { if (status === Image.Ready) { width = Math.min(implicitWidth, maxWidth) } } } } // Component { // id: videoComponent // Item { // id: videoContainer // width: parent.width // height: 400 // // 视频播放器 // Video { // id: videoPlayer // anchors.fill: parent // anchors.bottomMargin: 15 // source: itemData.content // loops: MediaPlayer.Infinite // autoPlay: false // // 错误处理 // onErrorChanged: { // if (error !== MediaPlayer.NoError) { // GlobalVars.msg_control.error("播放错误:", errorString); // } // } // } // // 播放控制按钮(可选) // Row { // anchors.top: videoPlayer.bottom // anchors.topMargin: 4 // anchors.horizontalCenter: parent.horizontalCenter // spacing: 10 // DelButton { // text: videoPlayer.playbackState === MediaPlayer.PlayingState ? "暂停" : "播放" // onClicked: videoPlayer.playbackState === MediaPlayer.PlayingState ? videoPlayer.pause() : videoPlayer.play() // } // DelButton { // text: "静音" // onClicked: videoPlayer.muted = !videoPlayer.muted // } // } // } // } Component { id: videoComponent Item { id: videoContainer width: itemData.width height: itemData.height + 40 // 视频播放器 Video { id: videoPlayer anchors { left: parent.left right: parent.right top: parent.top } height: itemData.height anchors.margins: 2 // fillMode: MediaPlayer.PreserveAspectFit // 保持视频比例,不拉伸 // source: itemData.content source: itemData.content loops: MediaPlayer.Infinite autoPlay: false focus: false // 确保视频播放器不捕获焦点 // 错误处理 onErrorChanged: { if (error !== MediaPlayer.NoError) { GlobalVars.msg_control.error("播放错误:", errorString); } } } // 外边框 Rectangle { id: border anchors { left: videoPlayer.left right: videoPlayer.right top: videoPlayer.top bottom: videoPlayer.bottom } border.color: "#cccccc" border.width: 2 radius: 4 color: "transparent" z: 1 // 确保边框在视频上方 } // 视频加载状态指示器 Rectangle { id: loadingIndicator anchors.centerIn: videoPlayer width: 60 height: 60 radius: 30 color: "#00000048" z: 2 // 确保加载指示器在最上层 Text { id: loadingText anchors.centerIn: parent text: "加载中..." color: "white" font.pixelSize: 12 } visible: videoPlayer.status === MediaPlayer.LoadingStatus } // 错误提示文本 Text { id: errorText anchors.centerIn: videoPlayer text: "视频加载失败,请检查链接或网络" color: "red" font.pixelSize: 14 visible: videoPlayer.error !== MediaPlayer.NoError z: 2 // 确保错误文本在最上层 } // 进度条容器,移到视频下方 Rectangle { id: progressBarContainer anchors { left: parent.left right: parent.right bottom: videoPlayer.bottom bottomMargin: 2 } height: 10 color: "#00000048" radius: 2 z: 2 // 确保进度条在最上层 Rectangle { id: progressBar anchors { left: parent.left top: parent.top bottom: parent.bottom } width: parent.width * (videoPlayer.position / Math.max(1, videoPlayer.duration)) color: "#0078d7" radius: 2 } MouseArea { id: progressBarMouseArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton propagateComposedEvents: true // 允许事件传播 onPressed: { if (videoPlayer.duration > 0) videoPlayer.seek((mouse.x / width) * videoPlayer.duration) } onPositionChanged: { if (mouse.pressedButtons & Qt.LeftButton && videoPlayer.duration > 0) { videoPlayer.seek((mouse.x / width) * videoPlayer.duration) } } } } // 播放控制按钮,移到视频下方 Row { id: controlButtons anchors { top: videoPlayer.bottom topMargin: 5 horizontalCenter: parent.horizontalCenter } spacing: 10 visible: true z: 2 // 确保控制按钮在最上层 HusButton { id: playPauseButton text: videoPlayer.playbackState === MediaPlayer.PlayingState ? "暂停" : "播放" onClicked: { if (videoPlayer.playbackState === MediaPlayer.PlayingState) videoPlayer.pause() else videoPlayer.play() } } HusButton { id: muteButton text: videoPlayer.muted ? "取消静音" : "静音" onClicked: videoPlayer.muted = !videoPlayer.muted } HusButton { id: replayButton text: "重播" onClicked: { videoPlayer.seek(0) videoPlayer.play() } } HusButton { text: "外部浏览器打开" onClicked: { Qt.openUrlExternally(GlobalVars.server_url + "/api/videos/" + p_name + "/" + itemData.index) } } } // 鼠标悬停显示控制按钮 MouseArea { id: containerMouseArea anchors.fill: parent hoverEnabled: true propagateComposedEvents: true // 允许事件传播 z: 1 // 确保鼠标区域在适当层级 // onEntered: { // controlButtons.visible = true // progressBarContainer.visible = true // } // onExited: { // controlButtons.visible = false // progressBarContainer.visible = false // } } } } /* DelButton { text: "Open XLSX File" anchors.centerIn: parent onClicked: { var filePath = "./基础配置.xlsx"; // 请替换为实际的文件路径 QmlTool.openFile(filePath); } } */ }