2025-05-29 14:04:05 +08:00
|
|
|
|
import QtQuick 2.15
|
|
|
|
|
|
import QtQuick.Window 2.15
|
|
|
|
|
|
import QtQuick.Controls 2.15
|
2025-09-15 09:46:04 +08:00
|
|
|
|
import HuskarUI.Basic 1.0
|
2025-05-29 14:04:05 +08:00
|
|
|
|
import "../MyGlobals" 1.0
|
|
|
|
|
|
import SSHManager 1.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Item {
|
|
|
|
|
|
visible: true
|
|
|
|
|
|
|
|
|
|
|
|
// SSH控制台输出显示区域
|
|
|
|
|
|
Item {
|
|
|
|
|
|
id:consolearea
|
|
|
|
|
|
anchors {
|
|
|
|
|
|
fill: parent
|
|
|
|
|
|
margins: 10
|
|
|
|
|
|
bottomMargin: 46
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ScrollView {
|
|
|
|
|
|
id: scrollView
|
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
|
clip: true
|
|
|
|
|
|
|
|
|
|
|
|
TextArea {
|
|
|
|
|
|
id: consoleOutput
|
|
|
|
|
|
selectByMouse: true
|
|
|
|
|
|
textFormat: Text.RichText
|
|
|
|
|
|
readOnly: true
|
|
|
|
|
|
wrapMode: Text.WrapAnywhere
|
|
|
|
|
|
font {
|
|
|
|
|
|
pixelSize: 14
|
2025-09-15 09:46:04 +08:00
|
|
|
|
family: HusTheme.Primary.fontPrimaryFamily
|
2025-05-29 14:04:05 +08:00
|
|
|
|
}
|
2025-09-15 09:46:04 +08:00
|
|
|
|
color: HusTheme.Primary.colorTextBase
|
2025-05-29 14:04:05 +08:00
|
|
|
|
background: Rectangle {
|
2025-09-15 09:46:04 +08:00
|
|
|
|
color: HusTheme.isDark ? "#2d2d2d" : "#f5f5f5"
|
2025-05-29 14:04:05 +08:00
|
|
|
|
radius: 4
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 自动滚动到底部
|
|
|
|
|
|
onTextChanged: {
|
|
|
|
|
|
if (length > 0) {
|
|
|
|
|
|
cursorPosition = length
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-15 09:46:04 +08:00
|
|
|
|
HusInput {
|
2025-05-29 14:04:05 +08:00
|
|
|
|
id: inputField
|
|
|
|
|
|
width: consolearea.width
|
2025-09-15 09:46:04 +08:00
|
|
|
|
iconPosition: HusInput.Position_Left
|
|
|
|
|
|
iconSource: HusIcon.ForwardOutlined
|
2025-05-29 14:04:05 +08:00
|
|
|
|
placeholderText: qsTr("这里可以输入服务器命令")
|
|
|
|
|
|
anchors {
|
|
|
|
|
|
top: consolearea.bottom
|
|
|
|
|
|
topMargin: 6
|
|
|
|
|
|
horizontalCenter: parent.horizontalCenter
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 添加回车键事件
|
|
|
|
|
|
onAccepted: {
|
|
|
|
|
|
SSHManager.sendInput(text)
|
|
|
|
|
|
text = ""
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 监听 Ctrl+C
|
|
|
|
|
|
Keys.onPressed: (event) => {
|
|
|
|
|
|
if ((event.modifiers & Qt.ControlModifier) && event.key === Qt.Key_C) {
|
|
|
|
|
|
SSHManager.sendInput("\x03")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ANSI转HTML的转换逻辑
|
|
|
|
|
|
function ansiToHtml(text) {
|
|
|
|
|
|
// 第一步:处理颜色代码
|
|
|
|
|
|
let colored = text.replace(/\x1B\[([0-9;]*)m/g, function(match, codes) {
|
|
|
|
|
|
let styles = [];
|
|
|
|
|
|
for (let code of codes.split(';')) {
|
|
|
|
|
|
switch (parseInt(code)) {
|
|
|
|
|
|
case 0:
|
|
|
|
|
|
return "</span>";
|
|
|
|
|
|
case 1:
|
|
|
|
|
|
styles.push("font-weight:bold");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 3:
|
|
|
|
|
|
styles.push("font-style:italic");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 4:
|
|
|
|
|
|
styles.push("text-decoration:underline");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 30:
|
|
|
|
|
|
styles.push("color:black");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 31:
|
|
|
|
|
|
styles.push("color:red");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 32:
|
|
|
|
|
|
styles.push("color:green");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 33:
|
|
|
|
|
|
styles.push("color:yellow");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 34:
|
|
|
|
|
|
styles.push("color:blue");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 35:
|
|
|
|
|
|
styles.push("color:magenta");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 36:
|
|
|
|
|
|
styles.push("color:cyan");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 37:
|
|
|
|
|
|
styles.push("color:white");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 40:
|
|
|
|
|
|
styles.push("background-color:black");
|
|
|
|
|
|
break;
|
|
|
|
|
|
// 其他颜色代码...
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return styles.length ? `<span style="${styles.join(';')}">` : "";
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 第二步:清除所有 ANSI 转义序列(包括 XTerm 标题、控制序列等)
|
|
|
|
|
|
let cleaned = colored
|
|
|
|
|
|
//删除 XTerm 窗口标题序列(格式:\x1B]0;标题\x07 或 \x1B]0;标题\x1B\)
|
|
|
|
|
|
.replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, '')
|
|
|
|
|
|
//删除其他 ANSI 控制序列(包括以 ? 开头的模式设置,如 ?1034h)
|
|
|
|
|
|
.replace(/\x1B\[[1034h;?]*[A-Za-z]/g, '');
|
|
|
|
|
|
|
|
|
|
|
|
return cleaned;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function connectionSuccess(){
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function connectionFailed(error){
|
|
|
|
|
|
consoleOutput.text += "连接服务器失败" + error + "\n"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function disconnected(){
|
|
|
|
|
|
consoleOutput.text += "服务器断开连接\n"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function updateStatus(msg) {
|
|
|
|
|
|
consoleOutput.text += (msg + "\n")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function appendOutput(msg) {
|
|
|
|
|
|
// 转义 HTML 特殊字符
|
|
|
|
|
|
const safeMsg = msg.replace(/</g, "<").replace(/>/g, ">").replace(/\n/g, "<br>"); // 新增换行符转换
|
|
|
|
|
|
const zemsg = ansiToHtml("> " + safeMsg)
|
|
|
|
|
|
consoleOutput.text += zemsg;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化后自动连接示例(可选)
|
|
|
|
|
|
Component.onCompleted: {
|
|
|
|
|
|
SSHManager.connectionSuccess.connect(connectionSuccess)
|
|
|
|
|
|
SSHManager.connectionFailed.connect(connectionFailed)
|
|
|
|
|
|
SSHManager.statusMessage.connect(updateStatus)
|
|
|
|
|
|
SSHManager.disconnected.connect(disconnected)
|
|
|
|
|
|
|
|
|
|
|
|
SSHManager.shellOutput.connect(appendOutput)
|
|
|
|
|
|
consoleOutput.text = "控制台已就绪...\n"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 窗口关闭时自动触发清理
|
|
|
|
|
|
Component.onDestruction: {
|
|
|
|
|
|
// 断开所有信号连接
|
|
|
|
|
|
SSHManager.connectionSuccess.disconnect(connectionSuccess);
|
|
|
|
|
|
SSHManager.connectionFailed.disconnect(connectionFailed);
|
|
|
|
|
|
SSHManager.statusMessage.disconnect(updateStatus);
|
|
|
|
|
|
SSHManager.disconnected.disconnect(disconnected);
|
|
|
|
|
|
SSHManager.shellOutput.disconnect(appendOutput);
|
|
|
|
|
|
console.log("窗口关闭,已断开所有信号连接并清理资源。");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|