2025-04-28 12:25:20 +08:00

481 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* UI渲染模块
* @type {module}
*/
import { escapeHtml } from './utils.js';
import { renderEnvironmentSection } from './environment-section.js';
import { renderModelGroupsSection } from './model-groups.js';
import { renderServicesSection } from './services.js';
import { renderConsoleAndLogSection } from './console-log.js';
/**
* 渲染基本UI
* @param {HTMLElement} component - 组件实例
*/
export function renderBasicUI(component) {
component.shadowRoot.innerHTML = `
<style>
:host {
display: block;
height: 100%;
overflow: auto;
padding: 16px;
box-sizing: border-box;
}
.config-container {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
padding: 16px;
height: 100%;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.config-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #e0e0e0;
}
.file-selector-header {
flex-grow: 1;
margin-right: 16px;
display: flex;
align-items: center;
}
.file-selector-label {
font-size: 18px;
font-weight: bold;
color: #333;
margin-right: 10px;
white-space: nowrap;
}
.file-selector-header select {
flex-grow: 1;
padding: 6px;
border-radius: 4px;
border: 1px solid #e0e0e0;
}
.refresh-button {
width: 28px;
height: 28px;
cursor: pointer;
margin-left: 8px;
border: none;
background-color: transparent;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
background-image: url('assets/icons/png/refresh_b.png');
transition: transform 0.3s ease;
opacity: 0.7;
}
.refresh-button:hover {
opacity: 1;
}
.refresh-button.refreshing {
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.action-buttons {
display: flex;
gap: 8px;
}
.action-button {
background-color: #7986E7;
color: white;
border: none;
border-radius: 4px;
padding: 6px 12px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}
.action-button.save-button.modified {
background-color: #E77979;
}
.action-button.save-button.modified:hover {
background-color: #D66868;
}
.action-button:hover {
background-color: #6875D6;
}
.config-content {
padding: 0;
flex-grow: 1;
display: flex;
flex-direction: column;
}
.file-content {
flex-grow: 1;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 12px;
overflow: auto;
background-color: #f9f9f9;
min-height: 200px;
}
.file-content pre {
margin: 0;
font-family: monospace;
white-space: pre-wrap;
word-wrap: break-word;
}
.no-file-selected {
color: #888;
font-style: italic;
text-align: center;
margin-top: 80px;
}
.visual-editor {
font-family: Arial, sans-serif;
color: #333;
}
.editor-section {
margin-bottom: 20px;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 16px;
background-color: #fff;
}
.section-title {
font-weight: bold;
font-size: 16px;
margin-bottom: 16px;
color: #444;
border-bottom: 1px solid #eee;
padding-bottom: 8px;
}
.property-group {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 16px;
margin-bottom: 16px;
}
.property-item {
margin-bottom: 12px;
background-color: #f9f9f9;
padding: 10px;
border-radius: 4px;
border: 1px solid #eee;
}
.property-label {
display: block;
font-size: 14px;
margin-bottom: 6px;
color: #555;
font-weight: bold;
}
.property-input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
}
.property-input:focus {
border-color: #7986E7;
outline: none;
box-shadow: 0 0 0 2px rgba(121, 134, 231, 0.2);
}
.add-button {
background-color: #7986E7;
color: white;
border: none;
border-radius: 4px;
padding: 8px 12px;
cursor: pointer;
font-size: 14px;
margin-top: 12px;
}
.add-button:hover {
background-color: #6875D6;
}
.list-item {
border: 1px solid #eee;
border-radius: 4px;
padding: 12px;
margin-bottom: 12px;
background-color: #f9f9f9;
}
.list-item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.list-item-title {
font-weight: bold;
color: #555;
font-size: 15px;
}
.list-item-actions {
display: flex;
gap: 8px;
}
.action-icon {
cursor: pointer;
color: #7986E7;
font-size: 18px;
width: 24px;
height: 24px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
.action-icon:hover {
color: #6875D6;
background-color: #f0f0f0;
}
/* 模态对话框样式 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 1000;
align-items: center;
justify-content: center;
}
.modal-content {
background-color: white;
border-radius: 6px;
padding: 24px;
width: 500px;
max-width: 90%;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.modal-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 16px;
color: #333;
}
.modal-form {
margin-bottom: 20px;
}
.form-row {
margin-bottom: 12px;
}
.form-label {
display: block;
margin-bottom: 6px;
font-weight: bold;
color: #555;
}
.form-input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 24px;
}
.modal-button {
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
border: none;
}
.cancel-button {
background-color: #f0f0f0;
color: #333;
}
.submit-button {
background-color: #7986E7;
color: white;
}
.submit-button:hover {
background-color: #6875D6;
}
</style>
<div class="config-container">
<div class="config-header">
<div class="file-selector-header">
<div class="file-selector-label">运行环境配置文件:</div>
<select id="scenarioFile">
<option value="">-- 请选择运行环境配置文件 --</option>
</select>
<button class="refresh-button" id="refreshButton" title="刷新文件列表"></button>
</div>
<div class="action-buttons">
<button class="action-button" id="newConfig">新建</button>
<button class="action-button save-button" id="saveConfig">保存</button>
<button class="action-button" id="saveAsConfig">另存为</button>
</div>
</div>
<div class="config-content">
<div class="file-content" id="fileContent">
<div class="no-file-selected">请选择一个运行环境配置文件查看内容</div>
</div>
</div>
</div>
`;
}
/**
* 更新文件内容显示
* @param {HTMLElement} component - 组件实例
*/
export function updateFileContent(component) {
const contentElement = component.shadowRoot.getElementById('fileContent');
if (!contentElement) {
return;
}
if (!component.xmlContent) {
contentElement.innerHTML = '<div class="no-file-selected">请选择一个运行环境配置文件查看内容</div>';
return;
}
try {
// 尝试解析XML
const parser = new DOMParser();
component.xmlDoc = parser.parseFromString(component.xmlContent, 'text/xml');
// 检查解析错误
const parseError = component.xmlDoc.querySelector('parsererror');
if (parseError) {
contentElement.innerHTML = `<pre>${escapeHtml(component.xmlContent)}</pre>`;
return;
}
// 创建可视化编辑器
contentElement.innerHTML = '';
renderVisualEditor(component, contentElement, component.xmlDoc);
} catch (error) {
contentElement.innerHTML = `<pre>${escapeHtml(component.xmlContent)}</pre>`;
}
}
/**
* 渲染可视化编辑界面
* @param {HTMLElement} component - 组件实例
* @param {HTMLElement} container - 容器元素
* @param {Document} xmlDoc - XML文档
*/
export function renderVisualEditor(component, container, xmlDoc) {
// 清空容器
container.innerHTML = '';
// 创建编辑器容器
const visualEditor = document.createElement('div');
visualEditor.className = 'visual-editor';
// 获取根元素
const rootElement = xmlDoc.documentElement;
// 只处理Scenario根元素
if (rootElement.nodeName === 'Scenario') {
// 创建模态对话框容器
const modalContainer = document.createElement('div');
modalContainer.className = 'modal';
modalContainer.id = 'propertyModal';
container.appendChild(modalContainer);
// 渲染Environment部分
renderEnvironmentSection(component, visualEditor, rootElement);
// 渲染Console输出和日志部分
renderConsoleAndLogSection(component, visualEditor, rootElement);
// 渲染ModelGroup部分
renderModelGroupsSection(component, visualEditor, rootElement);
// 渲染Services部分
renderServicesSection(component, visualEditor, rootElement);
} else {
// 不是Scenario根元素显示错误信息
visualEditor.innerHTML = `<div style="color: red; padding: 16px;">
无法编辑XML文档的根元素不是Scenario。
请确保XML文档的根元素是Scenario。
</div>`;
}
container.appendChild(visualEditor);
// 自动保存配置到XML
autoSaveToXml(component);
}
/**
* 自动保存表单内容到XML
* @param {HTMLElement} component - 组件实例
*/
export function autoSaveToXml(component) {
// 为所有输入框添加change事件
const inputs = component.shadowRoot.querySelectorAll('.property-input');
inputs.forEach(input => {
input.addEventListener('change', () => {
if (component.updateXmlFromVisualEditor) {
component.updateXmlFromVisualEditor();
}
});
});
}