TG-PlatformPlus/qml/debug/logviewer/main.qml

392 lines
12 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Dialogs
// import QtQuick.Controls.
import "./app.js" as App
import "./common"
Rectangle {
id: root
property var appId: App.appId
property var plusFile: App.plusFile
property var g_user: ""
property var g_sn: "8441"
property var appPath: "/"+plusFile+"/"+appId+"/"
property var firstPath: ""
property var logDirectory: ""
property var currentLogfilePath: ""
property var currentLogResult: ""
Component.onCompleted: {
firstPath = common.firstPath()
console.info("firstPath: " + firstPath)
console.info("appPath: " + appPath)
// 初始化应用程序脚本,确保脚本已准备就绪
common.initAppScript(appId, "log_processor", "log_processor.py", plusFile)
// common.registAppQml(appId, "file_system_model", "FileSystemModel", "file_system_model.py", plusFile)
var infos = projectManager.getInfo("all")
processInfoDict(infos)
g_user = common.getCurrentUser()
currentLogfilePath = ""
currentLogResult = ""
}
function processInfoDict(dict) {
var curProId = projectManager.getCurrentProId()
console.log("curProId: " + curProId)
var projects = Object.values(dict)
projects.forEach(pro => proFilter.model.push(pro))
var currentIndex = projects.findIndex(pro => pro.id === curProId)
if (currentIndex !== -1) {
logDirectory = "file:///" + projects[currentIndex].path + "/log"
}
proFilter.currentIndex = currentIndex
}
gradient: Gradient {
GradientStop { position: 0.0; color: "#ecf3fb" }
GradientStop { position: 1.0; color: "#b7d6fb" }
orientation: Gradient.Horizontal
}
ListModel { id: fileListModel }
function refreshFileList() {
fileListModel.clear()
if (logDirectory) {
var files = common.getTxtFiles(logDirectory)
for (var i = 0; i < files.length; i++) {
var fp = files[i].toString()
fileListModel.append({
"fileName": fp.substring(fp.lastIndexOf('/') + 1).replace(/\\/g, '/').split('/').pop(),
"filePath": fp
})
}
}
}
onLogDirectoryChanged: { refreshFileList() }
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('<span style="color:'+item.color+'">'+plain.replace(/&/g,"&amp;").replace(/</g,"&lt;")+'</span>')
}
if (!regexPattern) {
logTextArea.text = htmlLines.join("<br/>")
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('<span style="color:'+color+'">'+ml.replace(/&/g,"&amp;").replace(/</g,"&lt;")+'</span>')
})
}
logTextArea.text = resultHtml.join("<br/>")
} catch(e) {
logTextArea.text = htmlLines.join("<br/>")
}
}
Rectangle {
id: bar
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 12
anchors.leftMargin: 23
anchors.rightMargin: 23
height: 45
radius: 9
Row{
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 12
spacing: 80
Text {
text: "日志文件"
font.pixelSize: 20
color: "#000000"
font.bold: true
}
}
Row {
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 15
spacing: 13
Text {
text: "选择工程"
font.pixelSize: 16
color: "#000000"
}
ComboBox {
id: proFilter
width: 150
model: []
textRole: "name"
onActivated: {
logDirectory = "file:///" + model[currentIndex].path + "/log"
logModel.clear()
}
}
ComboBox {
id: levelFilter
width: 150
model: ["DEBUG", "INFO", "WARNING", "ERROR"]
onActivated: {
updateLogDisplay()
}
}
ComboBox {
id: tagFilter
width: 150
model: ["ALL"]
onActivated: {
updateLogDisplay()
}
}
}
}
Rectangle {
id: fileSystemViewContainer
anchors.top: bar.bottom
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.leftMargin: 23
anchors.bottomMargin: 12
anchors.topMargin: 23
radius: 9
width: 350
clip: true
ListView {
id: fileSystemView
anchors.fill: parent
boundsBehavior: Flickable.StopAtBounds
ScrollBar.vertical: ScrollBar{}
anchors.margins: 5
model: fileListModel
delegate: ItemDelegate {
text: model.fileName
icon.source: "resource/file.svg"
background: Rectangle {
color: fileSystemView.currentIndex == index ? "#D9D9D9" : "white"
}
onClicked: {
currentLogfilePath = model.filePath
currentLogResult = common.readLogFile(currentLogfilePath)
updateLogDisplay()
fileSystemView.currentIndex = index
}
}
}
}
function appendTagLogLine(line) {
try {
if (tagFilter.currentIndex > 0) {
if (line.tag.includes(tagFilter.currentText)) {
appendLogLine(line)
}
}
else {
appendLogLine(line)
}
} catch(e) {
console.error("Error in appendTagLogLine:", e)
}
}
function appendLogLine(line) {
try {
logModel.append({
"time": line.time,
"tag": line.tag,
"message": line.msg,
"level": line.level,
"color": line.color
})
} catch(e) {
console.error("Error in appendLogLine:", e)
}
}
function updateLogDisplay() {
try {
logModel.clear()
var allTags = new Set()
currentLogResult.split("\n").forEach(function(line) {
try {
var part = line.split("---END")[0]
if (part.length > 1) {
var item =JSON.parse(part)
item.tag.split(",").forEach(tag => allTags.add(tag))
switch (levelFilter.currentIndex) {
case 0:
appendTagLogLine(item)
break;
case 1:
if(item.level != "DEBUG")
{
appendTagLogLine(item)
}
break;
case 2:
if(item.level === "WARNING" || item.level === "ERROR")
{
appendTagLogLine(item)
}
break;
case 3:
if(item.level === "ERROR")
{
appendTagLogLine(item)
}
break;
}
}
} catch(e) {
console.error("Error processing log line:", e)
}
})
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
anchors.bottom: parent.bottom
anchors.leftMargin: 23
anchors.bottomMargin: 12
anchors.topMargin: 23
anchors.rightMargin: 23
radius: 9
clip: true
// 正则搜索栏
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()
}
}
}
}
}
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
}
}
}
}
}
function updateFileList() {
try {
if (logDirectory) {
var files = common.getTxtFiles(logDirectory)
deviceFilter.model = ["全部日志"].concat(files.map(function(file) {
return file.substring(file.lastIndexOf('/') + 1);
}))
return files
}
return []
} catch(e) {
console.error("Error in updateFileList:", e)
return []
}
}
}