445 lines
15 KiB
JavaScript
Raw Normal View History

2025-04-28 12:25:20 +08:00
/**
* 工具函数模块
* @type {module}
*/
import { saveFileContent } from './file-operations.js';
/**
* 格式化XML字符串
* @param {string} xml - XML字符串
* @returns {string} 格式化后的XML字符串
*/
export function formatXml(xml) {
if (!xml || !xml.trim()) return '';
try {
let formatted = '';
const parser = new DOMParser();
const doc = parser.parseFromString(xml, 'text/xml');
// 检查是否解析错误
const parseError = doc.querySelector('parsererror');
if (parseError) {
console.warn('XML解析错误返回原始文本');
return xml; // 如果解析错误,返回原始文本
}
// 使用XMLSerializer序列化为字符串
const serializer = new XMLSerializer();
formatted = serializer.serializeToString(doc);
// 简单格式化:替换'><'为'>\n<'添加换行
formatted = formatted.replace(/><(?!\/)/g, '>\n<');
// 添加缩进
const lines = formatted.split('\n');
let indent = 0;
formatted = lines.map(line => {
if (line.match(/<\/[^>]+>/)) {
// 关闭标签,减少缩进
indent--;
}
const result = ' '.repeat(Math.max(0, indent * 2)) + line;
if (line.match(/<[^\/][^>]*[^\/]>/) && !line.match(/<.*\/>/) && !line.match(/<[^>]+><\/[^>]+>/)) {
// 开放标签,增加缩进
indent++;
}
return result;
}).join('\n');
return formatted;
} catch (e) {
console.error('XML格式化失败:', e);
return xml;
}
}
/**
* 转义HTML字符
* @param {string} unsafe - 不安全的HTML字符串
* @returns {string} 转义后的HTML字符串
*/
export function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
/**
* 创建模态对话框
* @param {HTMLElement} shadowRoot - Shadow DOM根元素
* @param {string} title - 对话框标题
* @param {HTMLElement|string} content - 对话框内容
* @param {Function} confirmCallback - 确认按钮回调函数
* @returns {Object} 对话框引用
*/
export function createModal(shadowRoot, title, content, confirmCallback) {
// 创建模态框容器
const modalOverlay = document.createElement('div');
modalOverlay.className = 'modal-overlay active';
modalOverlay.style.position = 'fixed';
modalOverlay.style.top = '0';
modalOverlay.style.left = '0';
modalOverlay.style.right = '0';
modalOverlay.style.bottom = '0';
modalOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
modalOverlay.style.display = 'flex';
modalOverlay.style.alignItems = 'center';
modalOverlay.style.justifyContent = 'center';
modalOverlay.style.zIndex = '1000';
// 创建模态框
const modal = document.createElement('div');
modal.className = 'modal-content';
modal.style.backgroundColor = 'white';
modal.style.borderRadius = '12px';
modal.style.padding = '24px';
modal.style.width = '500px';
modal.style.maxWidth = '90%';
modal.style.maxHeight = '90vh';
modal.style.overflowY = 'auto';
modal.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15)';
// 创建模态框头部
const modalHeader = document.createElement('div');
modalHeader.className = 'modal-header';
modalHeader.style.display = 'flex';
modalHeader.style.justifyContent = 'space-between';
modalHeader.style.alignItems = 'center';
modalHeader.style.marginBottom = '16px';
// 创建标题
const modalTitle = document.createElement('h2');
modalTitle.className = 'modal-title';
modalTitle.textContent = title;
modalTitle.style.fontSize = '20px';
modalTitle.style.fontWeight = 'bold';
modalTitle.style.color = '#2c3e50';
modalTitle.style.margin = '0';
// 关闭按钮
const closeBtn = document.createElement('button');
closeBtn.className = 'close-btn';
closeBtn.innerHTML = '&times;';
closeBtn.style.background = 'none';
closeBtn.style.border = 'none';
closeBtn.style.fontSize = '24px';
closeBtn.style.cursor = 'pointer';
closeBtn.style.color = '#718096';
closeBtn.style.lineHeight = '1';
closeBtn.style.padding = '0';
// 添加关闭按钮事件
closeBtn.onclick = () => {
modalOverlay.remove();
};
// 组装头部
modalHeader.appendChild(modalTitle);
modalHeader.appendChild(closeBtn);
// 创建表单容器
const formContainer = document.createElement('form');
formContainer.style.marginBottom = '0';
// 添加内容
if (content) {
// 如果内容已经是DOM元素直接使用
if (content instanceof HTMLElement) {
// 为内容添加样式
content.style.margin = '0 0 20px 0';
formContainer.appendChild(content);
} else {
// 否则创建新的内容容器
const contentDiv = document.createElement('div');
contentDiv.innerHTML = content;
contentDiv.style.margin = '0 0 20px 0';
formContainer.appendChild(contentDiv);
}
}
// 创建按钮容器
const modalFooter = document.createElement('div');
modalFooter.className = 'modal-footer';
modalFooter.style.display = 'flex';
modalFooter.style.justifyContent = 'flex-end';
modalFooter.style.gap = '12px';
modalFooter.style.marginTop = '24px';
// 取消按钮
const cancelButton = document.createElement('button');
cancelButton.className = 'btn btn-secondary';
cancelButton.type = 'button';
cancelButton.textContent = '取消';
cancelButton.style.padding = '10px 16px';
cancelButton.style.borderRadius = '6px';
cancelButton.style.fontSize = '15px';
cancelButton.style.cursor = 'pointer';
cancelButton.style.transition = 'all 0.3s';
cancelButton.style.backgroundColor = '#f8f9fa';
cancelButton.style.border = '1px solid #ddd';
cancelButton.style.color = '#718096';
// 添加取消按钮事件
cancelButton.onclick = () => {
modalOverlay.remove();
};
// 确定按钮
const confirmButton = document.createElement('button');
confirmButton.className = 'btn btn-primary';
confirmButton.type = 'button';
confirmButton.textContent = '确定';
confirmButton.style.padding = '10px 16px';
confirmButton.style.borderRadius = '6px';
confirmButton.style.fontSize = '15px';
confirmButton.style.cursor = 'pointer';
confirmButton.style.transition = 'all 0.3s';
confirmButton.style.background = 'linear-gradient(135deg, #667eea, #764ba2)';
confirmButton.style.border = 'none';
confirmButton.style.color = 'white';
// 添加确定按钮事件
confirmButton.onclick = () => {
if (confirmCallback) {
confirmCallback();
modalOverlay.remove(); // 确保回调执行后关闭对话框
}
};
// 点击背景关闭
modalOverlay.onclick = (e) => {
if (e.target === modalOverlay) {
modalOverlay.remove();
}
};
// 组装底部按钮
modalFooter.appendChild(cancelButton);
modalFooter.appendChild(confirmButton);
// 组装表单
formContainer.appendChild(modalFooter);
// 阻止表单提交默认行为
formContainer.onsubmit = (e) => {
e.preventDefault();
if (confirmCallback) {
confirmCallback();
modalOverlay.remove(); // 确保回调执行后关闭对话框
}
return false;
};
// 组装整个模态框
modal.appendChild(modalHeader);
modal.appendChild(formContainer);
modalOverlay.appendChild(modal);
// 添加到DOM
shadowRoot.appendChild(modalOverlay);
// 返回引用
return {
modal: modalOverlay,
confirmButton: confirmButton
};
}
/**
* 标记编辑状态
* @param {HTMLElement} shadowRoot - Shadow DOM根元素
* @param {boolean} isEdited - 是否已编辑
* @returns {boolean} 新的编辑状态
*/
export function markEdited(shadowRoot, isEdited) {
if (!isEdited) {
// 更新保存按钮样式
const saveButton = shadowRoot.getElementById('saveConfig');
if (saveButton) {
saveButton.classList.add('modified');
saveButton.title = '文件已修改,请保存';
}
return true;
}
return isEdited;
}
/**
* 重置编辑状态
* @param {HTMLElement} shadowRoot - Shadow DOM根元素
* @returns {boolean} 重置后的编辑状态(false)
*/
export function resetEditState(shadowRoot) {
// 更新保存按钮样式
const saveButton = shadowRoot.getElementById('saveConfig');
if (saveButton) {
saveButton.classList.remove('modified');
saveButton.title = '';
}
return false;
}
/**
* 检查是否需要保存当前更改
* @param {HTMLElement} component - 组件实例
* @returns {Promise<boolean>} 如果可以继续操作返回true否则返回false
*/
export async function checkSaveNeeded(component) {
// 检查是否有未保存的修改
const saveButton = component.shadowRoot.getElementById('saveConfig');
const hasUnsavedChanges = saveButton && saveButton.classList.contains('modified');
if (hasUnsavedChanges) {
// 使用自定义对话框替代简单的confirm
const dialogResult = await new Promise(resolve => {
// 创建模态框
const modal = document.createElement('div');
modal.className = 'modal';
modal.id = 'saveConfirmModal';
modal.innerHTML = `
<style>
.modal {
display: block;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.4);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 400px;
border-radius: 5px;
position: relative;
text-align: center;
}
.modal-title {
margin-top: 0;
color: #333;
font-size: 18px;
margin-bottom: 20px;
}
.modal-buttons {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 20px;
}
.btn {
padding: 8px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
min-width: 80px;
}
.btn-save {
background-color: #7986E7;
color: white;
}
.btn-save:hover {
background-color: #6875D6;
}
.btn-dont-save {
background-color: #E77979;
color: white;
}
.btn-dont-save:hover {
background-color: #D66868;
}
.btn-cancel {
background-color: #f5f5f5;
color: #333;
}
.btn-cancel:hover {
background-color: #e0e0e0;
}
</style>
<div class="modal-content">
<h3 class="modal-title">当前文件有未保存的更改</h3>
<p>您想要保存这些更改吗</p>
<div class="modal-buttons">
<button class="btn btn-save" id="saveBtn">保存</button>
<button class="btn btn-dont-save" id="dontSaveBtn">不保存</button>
<button class="btn btn-cancel" id="cancelBtn">取消</button>
</div>
</div>
`;
document.body.appendChild(modal);
// 添加事件监听
const saveBtn = modal.querySelector('#saveBtn');
const dontSaveBtn = modal.querySelector('#dontSaveBtn');
const cancelBtn = modal.querySelector('#cancelBtn');
const closeModal = () => {
document.body.removeChild(modal);
};
saveBtn.addEventListener('click', () => {
closeModal();
resolve('save');
});
dontSaveBtn.addEventListener('click', () => {
closeModal();
resolve('dont-save');
});
cancelBtn.addEventListener('click', () => {
closeModal();
resolve('cancel');
});
// 点击模态窗口外部也取消
modal.addEventListener('click', (event) => {
if (event.target === modal) {
closeModal();
resolve('cancel');
}
});
});
// 根据对话框结果执行相应操作
if (dialogResult === 'save') {
try {
// 保存文件
if (component.currentFile && component.xmlContent) {
await saveFileContent(component, component.currentFile, component.xmlContent);
resetEditState(component.shadowRoot);
return true; // 继续执行后续操作
} else {
return false; // 无法保存,不继续执行
}
} catch (error) {
console.error('保存出错:', error);
return false; // 保存失败,不继续执行
}
} else if (dialogResult === 'dont-save') {
// 不保存,但继续执行后续操作
return true;
} else {
// 用户取消,不执行后续操作
return false;
}
}
// 没有编辑状态直接返回true允许继续操作
return true;
}