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

392 lines
12 KiB
QML
Raw Normal View History

2026-03-02 14:29:58 +08:00
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
}
2026-04-21 13:28:01 +08:00
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() }
2026-04-22 14:31:19 +08:00
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/>")
}
2026-03-02 14:29:58 +08:00
}
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
2026-04-21 13:28:01 +08:00
model: fileListModel
2026-03-02 14:29:58 +08:00
delegate: ItemDelegate {
text: model.fileName
icon.source: "resource/file.svg"
background: Rectangle {
color: fileSystemView.currentIndex == index ? "#D9D9D9" : "white"
}
onClicked: {
currentLogfilePath = model.filePath
2026-04-21 13:28:01 +08:00
currentLogResult = common.readLogFile(currentLogfilePath)
2026-03-02 14:29:58 +08:00
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))
2026-04-22 14:31:19 +08:00
applyRegexFilter()
2026-03-02 14:29:58 +08:00
} catch(e) {
console.error("Error in updateLogDisplay:", e)
}
}
Rectangle {
2026-04-22 14:31:19 +08:00
id: logViewContainer
2026-03-02 14:29:58 +08:00
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
2026-04-22 14:31:19 +08:00
// 正则搜索栏
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()
}
2026-03-02 14:29:58 +08:00
}
2026-04-22 14:31:19 +08:00
}
}
}
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
2026-03-02 14:29:58 +08:00
}
}
}
}
}
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 []
}
}
}