392 lines
12 KiB
QML
392 lines
12 KiB
QML
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,"&").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/>")
|
||
}
|
||
}
|
||
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 []
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|