722 lines
31 KiB
JavaScript
722 lines
31 KiB
JavaScript
|
/**
|
|||
|
* 可视化编辑器模块
|
|||
|
* @type {module}
|
|||
|
*/
|
|||
|
import { XmlUtils } from './xml-utils.js';
|
|||
|
import { DateTimeDialog } from './date-time-dialog.js';
|
|||
|
import { CommandDialog } from './command-dialog.js';
|
|||
|
|
|||
|
export class VisualEditor {
|
|||
|
/**
|
|||
|
* 渲染可视化编辑器
|
|||
|
* @param {HTMLElement} container - 容器元素
|
|||
|
* @param {Document} xmlDoc - XML文档
|
|||
|
* @param {Function} onEdit - 编辑回调函数
|
|||
|
* @returns {boolean} 是否渲染成功
|
|||
|
*/
|
|||
|
static render(container, xmlDoc, onEdit) {
|
|||
|
if (!xmlDoc || !container) return false;
|
|||
|
|
|||
|
const style = document.createElement('style');
|
|||
|
style.textContent = `
|
|||
|
.section {
|
|||
|
margin-bottom: 20px;
|
|||
|
border: 1px solid #e0e0e0;
|
|||
|
border-radius: 4px;
|
|||
|
padding: 10px;
|
|||
|
background-color: white;
|
|||
|
}
|
|||
|
|
|||
|
.section-header {
|
|||
|
display: flex;
|
|||
|
justify-content: space-between;
|
|||
|
align-items: center;
|
|||
|
margin-bottom: 10px;
|
|||
|
padding-bottom: 5px;
|
|||
|
border-bottom: 1px solid #e0e0e0;
|
|||
|
}
|
|||
|
|
|||
|
.section-title {
|
|||
|
font-weight: bold;
|
|||
|
color: #555;
|
|||
|
}
|
|||
|
|
|||
|
.property-table {
|
|||
|
width: 100%;
|
|||
|
border-collapse: collapse;
|
|||
|
}
|
|||
|
|
|||
|
.property-table th, .property-table td {
|
|||
|
text-align: left;
|
|||
|
padding: 8px;
|
|||
|
border-bottom: 1px solid #e0e0e0;
|
|||
|
}
|
|||
|
|
|||
|
.property-table th {
|
|||
|
background-color: #f5f5f5;
|
|||
|
}
|
|||
|
|
|||
|
.form-group {
|
|||
|
margin-bottom: 15px;
|
|||
|
}
|
|||
|
|
|||
|
.form-group label {
|
|||
|
display: block;
|
|||
|
margin-bottom: 5px;
|
|||
|
font-weight: 500;
|
|||
|
}
|
|||
|
|
|||
|
.form-control {
|
|||
|
width: 100%;
|
|||
|
padding: 8px;
|
|||
|
box-sizing: border-box;
|
|||
|
border: 1px solid #ddd;
|
|||
|
border-radius: 4px;
|
|||
|
}
|
|||
|
|
|||
|
.inline-form {
|
|||
|
display: flex;
|
|||
|
gap: 16px;
|
|||
|
}
|
|||
|
|
|||
|
.inline-form .form-group {
|
|||
|
flex: 1;
|
|||
|
}
|
|||
|
|
|||
|
.two-column-form {
|
|||
|
display: grid;
|
|||
|
grid-template-columns: repeat(2, 1fr);
|
|||
|
gap: 16px;
|
|||
|
}
|
|||
|
|
|||
|
.input-container {
|
|||
|
display: flex;
|
|||
|
align-items: center;
|
|||
|
gap: 8px;
|
|||
|
}
|
|||
|
|
|||
|
.input-container .icon-button {
|
|||
|
min-width: 28px;
|
|||
|
height: 28px;
|
|||
|
background-size: 16px;
|
|||
|
background-position: center;
|
|||
|
background-repeat: no-repeat;
|
|||
|
border: none;
|
|||
|
cursor: pointer;
|
|||
|
background-color: transparent;
|
|||
|
opacity: 0.7;
|
|||
|
}
|
|||
|
|
|||
|
.input-container .icon-button:hover {
|
|||
|
opacity: 1;
|
|||
|
}
|
|||
|
|
|||
|
.calendar-button {
|
|||
|
background-image: url('assets/icons/png/calendar_b.png');
|
|||
|
}
|
|||
|
|
|||
|
.command-table {
|
|||
|
width: 100%;
|
|||
|
border-collapse: collapse;
|
|||
|
margin-top: 10px;
|
|||
|
}
|
|||
|
|
|||
|
.command-table th, .command-table td {
|
|||
|
padding: 8px;
|
|||
|
text-align: left;
|
|||
|
border: 1px solid #ddd;
|
|||
|
}
|
|||
|
|
|||
|
.command-table th {
|
|||
|
background-color: #f5f5f5;
|
|||
|
}
|
|||
|
|
|||
|
.element-box {
|
|||
|
display: flex;
|
|||
|
flex-direction: column;
|
|||
|
padding: 10px;
|
|||
|
border: 1px solid #e0e0e0;
|
|||
|
border-radius: 4px;
|
|||
|
margin-bottom: 10px;
|
|||
|
background-color: #f9f9f9;
|
|||
|
}
|
|||
|
|
|||
|
.element-header {
|
|||
|
display: flex;
|
|||
|
align-items: center;
|
|||
|
margin-bottom: 8px;
|
|||
|
}
|
|||
|
|
|||
|
.element-name {
|
|||
|
font-weight: 500;
|
|||
|
color: #444;
|
|||
|
flex: 1;
|
|||
|
}
|
|||
|
|
|||
|
.element-value {
|
|||
|
width: 100%;
|
|||
|
}
|
|||
|
|
|||
|
.element-value input {
|
|||
|
max-width: 100%;
|
|||
|
}
|
|||
|
|
|||
|
.element-input-row {
|
|||
|
display: flex;
|
|||
|
align-items: center;
|
|||
|
gap: 8px;
|
|||
|
width: 100%;
|
|||
|
}
|
|||
|
|
|||
|
.element-input-row .form-control {
|
|||
|
flex: 1;
|
|||
|
}
|
|||
|
|
|||
|
.nested-elements {
|
|||
|
padding-left: 20px;
|
|||
|
margin-top: 5px;
|
|||
|
border-left: 1px dashed #ccc;
|
|||
|
}
|
|||
|
`;
|
|||
|
|
|||
|
const visualEditor = document.createElement('div');
|
|||
|
visualEditor.className = 'visual-editor';
|
|||
|
|
|||
|
// 获取根元素
|
|||
|
const rootElement = xmlDoc.documentElement;
|
|||
|
|
|||
|
// 只处理Service根元素
|
|||
|
if (rootElement.nodeName === 'Service') {
|
|||
|
// 添加基本信息部分
|
|||
|
visualEditor.appendChild(this.createBasicInfoSection(xmlDoc, onEdit));
|
|||
|
|
|||
|
// 添加命令列表部分
|
|||
|
visualEditor.appendChild(this.createCommandsSection(xmlDoc, onEdit));
|
|||
|
|
|||
|
// 添加其他设置部分
|
|||
|
visualEditor.appendChild(this.createOtherSettingsSection(xmlDoc, onEdit));
|
|||
|
|
|||
|
container.appendChild(style);
|
|||
|
container.appendChild(visualEditor);
|
|||
|
|
|||
|
return true;
|
|||
|
} else {
|
|||
|
// 不是Service根元素,显示错误信息
|
|||
|
visualEditor.innerHTML = `<div class="error-message">
|
|||
|
无法编辑:XML文档的根元素不是Service。
|
|||
|
请确保XML文档的根元素是Service。
|
|||
|
</div>`;
|
|||
|
|
|||
|
container.appendChild(style);
|
|||
|
container.appendChild(visualEditor);
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 创建基本信息部分
|
|||
|
*/
|
|||
|
static createBasicInfoSection(xmlDoc, onEdit) {
|
|||
|
const rootElement = xmlDoc.documentElement;
|
|||
|
|
|||
|
// 创建基本信息部分
|
|||
|
const basicInfoSection = document.createElement('div');
|
|||
|
basicInfoSection.className = 'section';
|
|||
|
|
|||
|
const basicInfoHeader = document.createElement('div');
|
|||
|
basicInfoHeader.className = 'section-header';
|
|||
|
basicInfoHeader.innerHTML = '<div class="section-title">基本信息</div>';
|
|||
|
basicInfoSection.appendChild(basicInfoHeader);
|
|||
|
|
|||
|
// 获取基本信息数据
|
|||
|
const nameElement = rootElement.querySelector('Name') || xmlDoc.createElement('Name');
|
|||
|
const descElement = rootElement.querySelector('Description') || xmlDoc.createElement('Description');
|
|||
|
const authorElement = rootElement.querySelector('Author') || xmlDoc.createElement('Author');
|
|||
|
const versionElement = rootElement.querySelector('Version') || xmlDoc.createElement('Version');
|
|||
|
const createTimeElement = rootElement.querySelector('CreateTime') || xmlDoc.createElement('CreateTime');
|
|||
|
const changeTimeElement = rootElement.querySelector('ChangeTime') || xmlDoc.createElement('ChangeTime');
|
|||
|
|
|||
|
// 确保元素存在于XML中
|
|||
|
if (!rootElement.querySelector('Name')) rootElement.appendChild(nameElement);
|
|||
|
if (!rootElement.querySelector('Description')) rootElement.appendChild(descElement);
|
|||
|
if (!rootElement.querySelector('Author')) rootElement.appendChild(authorElement);
|
|||
|
if (!rootElement.querySelector('Version')) rootElement.appendChild(versionElement);
|
|||
|
if (!rootElement.querySelector('CreateTime')) {
|
|||
|
createTimeElement.textContent = DateTimeDialog.getCurrentDateTime();
|
|||
|
rootElement.appendChild(createTimeElement);
|
|||
|
}
|
|||
|
if (!rootElement.querySelector('ChangeTime')) {
|
|||
|
changeTimeElement.textContent = DateTimeDialog.getCurrentDateTime();
|
|||
|
rootElement.appendChild(changeTimeElement);
|
|||
|
}
|
|||
|
|
|||
|
// 创建基本信息表单
|
|||
|
const basicInfoForm = document.createElement('div');
|
|||
|
basicInfoForm.className = 'form-container';
|
|||
|
|
|||
|
// 服务名称和描述在同一行
|
|||
|
const nameDescContainer = document.createElement('div');
|
|||
|
nameDescContainer.className = 'inline-form';
|
|||
|
nameDescContainer.innerHTML = `
|
|||
|
<div class="form-group">
|
|||
|
<label for="serviceName">服务名称</label>
|
|||
|
<input type="text" id="serviceName" class="form-control" placeholder="请输入服务名称" value="${XmlUtils.escapeHtml(nameElement.textContent || '')}" />
|
|||
|
</div>
|
|||
|
<div class="form-group">
|
|||
|
<label for="serviceDesc">服务描述</label>
|
|||
|
<input type="text" id="serviceDesc" class="form-control" placeholder="请输入服务描述" value="${XmlUtils.escapeHtml(descElement.textContent || '')}" />
|
|||
|
</div>
|
|||
|
`;
|
|||
|
basicInfoForm.appendChild(nameDescContainer);
|
|||
|
|
|||
|
// 其他信息以两列布局
|
|||
|
const otherInfoContainer = document.createElement('div');
|
|||
|
otherInfoContainer.className = 'two-column-form';
|
|||
|
otherInfoContainer.style.marginTop = '15px';
|
|||
|
otherInfoContainer.innerHTML = `
|
|||
|
<div class="form-group">
|
|||
|
<label for="serviceAuthor">作者</label>
|
|||
|
<input type="text" id="serviceAuthor" class="form-control" placeholder="请输入作者" value="${XmlUtils.escapeHtml(authorElement.textContent || '')}" />
|
|||
|
</div>
|
|||
|
<div class="form-group">
|
|||
|
<label for="serviceVersion">版本</label>
|
|||
|
<input type="text" id="serviceVersion" class="form-control" placeholder="请输入版本号" value="${XmlUtils.escapeHtml(versionElement.textContent || '1.0.0')}" />
|
|||
|
</div>
|
|||
|
<div class="form-group">
|
|||
|
<label for="serviceCreateTime">创建时间</label>
|
|||
|
<div class="input-container">
|
|||
|
<input type="text" id="serviceCreateTime" class="form-control" placeholder="YYYY-MM-DD HH:MM:SS" value="${XmlUtils.escapeHtml(createTimeElement.textContent || '')}" />
|
|||
|
<button class="icon-button calendar-button" title="选择日期和时间" id="createTimeBtn"></button>
|
|||
|
<button class="icon-button refresh-button-sm" title="设置为当前时间" id="refreshCreateTimeBtn" style="background-image: url('assets/icons/png/refresh_b.png'); min-width: 28px; height: 28px; background-size: 16px; background-position: center; background-repeat: no-repeat; border: none; cursor: pointer; background-color: transparent; opacity: 0.7;"></button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="form-group">
|
|||
|
<label for="serviceChangeTime">修改时间</label>
|
|||
|
<div class="input-container">
|
|||
|
<input type="text" id="serviceChangeTime" class="form-control" placeholder="YYYY-MM-DD HH:MM:SS" value="${XmlUtils.escapeHtml(changeTimeElement.textContent || '')}" />
|
|||
|
<button class="icon-button calendar-button" title="选择日期和时间" id="changeTimeBtn"></button>
|
|||
|
<button class="icon-button refresh-button-sm" title="设置为当前时间" id="refreshChangeTimeBtn" style="background-image: url('assets/icons/png/refresh_b.png'); min-width: 28px; height: 28px; background-size: 16px; background-position: center; background-repeat: no-repeat; border: none; cursor: pointer; background-color: transparent; opacity: 0.7;"></button>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
`;
|
|||
|
basicInfoForm.appendChild(otherInfoContainer);
|
|||
|
|
|||
|
basicInfoSection.appendChild(basicInfoForm);
|
|||
|
|
|||
|
// 添加事件监听
|
|||
|
basicInfoSection.addEventListener('change', (e) => {
|
|||
|
if (e.target.id === 'serviceName') {
|
|||
|
nameElement.textContent = e.target.value;
|
|||
|
if (onEdit) onEdit();
|
|||
|
} else if (e.target.id === 'serviceDesc') {
|
|||
|
descElement.textContent = e.target.value;
|
|||
|
if (onEdit) onEdit();
|
|||
|
} else if (e.target.id === 'serviceAuthor') {
|
|||
|
authorElement.textContent = e.target.value;
|
|||
|
if (onEdit) onEdit();
|
|||
|
} else if (e.target.id === 'serviceVersion') {
|
|||
|
versionElement.textContent = e.target.value;
|
|||
|
if (onEdit) onEdit();
|
|||
|
} else if (e.target.id === 'serviceCreateTime') {
|
|||
|
createTimeElement.textContent = e.target.value;
|
|||
|
if (onEdit) onEdit();
|
|||
|
} else if (e.target.id === 'serviceChangeTime') {
|
|||
|
changeTimeElement.textContent = e.target.value;
|
|||
|
if (onEdit) onEdit();
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// 添加日期时间选择器事件
|
|||
|
basicInfoSection.querySelector('#createTimeBtn').addEventListener('click', () => {
|
|||
|
const input = basicInfoSection.querySelector('#serviceCreateTime');
|
|||
|
DateTimeDialog.show(basicInfoSection.ownerDocument.body, input, (newValue) => {
|
|||
|
input.value = newValue;
|
|||
|
createTimeElement.textContent = newValue;
|
|||
|
if (onEdit) onEdit();
|
|||
|
});
|
|||
|
});
|
|||
|
|
|||
|
basicInfoSection.querySelector('#changeTimeBtn').addEventListener('click', () => {
|
|||
|
const input = basicInfoSection.querySelector('#serviceChangeTime');
|
|||
|
DateTimeDialog.show(basicInfoSection.ownerDocument.body, input, (newValue) => {
|
|||
|
input.value = newValue;
|
|||
|
changeTimeElement.textContent = newValue;
|
|||
|
if (onEdit) onEdit();
|
|||
|
});
|
|||
|
});
|
|||
|
|
|||
|
// 添加刷新按钮事件
|
|||
|
basicInfoSection.querySelector('#refreshCreateTimeBtn').addEventListener('click', () => {
|
|||
|
const input = basicInfoSection.querySelector('#serviceCreateTime');
|
|||
|
const datetimeStr = DateTimeDialog.getCurrentDateTime();
|
|||
|
|
|||
|
input.value = datetimeStr;
|
|||
|
createTimeElement.textContent = datetimeStr;
|
|||
|
if (onEdit) onEdit();
|
|||
|
});
|
|||
|
|
|||
|
basicInfoSection.querySelector('#refreshChangeTimeBtn').addEventListener('click', () => {
|
|||
|
const input = basicInfoSection.querySelector('#serviceChangeTime');
|
|||
|
const datetimeStr = DateTimeDialog.getCurrentDateTime();
|
|||
|
|
|||
|
input.value = datetimeStr;
|
|||
|
changeTimeElement.textContent = datetimeStr;
|
|||
|
if (onEdit) onEdit();
|
|||
|
});
|
|||
|
|
|||
|
return basicInfoSection;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 创建命令列表部分
|
|||
|
*/
|
|||
|
static createCommandsSection(xmlDoc, onEdit) {
|
|||
|
const rootElement = xmlDoc.documentElement;
|
|||
|
|
|||
|
// 创建命令列表部分
|
|||
|
const commandsSection = document.createElement('div');
|
|||
|
commandsSection.className = 'section';
|
|||
|
|
|||
|
const commandsHeader = document.createElement('div');
|
|||
|
commandsHeader.className = 'section-header';
|
|||
|
commandsHeader.innerHTML = `
|
|||
|
<div class="section-title">指令列表</div>
|
|||
|
<button id="addCommandBtn" class="action-button">添加指令</button>
|
|||
|
`;
|
|||
|
commandsSection.appendChild(commandsHeader);
|
|||
|
|
|||
|
// 获取或创建命令列表
|
|||
|
let commandListElement = rootElement.querySelector('CommandList');
|
|||
|
if (!commandListElement) {
|
|||
|
commandListElement = xmlDoc.createElement('CommandList');
|
|||
|
rootElement.appendChild(commandListElement);
|
|||
|
}
|
|||
|
|
|||
|
// 创建命令表格
|
|||
|
const commandsTable = document.createElement('table');
|
|||
|
commandsTable.className = 'command-table';
|
|||
|
commandsTable.innerHTML = `
|
|||
|
<thead>
|
|||
|
<tr>
|
|||
|
<th style="width: 20%;">名称</th>
|
|||
|
<th style="width: 25%;">调用</th>
|
|||
|
<th style="width: 40%;">描述</th>
|
|||
|
<th style="width: 15%;">操作</th>
|
|||
|
</tr>
|
|||
|
</thead>
|
|||
|
<tbody id="commandsTableBody">
|
|||
|
</tbody>
|
|||
|
`;
|
|||
|
|
|||
|
// 创建表格内容
|
|||
|
const commandsTableBody = commandsTable.querySelector('#commandsTableBody');
|
|||
|
|
|||
|
// 更新命令表格的函数
|
|||
|
const updateCommandTable = () => {
|
|||
|
// 清空表格
|
|||
|
commandsTableBody.innerHTML = '';
|
|||
|
|
|||
|
// 获取所有命令
|
|||
|
const commandElements = commandListElement.querySelectorAll('Command');
|
|||
|
|
|||
|
if (commandElements.length === 0) {
|
|||
|
// 如果没有命令,显示空行
|
|||
|
const emptyRow = document.createElement('tr');
|
|||
|
emptyRow.innerHTML = '<td colspan="4" style="text-align: center;">暂无指令</td>';
|
|||
|
commandsTableBody.appendChild(emptyRow);
|
|||
|
} else {
|
|||
|
// 添加所有命令到表格
|
|||
|
commandElements.forEach((command, index) => {
|
|||
|
const name = command.getAttribute('Name') || '';
|
|||
|
const call = command.getAttribute('Call') || '';
|
|||
|
const description = command.getAttribute('Description') || '';
|
|||
|
|
|||
|
const row = document.createElement('tr');
|
|||
|
row.dataset.index = index;
|
|||
|
row.innerHTML = `
|
|||
|
<td>${XmlUtils.escapeHtml(name)}</td>
|
|||
|
<td>${XmlUtils.escapeHtml(call)}</td>
|
|||
|
<td>${XmlUtils.escapeHtml(description)}</td>
|
|||
|
<td>
|
|||
|
<button class="action-button edit-btn" data-index="${index}">编辑</button>
|
|||
|
<button class="action-button delete-btn" data-index="${index}" style="background-color: #E77979;">删除</button>
|
|||
|
</td>
|
|||
|
`;
|
|||
|
|
|||
|
commandsTableBody.appendChild(row);
|
|||
|
});
|
|||
|
|
|||
|
// 添加编辑和删除事件
|
|||
|
commandsTableBody.querySelectorAll('.edit-btn').forEach(btn => {
|
|||
|
btn.addEventListener('click', () => {
|
|||
|
const index = parseInt(btn.dataset.index);
|
|||
|
const command = commandElements[index];
|
|||
|
CommandDialog.showEditDialog(
|
|||
|
commandsSection.ownerDocument.body,
|
|||
|
command,
|
|||
|
index,
|
|||
|
() => {
|
|||
|
updateCommandTable();
|
|||
|
if (onEdit) onEdit();
|
|||
|
}
|
|||
|
);
|
|||
|
});
|
|||
|
});
|
|||
|
|
|||
|
commandsTableBody.querySelectorAll('.delete-btn').forEach(btn => {
|
|||
|
btn.addEventListener('click', () => {
|
|||
|
const index = parseInt(btn.dataset.index);
|
|||
|
const command = commandElements[index];
|
|||
|
if (confirm(`确定要删除指令 ${command.getAttribute('Name')} 吗?`)) {
|
|||
|
commandListElement.removeChild(command);
|
|||
|
updateCommandTable();
|
|||
|
if (onEdit) onEdit();
|
|||
|
}
|
|||
|
});
|
|||
|
});
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// 初始化表格
|
|||
|
updateCommandTable();
|
|||
|
|
|||
|
// 添加"添加指令"按钮事件
|
|||
|
commandsSection.querySelector('#addCommandBtn').addEventListener('click', () => {
|
|||
|
CommandDialog.showAddDialog(
|
|||
|
commandsSection.ownerDocument.body,
|
|||
|
commandListElement,
|
|||
|
xmlDoc,
|
|||
|
() => {
|
|||
|
updateCommandTable();
|
|||
|
if (onEdit) onEdit();
|
|||
|
}
|
|||
|
);
|
|||
|
});
|
|||
|
|
|||
|
// 添加表格
|
|||
|
commandsSection.appendChild(commandsTable);
|
|||
|
|
|||
|
return commandsSection;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 创建其他设置部分
|
|||
|
*/
|
|||
|
static createOtherSettingsSection(xmlDoc, onEdit) {
|
|||
|
const rootElement = xmlDoc.documentElement;
|
|||
|
|
|||
|
// 创建其他设置部分
|
|||
|
const otherSettingsSection = document.createElement('div');
|
|||
|
otherSettingsSection.className = 'section';
|
|||
|
|
|||
|
const otherSettingsHeader = document.createElement('div');
|
|||
|
otherSettingsHeader.className = 'section-header';
|
|||
|
otherSettingsHeader.innerHTML = `
|
|||
|
<div class="section-title">其他设置</div>
|
|||
|
<span class="settings-hint" style="font-size: 12px; color: #777; font-style: italic;">如需添加参数,请手动编辑配置文件</span>
|
|||
|
`;
|
|||
|
otherSettingsSection.appendChild(otherSettingsHeader);
|
|||
|
|
|||
|
// 创建其他设置内容
|
|||
|
const otherSettingsContent = document.createElement('div');
|
|||
|
otherSettingsContent.className = 'other-settings-container';
|
|||
|
|
|||
|
// 递归处理元素及其子元素
|
|||
|
const processElement = (parentElement, containerElement, level = 0) => {
|
|||
|
// 筛选节点元素,排除文本节点
|
|||
|
const childElements = Array.from(parentElement.childNodes)
|
|||
|
.filter(node => node.nodeType === 1); // 只处理元素节点
|
|||
|
|
|||
|
if (childElements.length === 0) return;
|
|||
|
|
|||
|
childElements.forEach(element => {
|
|||
|
// 排除已处理的基本信息元素和命令列表
|
|||
|
const nodeName = element.nodeName;
|
|||
|
if (['Name', 'Description', 'Author', 'Version', 'CreateTime',
|
|||
|
'ChangeTime', 'CommandList'].includes(nodeName)) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// 创建元素框
|
|||
|
const elementBox = document.createElement('div');
|
|||
|
elementBox.className = 'element-box';
|
|||
|
elementBox.id = `element-${getElementPath(element).replace(/\./g, '-')}`;
|
|||
|
if (level > 0) {
|
|||
|
elementBox.style.marginLeft = `${level * 10}px`;
|
|||
|
}
|
|||
|
|
|||
|
// 创建元素头部(包含名称)
|
|||
|
const elementHeader = document.createElement('div');
|
|||
|
elementHeader.className = 'element-header';
|
|||
|
|
|||
|
// 创建元素名称
|
|||
|
const elementName = document.createElement('div');
|
|||
|
elementName.className = 'element-name';
|
|||
|
elementName.textContent = nodeName;
|
|||
|
elementHeader.appendChild(elementName);
|
|||
|
|
|||
|
// 将元素头部添加到元素框
|
|||
|
elementBox.appendChild(elementHeader);
|
|||
|
|
|||
|
// 创建元素内容容器
|
|||
|
const elementContent = document.createElement('div');
|
|||
|
elementContent.className = 'element-value';
|
|||
|
|
|||
|
// 处理不同类型的元素内容
|
|||
|
const hasChildElements = Array.from(element.childNodes)
|
|||
|
.some(node => node.nodeType === 1);
|
|||
|
|
|||
|
if (hasChildElements) {
|
|||
|
// 如果有子元素,为它们创建一个嵌套容器
|
|||
|
const childContainer = document.createElement('div');
|
|||
|
childContainer.className = 'nested-elements';
|
|||
|
|
|||
|
// 递归处理子元素
|
|||
|
processElement(element, childContainer, level + 1);
|
|||
|
|
|||
|
elementContent.appendChild(childContainer);
|
|||
|
|
|||
|
// 为父元素添加删除按钮
|
|||
|
const deleteBtn = document.createElement('button');
|
|||
|
deleteBtn.className = 'action-button';
|
|||
|
deleteBtn.textContent = '删除';
|
|||
|
deleteBtn.style.backgroundColor = '#e77979';
|
|||
|
deleteBtn.style.marginTop = '8px';
|
|||
|
deleteBtn.addEventListener('click', () => {
|
|||
|
if (confirm(`确定要删除 ${nodeName} 及其所有子元素吗?`)) {
|
|||
|
try {
|
|||
|
// 从DOM和XML中删除元素
|
|||
|
parentElement.removeChild(element);
|
|||
|
|
|||
|
// 移除元素框
|
|||
|
if (elementBox.parentNode) {
|
|||
|
elementBox.parentNode.removeChild(elementBox);
|
|||
|
}
|
|||
|
|
|||
|
// 触发编辑回调
|
|||
|
if (onEdit) onEdit();
|
|||
|
|
|||
|
// 如果没有其他元素了,显示"没有其他设置项"提示
|
|||
|
const otherSettingsContainer = otherSettingsContent;
|
|||
|
if (otherSettingsContainer && otherSettingsContainer.children.length === 0) {
|
|||
|
const noElementsMsg = document.createElement('div');
|
|||
|
noElementsMsg.style.padding = '10px';
|
|||
|
noElementsMsg.style.color = '#666';
|
|||
|
noElementsMsg.textContent = '没有其他设置项';
|
|||
|
otherSettingsContainer.appendChild(noElementsMsg);
|
|||
|
}
|
|||
|
} catch (error) {
|
|||
|
console.error('删除元素时出错:', error);
|
|||
|
alert(`删除元素失败: ${error.message}`);
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
elementContent.appendChild(deleteBtn);
|
|||
|
} else {
|
|||
|
// 如果没有子元素,创建可编辑的文本输入
|
|||
|
const elementValue = element.textContent || '';
|
|||
|
|
|||
|
const inputRow = document.createElement('div');
|
|||
|
inputRow.className = 'element-input-row';
|
|||
|
|
|||
|
const input = document.createElement('input');
|
|||
|
input.type = 'text';
|
|||
|
input.className = 'form-control';
|
|||
|
input.value = elementValue;
|
|||
|
input.dataset.elementPath = getElementPath(element);
|
|||
|
|
|||
|
// 为输入框添加事件监听
|
|||
|
input.addEventListener('change', (e) => {
|
|||
|
element.textContent = e.target.value;
|
|||
|
if (onEdit) onEdit();
|
|||
|
});
|
|||
|
|
|||
|
inputRow.appendChild(input);
|
|||
|
|
|||
|
// 添加删除按钮
|
|||
|
const deleteBtn = document.createElement('button');
|
|||
|
deleteBtn.className = 'action-button';
|
|||
|
deleteBtn.textContent = '删除';
|
|||
|
deleteBtn.style.backgroundColor = '#e77979';
|
|||
|
deleteBtn.addEventListener('click', () => {
|
|||
|
if (confirm(`确定要删除 ${nodeName} 吗?`)) {
|
|||
|
try {
|
|||
|
// 从DOM和XML中删除元素
|
|||
|
parentElement.removeChild(element);
|
|||
|
|
|||
|
// 移除元素框
|
|||
|
if (elementBox.parentNode) {
|
|||
|
elementBox.parentNode.removeChild(elementBox);
|
|||
|
}
|
|||
|
|
|||
|
// 触发编辑回调
|
|||
|
if (onEdit) onEdit();
|
|||
|
|
|||
|
// 如果没有其他元素了,显示"没有其他设置项"提示
|
|||
|
const otherSettingsContainer = otherSettingsContent;
|
|||
|
if (otherSettingsContainer && otherSettingsContainer.querySelectorAll('.element-box').length === 0) {
|
|||
|
const noElementsMsg = document.createElement('div');
|
|||
|
noElementsMsg.style.padding = '10px';
|
|||
|
noElementsMsg.style.color = '#666';
|
|||
|
noElementsMsg.textContent = '没有其他设置项';
|
|||
|
otherSettingsContainer.appendChild(noElementsMsg);
|
|||
|
}
|
|||
|
} catch (error) {
|
|||
|
console.error('删除元素时出错:', error);
|
|||
|
alert(`删除元素失败: ${error.message}`);
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
inputRow.appendChild(deleteBtn);
|
|||
|
elementContent.appendChild(inputRow);
|
|||
|
}
|
|||
|
|
|||
|
// 将元素内容添加到元素框
|
|||
|
elementBox.appendChild(elementContent);
|
|||
|
|
|||
|
// 将元素框添加到容器
|
|||
|
containerElement.appendChild(elementBox);
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
// 获取元素的路径,用于唯一标识
|
|||
|
const getElementPath = (element) => {
|
|||
|
const path = [];
|
|||
|
let current = element;
|
|||
|
|
|||
|
while (current && current !== xmlDoc) {
|
|||
|
const nodeName = current.nodeName;
|
|||
|
path.unshift(nodeName);
|
|||
|
current = current.parentNode;
|
|||
|
}
|
|||
|
|
|||
|
return path.join('.');
|
|||
|
};
|
|||
|
|
|||
|
// 处理其他元素(排除标准元素)
|
|||
|
const standardElements = ['Name', 'Description', 'Author', 'Version',
|
|||
|
'CreateTime', 'ChangeTime', 'CommandList'];
|
|||
|
|
|||
|
const otherElements = Array.from(rootElement.childNodes)
|
|||
|
.filter(node => node.nodeType === 1 && !standardElements.includes(node.nodeName));
|
|||
|
|
|||
|
if (otherElements.length > 0) {
|
|||
|
// 处理所有其他元素
|
|||
|
processElement(rootElement, otherSettingsContent);
|
|||
|
} else {
|
|||
|
// 如果没有其他元素,显示提示
|
|||
|
const noElementsMsg = document.createElement('div');
|
|||
|
noElementsMsg.style.padding = '10px';
|
|||
|
noElementsMsg.style.color = '#666';
|
|||
|
noElementsMsg.textContent = '没有其他设置项';
|
|||
|
otherSettingsContent.appendChild(noElementsMsg);
|
|||
|
}
|
|||
|
|
|||
|
otherSettingsSection.appendChild(otherSettingsContent);
|
|||
|
|
|||
|
return otherSettingsSection;
|
|||
|
}
|
|||
|
}
|