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,"&").replace(/</g,"<")+'</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,"&").replace(/</g,"<")+'</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 []
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|