DPS_Manage/Component/JsonEditor.qml

181 lines
5.7 KiB
QML
Raw Normal View History

2025-05-29 14:04:05 +08:00
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import JsonEditor 1.0
2025-09-15 09:46:04 +08:00
import HuskarUI.Basic 1.0
2025-05-29 14:04:05 +08:00
ScrollView {
width: 600
height: 400
property var projectConfigStr: null // 初始化为空对象
property var saveFunction: function(config){}
property var errorLine: null
property var errorPosition : null
// JSON 处理器
JsonProcessor {
id: jsonProcessor
}
// 将 UTF-8 字节偏移量转换为 UTF-16 代码单元索引
function utf8OffsetToUtf16Index(text, utf8Offset) {
var utf16Index = 0;
var utf8BytesCount = 0;
for (var i = 0; i < text.length; i++) {
var charCode = text.charCodeAt(i);
var charUtf8Length = 1;
if (charCode <= 0x7F) {
charUtf8Length = 1;
} else if (charCode <= 0x7FF) {
charUtf8Length = 2;
} else if (charCode <= 0xFFFF) {
charUtf8Length = 3;
} else {
charUtf8Length = 4;
}
if (utf8BytesCount + charUtf8Length > utf8Offset) {
break;
}
utf8BytesCount += charUtf8Length;
utf16Index++;
}
return utf16Index;
}
function getErrorLine(text, offset) {
var tbuf = text.slice(0,offset);
var lines = tbuf.split('\n')
if(lines.length > 0)return lines.length
return -1
}
Rectangle{
id:header_area
anchors.fill: parent
anchors.bottomMargin: 40
color:"transparent"
radius:8
2025-09-15 09:46:04 +08:00
border.color: HusTheme.isDark ? "#23272e" : "#f0f4f7"
2025-05-29 14:04:05 +08:00
border.width: 2
ScrollView {
id: textScrollView
anchors.fill: parent
clip: true
TextArea {
id: jsonEditor
font.family: "Consolas"
font.pixelSize: 14
wrapMode: TextArea.Wrap
placeholderText: "请输入JSON内容..."
selectByMouse: true
selectionColor: "#0078d4" // 选中区域颜色Fluent 主题蓝)
text:projectConfigStr
JsonHighlighter {
document: jsonEditor.textDocument
}
onTextChanged: {
var result = jsonProcessor.validateJson(jsonEditor.text)
if (result.hasError) {
var utf16Index = utf8OffsetToUtf16Index(jsonEditor.text, result.errorOffset);
errorPosition = utf16Index
// 计算错误行号
errorLine = getErrorLine(jsonEditor.text, utf16Index)
errorLabel.text = "❌ JSON语法错误" + ` ${errorLine} ${result.errorMessage}`
errorCanvas.requestPaint()
} else {
errorLine = -1
errorPosition = -1
}
errorLabel.visible = (errorLine !== -1)
}
}
Canvas {
id: errorCanvas
anchors.fill: parent
visible: errorPosition !== -1
z: 1
renderTarget: Canvas.Image
onPaint: {
var ctx = getContext("2d")
ctx.clearRect(0, 0, width, height)
if (errorPosition === -1) return
// 获取错误位置在TextArea中的坐标
var rect = jsonEditor.positionToRectangle(errorPosition)
if (!rect || rect.width === 0) return
// 关键步骤映射到ScrollView的contentItemFlickable
var flickablePos = jsonEditor.mapToItem(textScrollView.contentItem, rect.x, rect.y)
// 计算滚动偏移后的可视坐标(减去滚动量)
var canvasX = flickablePos.x - textScrollView.contentItem.contentX
var canvasY = flickablePos.y + textScrollView.contentItem.contentY
// 调整标记到文本下方
canvasY += rect.height
// 绘制红色三角形(向下箭头)
ctx.fillStyle = "#d13438"
ctx.beginPath()
ctx.moveTo(canvasX, canvasY) // 顶点
ctx.lineTo(canvasX - 8, canvasY + 10) // 左下
ctx.lineTo(canvasX + 8, canvasY + 10) // 右下
ctx.closePath()
ctx.fill()
}
// 滚动时实时更新
Connections {
target: textScrollView.contentItem
function onContentYChanged(){
errorCanvas.requestPaint()
}
function onContentXChanged(){
errorCanvas.requestPaint()
}
}
}
}
}
// 按钮区域(底部固定高度)
RowLayout {
anchors.top:header_area.bottom
anchors.topMargin: 6
anchors.horizontalCenter: parent.horizontalCenter
spacing: 15
// 错误提示
Text {
id: errorLabel
Layout.alignment: Qt.AlignHCenter
visible: false
color: "#d13438"
font.bold: true
}
2025-09-15 09:46:04 +08:00
HusButton {
2025-05-29 14:04:05 +08:00
text: "格式化"
onClicked: {
jsonEditor.text = jsonProcessor.formatJson(jsonEditor.text)
}
}
2025-09-15 09:46:04 +08:00
HusButton {
2025-05-29 14:04:05 +08:00
text: "保存"
onClicked: saveFunction(jsonEditor.text)
}
}
}