diff --git a/qml/debug/logviewer/main.qml b/qml/debug/logviewer/main.qml index 43de76f..f8d26d1 100644 --- a/qml/debug/logviewer/main.qml +++ b/qml/debug/logviewer/main.qml @@ -72,8 +72,45 @@ Rectangle { onLogDirectoryChanged: { refreshFileList() } - ListModel{ - id: logModel + ListModel { id: logModel } + + property string regexPattern: "" + + function applyRegexFilter() { + // 先拼出所有行(plain + html 两份) + var plainLines = [] + var htmlLines = [] + for (var i = 0; i < logModel.count; i++) { + var item = logModel.get(i) + var plain = "["+item.time+"] ["+item.level+"] ["+item.tag+"] "+item.message + plainLines.push(plain) + htmlLines.push(''+plain.replace(/&/g,"&").replace(/') + } + + if (!regexPattern) { + logTextArea.text = htmlLines.join("
") + return + } + + try { + var re = new RegExp(regexPattern, "gim") + var fullText = plainLines.join("\n") + var resultHtml = [] + var match + while ((match = re.exec(fullText)) !== null) { + // 找到匹配片段,按行拆分后重新着色 + var matchedLines = match[0].split("\n") + matchedLines.forEach(function(ml) { + // 找对应原始行的颜色 + var idx = plainLines.indexOf(ml) + var color = idx >= 0 ? logModel.get(idx).color : "#000" + resultHtml.push(''+ml.replace(/&/g,"&").replace(/') + }) + } + logTextArea.text = resultHtml.join("
") + } catch(e) { + logTextArea.text = htmlLines.join("
") + } } Rectangle { id: bar @@ -241,11 +278,13 @@ Rectangle { } }) tagFilter.model = ["ALL"].concat(Array.from(allTags)) + applyRegexFilter() } catch(e) { console.error("Error in updateLogDisplay:", e) } } Rectangle { + id: logViewContainer anchors.top: bar.bottom anchors.left: fileSystemViewContainer.right anchors.right: parent.right @@ -256,36 +295,74 @@ Rectangle { anchors.rightMargin: 23 radius: 9 clip: true - // Log display - ListView { - anchors.fill: parent - anchors.margins: 10 - boundsBehavior: Flickable.StopAtBounds - model: logModel - ScrollBar.vertical: ScrollBar{} - delegate: Item { - - height: 20 - Row { - spacing: 5 - Text { - text: "["+model.time+"]" - color: "gray" - - } - - Text { - text: "["+model.level+"]" - color: model.color - } - Text { - text: "["+model.tag+"]" - color: model.color + // 正则搜索栏 + Rectangle { + id: searchBar + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 8 + height: 32 + radius: 6 + color: "#f0f0f0" + border.color: regexError ? "red" : "#cccccc" + border.width: 1 + + property bool regexError: false + + Row { + anchors.fill: parent + anchors.leftMargin: 8 + anchors.rightMargin: 8 + spacing: 6 + Text { + anchors.verticalCenter: parent.verticalCenter + text: "正则:" + font.pixelSize: 13 + color: "#555" + } + TextInput { + id: regexInput + anchors.verticalCenter: parent.verticalCenter + width: parent.width - 50 + font.pixelSize: 13 + onTextChanged: { + try { + if (text) new RegExp(text) + searchBar.regexError = false + } catch(e) { + searchBar.regexError = true + } + if (!searchBar.regexError) { + regexPattern = text + applyRegexFilter() + } } - Text { - text: model.message - color: model.color + } + } + } + + ScrollView { + anchors.top: searchBar.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: 10 + anchors.topMargin: 6 + TextArea { + id: logTextArea + readOnly: true + selectByMouse: true + textFormat: TextEdit.RichText + wrapMode: TextEdit.NoWrap + font.pixelSize: 13 + font.family: "Courier New" + background: null + Keys.onPressed: function(event) { + if (event.matches(StandardKey.Copy)) { + logTextArea.copy() + event.accepted = true } } }