class ModelDevelopment extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.chapters = [];
this.currentModels = [];
this.currentModelVersions = [];
this.currentVersion = null;
this.currentView = 'chapters'; // 'chapters', 'models', 'versions', 'versionForm'
this.currentChapter = null;
this.currentModel = null;
this.isEditMode = false;
// 初始化DOM结构
this.shadowRoot.innerHTML = `
`;
// 开始初始化数据
this.init();
}
async init() {
try {
const response = await fetch('/api/ata-chapters');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
this.chapters = await response.json();
this.renderChapters();
} catch (error) {
console.error('初始化模型开发页面失败:', error);
this.renderError('加载ATA章节失败,请稍后重试。');
}
}
async fetchModels(chapterId) {
try {
const savedSelection = localStorage.getItem('xnsim-selection');
const selection = savedSelection ? JSON.parse(savedSelection) : {};
const plane = selection.plane;
if (!plane) {
throw new Error('请先选择机型!');
}
const response = await fetch(`/api/chapter-models/${chapterId}?planeName=${encodeURIComponent(plane)}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
this.currentModels = await response.json();
this.currentChapter = this.chapters.find(chapter => chapter.ID === chapterId);
this.renderModels();
} catch (error) {
console.error(`获取章节${chapterId}的模型数据失败:`, error);
this.renderError(error.message);
}
}
async fetchModelVersions(className, modelName) {
try {
const savedSelection = localStorage.getItem('xnsim-selection');
const selection = savedSelection ? JSON.parse(savedSelection) : {};
const plane = selection.plane;
const confID = selection.configurationId;
if (!plane) {
throw new Error('请先选择机型!');
}
if (!confID) {
throw new Error('请先选择构型!');
}
const response = await fetch(`/api/model-versions/${encodeURIComponent(className)}?planeName=${encodeURIComponent(plane)}&confID=${encodeURIComponent(confID)}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
this.currentModelVersions = await response.json();
this.currentModel = { className, name: modelName };
this.renderModelVersions();
} catch (error) {
console.error(`获取模型${className}的版本数据失败:`, error);
this.renderError(error.message);
}
}
async saveModelVersion(versionData) {
try {
const response = await fetch('/api/model-versions', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(versionData)
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('保存模型版本失败:', error);
throw error;
}
}
renderChapters() {
this.currentView = 'chapters';
// 重新初始化Shadow DOM,如果是首次初始化的情况
if (!this.shadowRoot.querySelector('.model-development')) {
this.initBasicStructure();
console.log('重新初始化DOM结构');
} else {
// 如果已经有DOM结构,只更新标题
const title = this.shadowRoot.querySelector('.page-title');
if (title) {
title.textContent = 'ATA章节';
}
}
const container = this.shadowRoot.querySelector('.model-dev-container');
if (!container) {
console.error('找不到容器元素');
return;
}
container.innerHTML = '';
if (this.chapters.length === 0) {
const emptyState = document.createElement('div');
emptyState.className = 'empty-state';
emptyState.textContent = '未找到ATA章节数据';
container.appendChild(emptyState);
} else {
// 创建章节容器并添加底部边距
const chaptersGrid = document.createElement('div');
chaptersGrid.className = 'chapters-grid';
chaptersGrid.style.cssText = 'display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 20px; grid-column: 1 / -1; width: 100%; padding-bottom: 40px;';
// 添加从后端获取的章节卡片
this.chapters.forEach((chapter, index) => {
const card = document.createElement('div');
card.className = 'chapter-card';
// 设置样式以确保卡片一致性
card.style.height = '160px';
card.style.display = 'flex';
card.style.flexDirection = 'column';
card.style.overflow = 'hidden';
// 确保最后一行的卡片有足够的下边距
if (index >= this.chapters.length - (this.chapters.length % 3 || 3)) {
card.style.marginBottom = '20px';
}
card.innerHTML = `
${chapter.ID} - ${chapter.Name}
${chapter.Name_CN || '无中文名称'}
`;
card.addEventListener('click', () => this.fetchModels(chapter.ID));
chaptersGrid.appendChild(card);
});
container.appendChild(chaptersGrid);
}
}
renderModels() {
this.currentView = 'models';
// 确保DOM结构存在
if (!this.shadowRoot.querySelector('.model-development')) {
this.initBasicStructure();
console.log('重新初始化DOM结构');
}
// 更新标题为章节信息
const title = this.shadowRoot.querySelector('.page-title');
if (title) {
title.innerHTML = `${this.currentChapter.ID} - ${this.currentChapter.Name} ${this.currentChapter.Name_CN || ''}`;
}
const container = this.shadowRoot.querySelector('.model-dev-container');
if (!container) {
console.error('找不到容器元素');
return;
}
container.innerHTML = '';
// 添加返回按钮
const backButton = document.createElement('div');
backButton.className = 'back-button';
backButton.innerHTML = `← 返回章节列表`;
backButton.addEventListener('click', () => this.renderChapters());
container.appendChild(backButton);
if (this.currentModels.length === 0) {
const emptyState = document.createElement('div');
emptyState.className = 'empty-state';
emptyState.textContent = '该章节下没有模型';
container.appendChild(emptyState);
} else {
// 创建模型卡片容器
const modelsGrid = document.createElement('div');
modelsGrid.className = 'models-grid';
modelsGrid.style.paddingBottom = '40px';
modelsGrid.style.marginBottom = '20px'; /* 添加底部边距,确保滚动条箭头完全可见 */
// 添加从后端获取的模型卡片
this.currentModels.forEach((model, index) => {
const card = document.createElement('div');
card.className = 'model-card';
// 设置样式以确保卡片一致性
card.style.height = '160px';
card.style.display = 'flex';
card.style.flexDirection = 'column';
card.style.overflow = 'hidden';
// 确保最后一行的卡片有足够的下边距
if (index >= this.currentModels.length - (this.currentModels.length % 3 || 3)) {
card.style.marginBottom = '20px';
}
card.innerHTML = `
${model.ModelName}
${model.ModelName_CN || '无中文名称'}
${model.Description || '无描述'}
类名: ${model.ClassName}
`;
card.addEventListener('click', () => this.fetchModelVersions(model.ClassName, model.ModelName));
modelsGrid.appendChild(card);
});
container.appendChild(modelsGrid);
}
}
renderModelVersions() {
this.currentView = 'versions';
// 更新标题为"ATA章节号/模型名称"格式
const title = this.shadowRoot.querySelector('.page-title');
title.textContent = `${this.currentChapter.ID} / ${this.currentModel.name}`;
const container = this.shadowRoot.querySelector('.model-dev-container');
container.innerHTML = '';
// 添加返回按钮
const backButton = document.createElement('div');
backButton.className = 'back-button';
backButton.innerHTML = `← 返回模型列表`;
backButton.addEventListener('click', () => this.fetchModels(this.currentChapter.ID));
container.appendChild(backButton);
// 创建版本卡片容器
const versionsGrid = document.createElement('div');
versionsGrid.className = 'versions-grid';
// 添加新建模型版本卡片(始终是第一个)
const newVersionCard = document.createElement('div');
newVersionCard.className = 'version-card new-version-card';
newVersionCard.style.height = '100%'; // 确保高度与其他卡片一致
newVersionCard.innerHTML = `
新建模型版本
为${this.currentModel.name}创建新的版本
`;
newVersionCard.addEventListener('click', () => this.createNewVersion());
versionsGrid.appendChild(newVersionCard);
if (this.currentModelVersions.length === 0) {
const emptyState = document.createElement('div');
emptyState.className = 'empty-state';
emptyState.textContent = '该模型没有版本信息,点击"新建模型版本"创建第一个版本';
versionsGrid.appendChild(emptyState);
} else {
// 对版本进行排序 - 按版本号从高到低排序
const sortedVersions = [...this.currentModelVersions].sort((a, b) => {
return this.compareVersions(b.Version, a.Version);
});
// 添加从后端获取的版本卡片
sortedVersions.forEach((version, index) => {
const card = document.createElement('div');
card.className = 'version-card';
card.style.height = '100%'; // 设置统一高度
// 确保最后一行的卡片有足够的下边距
if (index >= sortedVersions.length - (sortedVersions.length % 3 || 3)) {
card.style.marginBottom = '20px';
}
card.innerHTML = `
作者: ${version.Author || '未知'}
创建时间: ${version.CreatTime || '未知'}
修改时间: ${version.ChangeTime || '未知'}
${version.Description || '无描述'}
`;
card.addEventListener('click', () => this.showVersionEditor(version));
versionsGrid.appendChild(card);
});
}
// 添加额外的下边距,确保最后一行完全可见
versionsGrid.style.paddingBottom = '40px';
versionsGrid.style.marginBottom = '20px'; /* 添加底部边距,确保滚动条箭头完全可见 */
container.appendChild(versionsGrid);
}
showVersionEditor(versionData) {
this.currentView = 'versionEditor';
// 确保 CmdList 是数组
if (versionData.CmdList) {
try {
// 如果 CmdList 是字符串,尝试解析它
if (typeof versionData.CmdList === 'string') {
versionData.CmdList = JSON.parse(versionData.CmdList);
}
// 确保 CmdList 是数组
if (!Array.isArray(versionData.CmdList)) {
versionData.CmdList = [];
}
} catch (e) {
console.error('解析 CmdList 失败:', e);
versionData.CmdList = [];
}
} else {
versionData.CmdList = [];
}
this.currentVersion = versionData;
this.isEditMode = versionData && versionData.Version ? true : false;
const container = this.shadowRoot.querySelector('.model-dev-container');
if (!container) {
console.error('找不到容器元素');
return;
}
// 更新标题
const title = this.shadowRoot.querySelector('.page-title');
if (title) {
title.textContent = this.isEditMode
? `编辑版本: ${versionData.Name} (v${versionData.Version})`
: `为 ${this.currentModel.name} 创建新版本`;
}
container.innerHTML = '';
// 添加工具栏按钮
const toolbarDiv = document.createElement('div');
toolbarDiv.className = 'editor-toolbar';
toolbarDiv.style.cssText = 'display: flex; justify-content: space-between; margin-bottom: 20px; grid-column: 1 / -1; width: 100%;';
// 添加返回按钮
const backButton = document.createElement('div');
backButton.className = 'back-button';
backButton.innerHTML = `← 返回版本列表`;
backButton.style.cssText = 'margin: 0; cursor: pointer; color: #667eea; font-weight: 500;';
backButton.addEventListener('click', () => {
this.fetchModelVersions(this.currentModel.className, this.currentModel.name);
});
// 添加按钮组
const actionsDiv = document.createElement('div');
actionsDiv.className = 'toolbar-actions';
actionsDiv.style.cssText = 'display: flex; gap: 10px;';
// 创建各种按钮
const buttonConfigs = [
{
text: '刷新',
color: '#38a169',
action: async () => {
try {
// 保存当前版本ID用于刷新后重新定位
const versionId = this.currentVersion.Version;
// 先刷新版本列表
await this.fetchModelVersions(this.currentModel.className, this.currentModel.name);
// 然后找到当前编辑的版本并重新打开
if (versionId) {
const refreshedVersion = this.currentModelVersions.find(v => v.Version === versionId);
if (refreshedVersion) {
this.showVersionEditor(refreshedVersion);
} else {
alert('未能找到当前版本,可能已被删除');
// 如果找不到当前版本,回到版本列表
this.renderModelVersions();
}
}
} catch (error) {
console.error('刷新版本数据失败:', error);
alert(`刷新失败: ${error.message}`);
}
}
},
{
text: '保存',
color: '#3182ce',
action: () => {
try {
// 获取表单元素
const form = this.shadowRoot.querySelector('#versionEditorForm');
if (form) {
// 触发提交事件
form.dispatchEvent(new Event('submit'));
} else {
console.error('找不到表单元素');
alert('保存失败: 找不到表单元素');
}
} catch (error) {
console.error('保存时发生错误:', error);
alert(`保存失败: ${error.message}`);
}
}
},
{
text: '数据包模型上传',
color: '#dd6b20',
action: () => this.uploadDataPackage()
},
{
text: '模板代码生成',
color: '#805ad5',
action: () => this.generateTemplateCode()
},
{
text: '模板代码下载',
color: '#3182ce',
action: () => this.downloadTemplateCode()
},
{
text: '封装代码上传',
color: '#38a169',
action: () => this.uploadWrapperCode()
},
{
text: '模型编译发布',
color: '#e53e3e',
action: () => this.compileAndPublishModel()
}
];
buttonConfigs.forEach(config => {
const button = document.createElement('button');
button.className = 'toolbar-button';
button.textContent = config.text;
button.style.cssText = `background-color: ${config.color}; color: white; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer; font-weight: 500;`;
button.addEventListener('click', config.action);
actionsDiv.appendChild(button);
});
toolbarDiv.appendChild(backButton);
toolbarDiv.appendChild(actionsDiv);
container.appendChild(toolbarDiv);
// 创建表单容器 - 使用网格布局
const formContainer = document.createElement('div');
formContainer.className = 'form-container';
formContainer.style.cssText = 'display: grid; grid-template-columns: 1fr 1fr; gap: 20px; grid-column: 1 / -1; width: 100%;';
// 创建表单
const form = document.createElement('form');
form.className = 'version-form';
form.id = 'versionEditorForm';
form.style.cssText = 'background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); padding: 20px; grid-column: 1 / -1;';
// 创建两列布局的基本信息部分
const basicInfoSection = document.createElement('div');
basicInfoSection.className = 'form-section basic-info';
basicInfoSection.style.cssText = 'display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;';
// 左列 - 基本信息
const leftColumn = document.createElement('div');
leftColumn.className = 'form-column';
leftColumn.innerHTML = `
`;
// 右列 - 描述和其他信息
const rightColumn = document.createElement('div');
rightColumn.className = 'form-column';
rightColumn.innerHTML = `
`;
basicInfoSection.appendChild(leftColumn);
basicInfoSection.appendChild(rightColumn);
// 重新设计的高级设置部分 - 使用网格布局,去掉标题
const advancedSection = document.createElement('div');
advancedSection.className = 'form-section advanced';
advancedSection.style.cssText = 'border-top: 1px solid #e2e8f0; padding-top: 20px; margin-top: 10px;';
// 创建网格容器
const gridLayout = document.createElement('div');
gridLayout.style.cssText = 'display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px;';
// 添加左列输入框
const leftAdvancedColumn = document.createElement('div');
leftAdvancedColumn.innerHTML = `
`;
// 添加右列输入框
const rightAdvancedColumn = document.createElement('div');
rightAdvancedColumn.innerHTML = `
`;
// 第三行 - 添加剩余输入框
const bottomRow = document.createElement('div');
bottomRow.style.cssText = 'grid-column: span 2; display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px;';
const bottomLeftColumn = document.createElement('div');
bottomLeftColumn.innerHTML = `
`;
const bottomRightColumn = document.createElement('div');
bottomRightColumn.innerHTML = `
`;
bottomRow.appendChild(bottomLeftColumn);
bottomRow.appendChild(bottomRightColumn);
// 装配高级设置部分
gridLayout.appendChild(leftAdvancedColumn);
gridLayout.appendChild(rightAdvancedColumn);
advancedSection.appendChild(gridLayout);
advancedSection.appendChild(bottomRow);
// 添加指令列表参数表格
const cmdListSection = document.createElement('div');
cmdListSection.className = 'form-section cmd-list';
cmdListSection.style.cssText = 'border-top: 1px solid #e2e8f0; padding-top: 20px; margin-top: 20px;';
// 添加结构体参数部分
const structSection = document.createElement('div');
structSection.className = 'form-section struct-params';
structSection.style.cssText = 'border-top: 1px solid #e2e8f0; padding-top: 20px; margin-top: 20px;';
// 创建结构体参数标题
const structHeader = document.createElement('div');
structHeader.style.cssText = 'margin-bottom: 15px;';
structHeader.innerHTML = '';
// 创建结构体参数网格布局
const structGrid = document.createElement('div');
structGrid.style.cssText = 'display: grid; grid-template-columns: auto repeat(3, 1fr); gap: 20px;';
// 添加六个下拉框,按类型分组
structGrid.innerHTML = `
`;
structSection.appendChild(structHeader);
structSection.appendChild(structGrid);
// 创建表格标题和工具栏
const cmdListHeader = document.createElement('div');
cmdListHeader.style.cssText = 'display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;';
cmdListHeader.innerHTML = `
`;
// 创建表格
const cmdListTable = document.createElement('table');
cmdListTable.style.cssText = 'width: 100%; border-collapse: collapse; margin-top: 10px;';
cmdListTable.innerHTML = `
名称 |
描述 |
调用函数 |
操作 |
${this.renderCmdListRows()}
`;
cmdListSection.appendChild(cmdListHeader);
cmdListSection.appendChild(cmdListTable);
// 表单操作按钮
const formActions = document.createElement('div');
formActions.className = 'form-actions';
formActions.style.cssText = 'margin-top: 30px; text-align: right; border-top: 1px solid #e2e8f0; padding-top: 20px;';
// 组装表单
form.appendChild(basicInfoSection);
form.appendChild(advancedSection);
form.appendChild(structSection);
form.appendChild(cmdListSection);
form.appendChild(formActions);
formContainer.appendChild(form);
container.appendChild(formContainer);
// 为添加指令按钮添加事件监听器
setTimeout(() => {
const addCmdBtn = this.shadowRoot.querySelector('#addCmdBtn');
if (addCmdBtn) {
addCmdBtn.addEventListener('click', () => {
this.addCmdListRow();
});
}
// 为删除按钮添加事件监听器
const deleteButtons = this.shadowRoot.querySelectorAll('.delete-cmd-btn');
deleteButtons.forEach(button => {
button.addEventListener('click', (e) => {
const index = parseInt(e.target.dataset.index);
this.deleteCmdListRow(index);
});
});
}, 0);
// 为日期时间按钮添加事件监听器
setTimeout(() => {
const dateTimePickerButton = this.shadowRoot.querySelector('#dateTimePickerButton');
const changeTimeInput = this.shadowRoot.querySelector('#changeTime');
if (dateTimePickerButton && changeTimeInput) {
dateTimePickerButton.addEventListener('click', () => {
this.showDateTimeDialog(changeTimeInput);
});
}
// 动态生成运行节点选项
this.updateRunNodeOptions();
// 当运行频率组变化时,更新运行节点选项
const runFreqGroup = this.shadowRoot.querySelector('#runFreqGroup');
if (runFreqGroup) {
runFreqGroup.addEventListener('change', () => {
this.updateRunNodeOptions();
});
}
// 填充数据库中结构体下拉框
this.populateDatabaseStructDropdowns();
}, 0);
// 添加表单提交事件
form.addEventListener('submit', async (e) => {
e.preventDefault();
try {
const savedSelection = localStorage.getItem('xnsim-selection');
const selection = savedSelection ? JSON.parse(savedSelection) : {};
// 获取工具栏中的保存按钮
const saveButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(2)');
if (saveButton) {
const originalButtonText = saveButton.textContent;
saveButton.textContent = '保存中...';
saveButton.disabled = true;
}
// 验证版本号格式
const versionInput = form.querySelector('#version');
if (versionInput && !/^[0-9.]+$/.test(versionInput.value)) {
throw new Error('版本号只能包含数字和小数点');
}
// 验证优先级范围
const priorityInput = form.querySelector('#priority');
if (priorityInput) {
const priority = parseInt(priorityInput.value);
if (isNaN(priority) || priority < 0 || priority > 99) {
throw new Error('优先级必须是0-99之间的整数');
}
}
// 验证结构体字段的JSON格式
const inputStructInput = form.querySelector('#inputStruct');
if (inputStructInput && inputStructInput.value.trim() !== '') {
try {
JSON.parse(inputStructInput.value);
} catch (e) {
throw new Error('InputStruct 必须是有效的 JSON 格式');
}
}
const outputStructInput = form.querySelector('#outputStruct');
if (outputStructInput && outputStructInput.value.trim() !== '') {
try {
JSON.parse(outputStructInput.value);
} catch (e) {
throw new Error('OutputStruct 必须是有效的 JSON 格式');
}
}
const heartStructInput = form.querySelector('#heartStruct');
if (heartStructInput && heartStructInput.value.trim() !== '') {
try {
JSON.parse(heartStructInput.value);
} catch (e) {
throw new Error('HeartStruct 必须是有效的 JSON 格式');
}
}
// 收集指令列表数据
const cmdList = [];
const cmdRows = this.shadowRoot.querySelectorAll('#cmdListBody tr');
cmdRows.forEach(row => {
const nameInput = row.querySelector('input[name^="cmdList"][name$=".Name"]');
const descriptionInput = row.querySelector('input[name^="cmdList"][name$=".Description"]');
const callInput = row.querySelector('input[name^="cmdList"][name$=".Call"]');
if (nameInput && descriptionInput && callInput) {
// 确保每个字段都有值,如果没有则使用空字符串
const cmd = {
"Name" : nameInput.value.trim() || "",
"Description" : descriptionInput.value.trim() || "",
"Call" : callInput.value.trim() || ""
};
cmdList.push(cmd);
}
});
// 构建结构体映射JSON字符串
const buildStructMapping = (select1, select2) => {
const selectedOption1 = select1.selectedOptions[0];
const value2 = select2.value;
if (selectedOption1 && selectedOption1.dataset.fullname && value2) {
return JSON.stringify({ [selectedOption1.dataset.fullname]: value2 });
}
return '';
};
// 构建版本数据
const versionData = {
PlaneName: selection.plane,
ConfID: selection.configurationId,
ClassName: form.querySelector('#className').value,
Name: form.querySelector('#name').value,
Version: form.querySelector('#version').value,
Author: form.querySelector('#author').value,
Description: form.querySelector('#description').value,
ChangeTime: form.querySelector('#changeTime').value,
RunFreqGroup: form.querySelector('#runFreqGroup').value,
RunNode: form.querySelector('#runNode').value,
Priority: form.querySelector('#priority').value,
DataPackagePath: form.querySelector('#dataPackagePath').value,
DataPackageName: form.querySelector('#dataPackageName').value,
DataPackageHeaderName: form.querySelector('#dataPackageHeaderName').value,
DataPackageEntryPoint: form.querySelector('#dataPackageEntryPoint').value,
DataPackageInterfaceName: form.querySelector('#dataPackageInterfaceName').value,
InputStruct: buildStructMapping(
form.querySelector('#inputStructMapping1'),
form.querySelector('#inputStructMapping2')
),
OutputStruct: buildStructMapping(
form.querySelector('#outputStructMapping1'),
form.querySelector('#outputStructMapping2')
),
HeartStruct: buildStructMapping(
form.querySelector('#heartStructMapping1'),
form.querySelector('#heartStructMapping2')
),
CmdList: JSON.stringify(cmdList),
isUpdate: this.isEditMode,
originalVersion: this.isEditMode ? this.currentVersion.Version : null
};
// 验证必填字段
const requiredFields = ['PlaneName', 'ConfID', 'ClassName', 'Name', 'Version', 'Author', 'ChangeTime'];
for (const field of requiredFields) {
if (!versionData[field]) {
throw new Error(`${field}是必填字段`);
}
}
// 保存版本数据
const result = await this.saveModelVersion(versionData);
// 显示保存成功提示
alert(this.isEditMode ? '版本更新成功!' : '新版本创建成功!');
// 刷新版本列表
this.fetchModelVersions(this.currentModel.className, this.currentModel.name);
} catch (error) {
alert(`保存失败: ${error.message}`);
} finally {
// 恢复按钮状态
const saveButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(2)');
if (saveButton) {
saveButton.textContent = this.isEditMode ? '保存修改' : '创建版本';
saveButton.disabled = false;
}
}
});
}
/**
* 更新运行节点选项
*/
updateRunNodeOptions() {
const runFreqGroupSelect = this.shadowRoot.querySelector('#runFreqGroup');
const runNodeSelect = this.shadowRoot.querySelector('#runNode');
if (!runFreqGroupSelect || !runNodeSelect) return;
const freqGroup = parseInt(runFreqGroupSelect.value) || 0;
const maxNode = Math.pow(2, freqGroup); // 2的运行频率组次方
// 清空现有选项
runNodeSelect.innerHTML = '';
// 添加新选项
for (let i = 0; i < maxNode; i++) {
const option = document.createElement('option');
option.value = i.toString();
option.textContent = i.toString();
if (this.currentVersion.RunNode === i.toString()) {
option.selected = true;
}
runNodeSelect.appendChild(option);
}
}
/**
* 显示日期时间选择对话框
* @param {HTMLInputElement} inputElement - 输入元素
*/
showDateTimeDialog(inputElement) {
// 获取当前系统时间
const now = new Date();
// 解析当前日期和时间
let currentDate = now;
let currentTime = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`;
// 如果输入框有值,尝试解析
if (inputElement.value && inputElement.value.trim() !== '') {
try {
const parts = inputElement.value.split(' ');
if (parts.length >= 1) {
const dateParts = parts[0].split('-');
if (dateParts.length === 3) {
const year = parseInt(dateParts[0]);
const month = parseInt(dateParts[1]) - 1; // 月份从0开始
const day = parseInt(dateParts[2]);
const parsedDate = new Date(year, month, day);
// 验证日期是否有效
if (!isNaN(parsedDate.getTime())) {
currentDate = parsedDate;
}
}
}
if (parts.length >= 2) {
// 验证时间格式
const timeRegex = /^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$/;
if (timeRegex.test(parts[1])) {
currentTime = parts[1];
}
}
} catch (error) {
console.error('解析日期时间失败:', error);
// 使用系统当前时间(已在初始化时设置)
}
}
// 创建模态框
const modal = document.createElement('div');
modal.className = 'modal';
modal.id = 'dateTimeModal';
modal.style.cssText = 'position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.4); display: flex; justify-content: center; align-items: center;';
// 获取年、月、日
const year = currentDate.getFullYear();
const month = currentDate.getMonth(); // 0-11
const day = currentDate.getDate();
// 生成日历
const daysInMonth = new Date(year, month + 1, 0).getDate();
const firstDay = new Date(year, month, 1).getDay(); // 0-6,0表示周日
// 生成月份选项
let monthOptions = '';
const monthNames = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'];
monthNames.forEach((name, idx) => {
monthOptions += ``;
});
// 生成年份选项
let yearOptions = '';
const currentYear = new Date().getFullYear();
for (let y = currentYear - 10; y <= currentYear + 10; y++) {
yearOptions += ``;
}
// 生成日历表格
let calendarRows = '';
let dayCount = 1;
// 添加表头
calendarRows += '';
['日', '一', '二', '三', '四', '五', '六'].forEach(dayName => {
calendarRows += `${dayName} | `;
});
calendarRows += '
';
// 计算行数
const totalCells = firstDay + daysInMonth;
const rowCount = Math.ceil(totalCells / 7);
// 添加日期行
for (let i = 0; i < rowCount; i++) {
calendarRows += '';
for (let j = 0; j < 7; j++) {
if ((i === 0 && j < firstDay) || dayCount > daysInMonth) {
calendarRows += ' | ';
} else {
const isToday = dayCount === day;
calendarRows += `${dayCount} | `;
dayCount++;
}
}
calendarRows += '
';
}
// 解析当前时间为时、分、秒
let hours = '00', minutes = '00', seconds = '00';
if (currentTime) {
const timeParts = currentTime.split(':');
if (timeParts.length >= 1) hours = timeParts[0].padStart(2, '0');
if (timeParts.length >= 2) minutes = timeParts[1].padStart(2, '0');
if (timeParts.length >= 3) seconds = timeParts[2].padStart(2, '0');
}
modal.innerHTML = `
`;
// 添加样式
const style = document.createElement('style');
style.textContent = `
.calendar-day {
cursor: pointer;
padding: 8px;
text-align: center;
border: 1px solid #e2e8f0;
}
.calendar-day:hover {
background-color: #ebf4ff;
}
.calendar-day.selected {
background-color: #4c6ef5;
color: white;
}
.calendar-table th {
padding: 8px;
text-align: center;
border: 1px solid #e2e8f0;
color: #4a5568;
}
`;
// 获取shadowRoot
const shadowRoot = this.shadowRoot;
// 添加到DOM
shadowRoot.appendChild(style);
shadowRoot.appendChild(modal);
// 事件处理
const closeBtn = modal.querySelector('.close');
const cancelBtn = modal.querySelector('#cancelDateTime');
const confirmBtn = modal.querySelector('#confirmDateTime');
const calendarMonth = modal.querySelector('#calendarMonth');
const calendarYear = modal.querySelector('#calendarYear');
const calendarDays = modal.querySelectorAll('.calendar-day');
const hourInput = modal.querySelector('#hourInput');
const minuteInput = modal.querySelector('#minuteInput');
const secondInput = modal.querySelector('#secondInput');
// 验证时间输入
const validateTimeInput = (input, min, max) => {
let value = parseInt(input.value);
if (isNaN(value)) value = 0;
if (value < min) value = min;
if (value > max) value = max;
input.value = value.toString().padStart(2, '0');
};
// 添加输入验证
hourInput.addEventListener('change', () => validateTimeInput(hourInput, 0, 23));
minuteInput.addEventListener('change', () => validateTimeInput(minuteInput, 0, 59));
secondInput.addEventListener('change', () => validateTimeInput(secondInput, 0, 59));
// 选择日期事件
calendarDays.forEach(cell => {
cell.addEventListener('click', (e) => {
// 移除所有选中状态
calendarDays.forEach(day => day.classList.remove('selected'));
// 添加新选中状态
e.target.classList.add('selected');
});
});
// 月份和年份变化时重新渲染日历
const updateCalendar = () => {
const selectedYear = parseInt(calendarYear.value);
const selectedMonth = parseInt(calendarMonth.value);
// 更新当前日期
currentDate = new Date(selectedYear, selectedMonth, 1);
// 重新生成日历内容
const daysInMonth = new Date(selectedYear, selectedMonth + 1, 0).getDate();
const firstDay = new Date(selectedYear, selectedMonth, 1).getDay();
let calendarRows = '';
let dayCount = 1;
// 添加表头
calendarRows += '';
['日', '一', '二', '三', '四', '五', '六'].forEach(dayName => {
calendarRows += `${dayName} | `;
});
calendarRows += '
';
// 计算行数
const totalCells = firstDay + daysInMonth;
const rowCount = Math.ceil(totalCells / 7);
// 添加日期行
for (let i = 0; i < rowCount; i++) {
calendarRows += '';
for (let j = 0; j < 7; j++) {
if ((i === 0 && j < firstDay) || dayCount > daysInMonth) {
calendarRows += ' | ';
} else {
const isToday = dayCount === currentDate.getDate();
calendarRows += `${dayCount} | `;
dayCount++;
}
}
calendarRows += '
';
}
// 更新日历表格内容
const calendarTable = modal.querySelector('.calendar-table');
calendarTable.innerHTML = calendarRows;
// 重新绑定日期选择事件
const calendarDays = modal.querySelectorAll('.calendar-day');
calendarDays.forEach(cell => {
cell.addEventListener('click', (e) => {
// 移除所有选中状态
calendarDays.forEach(day => day.classList.remove('selected'));
// 添加新选中状态
e.target.classList.add('selected');
});
});
};
calendarMonth.addEventListener('change', updateCalendar);
calendarYear.addEventListener('change', updateCalendar);
const closeModal = () => {
if (modal.parentNode === shadowRoot) {
shadowRoot.removeChild(modal);
}
if (style.parentNode === shadowRoot) {
shadowRoot.removeChild(style);
}
};
closeBtn.addEventListener('click', closeModal);
cancelBtn.addEventListener('click', closeModal);
confirmBtn.addEventListener('click', () => {
// 获取选中的日期
const selectedDay = modal.querySelector('.calendar-day.selected');
if (!selectedDay) {
closeModal();
return;
}
const day = selectedDay.dataset.day;
const month = parseInt(calendarMonth.value) + 1; // 月份从0开始,显示时+1
const year = calendarYear.value;
// 获取时间值并确保两位数
const hours = hourInput.value.padStart(2, '0');
const minutes = minuteInput.value.padStart(2, '0');
const seconds = secondInput.value.padStart(2, '0');
// 格式化日期
const formattedDate = `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`;
// 格式化时间
const formattedTime = `${hours}:${minutes}:${seconds}`;
// 更新输入框值为标准格式
inputElement.value = `${formattedDate} ${formattedTime}`;
closeModal();
});
}
// 比较版本号的方法
compareVersions(versionA, versionB) {
if (!versionA) return -1;
if (!versionB) return 1;
const partsA = versionA.toString().split('.').map(Number);
const partsB = versionB.toString().split('.').map(Number);
// 确保两个数组长度相同,以便比较
const maxLength = Math.max(partsA.length, partsB.length);
for (let i = 0; i < maxLength; i++) {
// 处理长度不同的情况,用0填充
const partA = i < partsA.length ? partsA[i] : 0;
const partB = i < partsB.length ? partsB[i] : 0;
if (partA > partB) return 1;
if (partA < partB) return -1;
}
return 0; // 版本号相同
}
renderError(message) {
// 确保DOM结构存在
if (!this.shadowRoot.querySelector('.model-development')) {
this.initBasicStructure();
console.log('重新初始化DOM结构');
}
const container = this.shadowRoot.querySelector('.model-dev-container');
if (!container) {
console.error('找不到容器元素');
return;
}
container.innerHTML = `
`;
const retryBtn = this.shadowRoot.querySelector('.retry-btn');
if (retryBtn) {
retryBtn.addEventListener('click', () => {
if (this.currentView === 'chapters') {
this.init();
} else if (this.currentView === 'models' && this.currentChapter) {
this.fetchModels(this.currentChapter.ID);
} else if (this.currentView === 'versions' && this.currentModel) {
this.fetchModelVersions(this.currentModel.className, this.currentModel.name);
} else {
this.init();
}
});
}
}
// 初始化基本DOM结构的辅助方法
initBasicStructure() {
this.shadowRoot.innerHTML = `
`;
}
// 组件被重新激活时调用
reactivate() {
if (this.currentView === 'chapters') {
this.init();
} else if (this.currentView === 'models' && this.currentChapter) {
this.fetchModels(this.currentChapter.ID);
} else if (this.currentView === 'versions' && this.currentModel) {
this.fetchModelVersions(this.currentModel.className, this.currentModel.name);
} else if (this.currentView === 'versionEditor' && this.currentVersion) {
this.showVersionEditor(this.currentVersion);
} else {
this.init();
}
}
// 创建新版本的方法
createNewVersion() {
// 创建一个新的版本对象,设置默认值
const newVersion = {
ClassName: this.currentModel.className,
Name: this.currentModel.name,
ModelName: this.currentModel.name,
Version: this.getNextVersionNumber(),
Author: '',
Description: '',
CreatTime: this.getCurrentDateTime(),
ChangeTime: this.getCurrentDateTime(),
RunFreqGroup: '0',
RunNode: '0',
Priority: '0',
DataPackagePath: '',
DataPackageName: '',
DataPackageHeaderName: '',
DataPackageEntryPoint: '',
DataPackageInterfaceName: '',
InputStruct: '',
OutputStruct: '',
HeartStruct: '',
CmdList: [] // 初始化为空数组
};
// 打开编辑界面
this.showVersionEditor(newVersion);
}
// 获取下一个版本号
getNextVersionNumber() {
if (!this.currentModelVersions || this.currentModelVersions.length === 0) {
return "1.0.0.0";
}
// 找出最高版本号
let highestVersion = null;
this.currentModelVersions.forEach(version => {
if (!version.Version) return;
if (!highestVersion || this.compareVersions(version.Version, highestVersion) > 0) {
highestVersion = version.Version;
}
});
if (!highestVersion) return "1.0.0.0";
// 解析版本号
const versionParts = highestVersion.toString().split('.').map(Number);
// 确保版本号有4位
while (versionParts.length < 4) {
versionParts.push(0);
}
// 增加最后一位
versionParts[versionParts.length - 1]++;
// 处理进位
for (let i = versionParts.length - 1; i > 0; i--) {
if (versionParts[i] > 9) {
versionParts[i] = 0;
versionParts[i - 1]++;
}
}
return versionParts.join('.');
}
// 获取当前日期时间格式化字符串
getCurrentDateTime() {
const now = new Date();
// 格式化日期:YYYY-MM-DD
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
const date = `${year}-${month}-${day}`;
// 格式化时间:HH:MM:SS
const hours = String(now.getHours()).padStart(2, '0');
const minutes = String(now.getMinutes()).padStart(2, '0');
const seconds = String(now.getSeconds()).padStart(2, '0');
const time = `${hours}:${minutes}:${seconds}`;
return `${date} ${time}`;
}
/**
* 渲染指令列表行
* @returns {string} 表格行的HTML字符串
*/
renderCmdListRows() {
if (!this.currentVersion.CmdList || !Array.isArray(this.currentVersion.CmdList) || this.currentVersion.CmdList.length === 0) {
return '暂无指令参数 |
';
}
return this.currentVersion.CmdList.map((cmd, index) => `
|
|
|
|
`).join('');
}
/**
* 添加新的指令行
*/
addCmdListRow() {
if (!this.currentVersion.CmdList) {
this.currentVersion.CmdList = [];
}
this.currentVersion.CmdList.push({
Name: '',
Description: '',
Call: ''
});
// 更新表格内容
const tbody = this.shadowRoot.querySelector('#cmdListBody');
if (tbody) {
// 如果当前显示的是"暂无指令参数"的提示,则清空表格
if (tbody.children.length === 1 && tbody.children[0].children.length === 1) {
tbody.innerHTML = '';
}
// 创建新的表格行
const newRow = document.createElement('tr');
// 创建指令名称输入框
const nameCell = document.createElement('td');
nameCell.style.cssText = 'padding: 8px; border: 1px solid #e2e8f0;';
const nameInput = document.createElement('input');
nameInput.type = 'text';
nameInput.className = 'cmd-name';
nameInput.style.cssText = 'width: 100%; border: none; padding: 4px;';
nameCell.appendChild(nameInput);
// 创建描述输入框
const descCell = document.createElement('td');
descCell.style.cssText = 'padding: 8px; border: 1px solid #e2e8f0;';
const descInput = document.createElement('input');
descInput.type = 'text';
descInput.className = 'cmd-description';
descInput.style.cssText = 'width: 100%; border: none; padding: 4px;';
descCell.appendChild(descInput);
// 创建调用函数输入框
const callCell = document.createElement('td');
callCell.style.cssText = 'padding: 8px; border: 1px solid #e2e8f0;';
const callInput = document.createElement('input');
callInput.type = 'text';
callInput.className = 'cmd-call';
callInput.style.cssText = 'width: 100%; border: none; padding: 4px;';
callCell.appendChild(callInput);
// 创建删除按钮
const actionCell = document.createElement('td');
actionCell.style.cssText = 'padding: 8px; border: 1px solid #e2e8f0; text-align: center;';
const deleteButton = document.createElement('button');
deleteButton.type = 'button';
deleteButton.className = 'delete-cmd';
deleteButton.dataset.index = (this.currentVersion.CmdList.length - 1).toString();
deleteButton.style.cssText = 'background-color: #e53e3e; color: white; border: none; border-radius: 4px; padding: 4px 8px; cursor: pointer;';
deleteButton.textContent = '删除';
deleteButton.addEventListener('click', (e) => {
const index = parseInt(e.target.dataset.index);
this.deleteCmdListRow(index);
});
actionCell.appendChild(deleteButton);
// 组装行
newRow.appendChild(nameCell);
newRow.appendChild(descCell);
newRow.appendChild(callCell);
newRow.appendChild(actionCell);
// 添加到表格
tbody.appendChild(newRow);
}
}
/**
* 删除指令行
* @param {number} index - 要删除的行索引
*/
deleteCmdListRow(index) {
if (this.currentVersion.CmdList && Array.isArray(this.currentVersion.CmdList)) {
this.currentVersion.CmdList.splice(index, 1);
// 更新表格内容
const tbody = this.shadowRoot.querySelector('#cmdListBody');
if (tbody) {
if (this.currentVersion.CmdList.length === 0) {
// 如果没有指令了,显示提示信息
tbody.innerHTML = '暂无指令参数 |
';
} else {
// 重新渲染所有行
tbody.innerHTML = this.renderCmdListRows();
// 重新绑定删除按钮事件
const deleteButtons = this.shadowRoot.querySelectorAll('.delete-cmd-btn');
deleteButtons.forEach(button => {
button.addEventListener('click', (e) => {
const index = parseInt(e.target.dataset.index);
this.deleteCmdListRow(index);
});
});
}
}
}
}
/**
* 填充数据库中结构体下拉框
*/
async populateDatabaseStructDropdowns() {
try {
const savedSelection = localStorage.getItem('xnsim-selection');
const selection = savedSelection ? JSON.parse(savedSelection) : {};
if (!selection.configurationId) {
console.warn('未找到构型ID,无法获取结构体列表');
return;
}
const structResponse = await fetch(`/api/interface/list?confID=${selection.configurationId}`);
if (structResponse.ok) {
const structData = await structResponse.json();
// 以ModelStructName为key去重,只保留第一个全称
const uniqueMap = {};
structData.forEach(item => {
if (!uniqueMap[item.ModelStructName]) {
uniqueMap[item.ModelStructName] = `${item.SystemName}::${item.PlaneName}::${item.ATAName}::${item.ModelStructName}`;
}
});
const structNames = Object.keys(uniqueMap);
// 获取下拉框元素
const inputStructMapping1 = this.shadowRoot.querySelector('#inputStructMapping1');
const outputStructMapping1 = this.shadowRoot.querySelector('#outputStructMapping1');
const heartStructMapping1 = this.shadowRoot.querySelector('#heartStructMapping1');
// 填充数据库中结构体下拉框
if (inputStructMapping1) {
inputStructMapping1.innerHTML = '' +
structNames.map(name => ``).join('');
}
if (outputStructMapping1) {
outputStructMapping1.innerHTML = '' +
structNames.map(name => ``).join('');
}
if (heartStructMapping1) {
heartStructMapping1.innerHTML = '' +
structNames.map(name => ``).join('');
}
// 解析数据库中存储的JSON字符串并自动选择正确的选项
this.selectStructMappingOptions();
// 检查数据包相关字段是否有值,如果有则获取结构体成员信息
const dataPackagePathInput = this.shadowRoot.querySelector('#dataPackagePath');
const dataPackageHeaderNameInput = this.shadowRoot.querySelector('#dataPackageHeaderName');
const dataPackageInterfaceNameInput = this.shadowRoot.querySelector('#dataPackageInterfaceName');
if (dataPackagePathInput && dataPackageHeaderNameInput && dataPackageInterfaceNameInput) {
const packagePath = dataPackagePathInput.value.trim();
const hearderName = dataPackageHeaderNameInput.value.trim();
const interfaceName = dataPackageInterfaceNameInput.value.trim();
if (packagePath && hearderName && interfaceName) {
const headerFilePath = packagePath + '/' + hearderName;
try {
const memberResponse = await fetch('/api/filesystem/get-struct-members', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
confName: selection.configurationName,
headerFilePath: headerFilePath,
structName: interfaceName
})
});
if (memberResponse.ok) {
const memberData = await memberResponse.json();
if (memberData.success && memberData.memberNames) {
// 获取头文件结构体下拉框
const inputStructMapping2 = this.shadowRoot.querySelector('#inputStructMapping2');
const outputStructMapping2 = this.shadowRoot.querySelector('#outputStructMapping2');
const heartStructMapping2 = this.shadowRoot.querySelector('#heartStructMapping2');
// 填充头文件中结构体下拉框
if (inputStructMapping2 && outputStructMapping2 && heartStructMapping2) {
const memberOptions = '' +
memberData.memberNames.map(member => ``).join('');
inputStructMapping2.innerHTML = memberOptions;
outputStructMapping2.innerHTML = memberOptions;
heartStructMapping2.innerHTML = memberOptions;
// 在填充头文件结构体下拉框后,再次解析JSON并选择选项
this.selectStructMappingOptions();
}
}
} else {
console.warn('获取结构体成员信息失败:', memberResponse.status);
}
} catch (error) {
console.warn('获取结构体成员信息失败:', error);
}
}
}
} else {
console.warn('获取接口结构体列表失败:', structResponse.status);
}
} catch (error) {
console.warn('填充数据库结构体下拉框失败:', error);
}
}
/**
* 解析数据库中存储的JSON字符串并自动选择正确的选项
*/
selectStructMappingOptions() {
if (!this.currentVersion) return;
// 解析InputStruct
if (this.currentVersion.InputStruct) {
try {
const inputStructData = JSON.parse(this.currentVersion.InputStruct);
const inputStructKey = Object.keys(inputStructData)[0]; // 获取JSON对象的key
const inputStructValue = inputStructData[inputStructKey]; // 获取JSON对象的value
// 选择数据库结构体下拉框
const inputStructMapping1 = this.shadowRoot.querySelector('#inputStructMapping1');
if (inputStructMapping1) {
// 从全称中提取结构体名
const structName = inputStructKey.split('::').pop();
inputStructMapping1.value = structName;
}
// 选择头文件结构体下拉框
const inputStructMapping2 = this.shadowRoot.querySelector('#inputStructMapping2');
if (inputStructMapping2) {
inputStructMapping2.value = inputStructValue;
}
} catch (error) {
console.warn('解析InputStruct失败:', error);
}
}
// 解析OutputStruct
if (this.currentVersion.OutputStruct) {
try {
const outputStructData = JSON.parse(this.currentVersion.OutputStruct);
const outputStructKey = Object.keys(outputStructData)[0];
const outputStructValue = outputStructData[outputStructKey];
// 选择数据库结构体下拉框
const outputStructMapping1 = this.shadowRoot.querySelector('#outputStructMapping1');
if (outputStructMapping1) {
const structName = outputStructKey.split('::').pop();
outputStructMapping1.value = structName;
}
// 选择头文件结构体下拉框
const outputStructMapping2 = this.shadowRoot.querySelector('#outputStructMapping2');
if (outputStructMapping2) {
outputStructMapping2.value = outputStructValue;
}
} catch (error) {
console.warn('解析OutputStruct失败:', error);
}
}
// 解析HeartStruct
if (this.currentVersion.HeartStruct) {
try {
const heartStructData = JSON.parse(this.currentVersion.HeartStruct);
const heartStructKey = Object.keys(heartStructData)[0];
const heartStructValue = heartStructData[heartStructKey];
// 选择数据库结构体下拉框
const heartStructMapping1 = this.shadowRoot.querySelector('#heartStructMapping1');
if (heartStructMapping1) {
const structName = heartStructKey.split('::').pop();
heartStructMapping1.value = structName;
}
// 选择头文件结构体下拉框
const heartStructMapping2 = this.shadowRoot.querySelector('#heartStructMapping2');
if (heartStructMapping2) {
heartStructMapping2.value = heartStructValue;
}
} catch (error) {
console.warn('解析HeartStruct失败:', error);
}
}
}
/**
* 上传数据包模型
*/
async uploadDataPackage() {
try {
// 获取当前选择的构型信息
const savedSelection = localStorage.getItem('xnsim-selection');
const selection = savedSelection ? JSON.parse(savedSelection) : {};
if (!selection.configurationName) {
alert('请先选择构型!');
return;
}
// 创建文件输入元素
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.webkitdirectory = true;
fileInput.directory = true;
fileInput.multiple = true;
fileInput.style.display = 'none';
// 添加到DOM
document.body.appendChild(fileInput);
// 监听文件选择
fileInput.addEventListener('change', async (event) => {
try {
const files = Array.from(event.target.files);
if (files.length === 0) {
alert('请选择文件夹!');
return;
}
// 从第一个文件的webkitRelativePath中获取文件夹名称
let folderName = null;
if (files.length > 0 && files[0].webkitRelativePath) {
const pathParts = files[0].webkitRelativePath.split('/');
if (pathParts.length > 1) {
folderName = pathParts[0];
}
}
if (!folderName) {
alert('无法获取文件夹名称,请重新选择文件夹!');
return;
}
// 验证文件夹内容
const headerFiles = files.filter(file => file.name.toLowerCase().endsWith('.h'));
const libraryFiles = files.filter(file => file.name.toLowerCase().includes('.so'));
if (headerFiles.length !== 1) {
alert(`文件夹必须包含且仅包含一个.h文件,当前包含 ${headerFiles.length} 个.h文件`);
return;
}
if (libraryFiles.length !== 1) {
alert(`文件夹必须包含且仅包含一个动态库文件,当前包含 ${libraryFiles.length} 个动态库文件`);
return;
}
if (files.length !== 2) {
alert(`文件夹只能包含一个.h文件和一个动态库文件,当前包含 ${files.length} 个文件`);
return;
}
// 创建FormData
const formData = new FormData();
formData.append('confName', selection.configurationName);
formData.append('folderName', folderName);
// 添加所有文件
files.forEach(file => {
formData.append('files', file);
});
// 显示上传进度
const uploadButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(3)');
if (uploadButton) {
const originalText = uploadButton.textContent;
uploadButton.textContent = '上传中...';
uploadButton.disabled = true;
}
// 发送上传请求
const response = await fetch('/api/filesystem/upload-package', {
method: 'POST',
body: formData
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || `上传失败: ${response.status}`);
}
const result = await response.json();
if (result.success) {
// 将返回的值填入对应的输入框
const dataPackagePathInput = this.shadowRoot.querySelector('#dataPackagePath');
const dataPackageNameInput = this.shadowRoot.querySelector('#dataPackageName');
const dataPackageHeaderNameInput = this.shadowRoot.querySelector('#dataPackageHeaderName');
const dataPackageEntryPointInput = this.shadowRoot.querySelector('#dataPackageEntryPoint');
const dataPackageInterfaceNameInput = this.shadowRoot.querySelector('#dataPackageInterfaceName');
if (dataPackagePathInput) {
dataPackagePathInput.value = result.packagePath;
}
if (dataPackageNameInput) {
dataPackageNameInput.value = result.libraryFile;
}
if (dataPackageHeaderNameInput) {
dataPackageHeaderNameInput.value = result.headerFile;
}
if (dataPackageEntryPointInput) {
dataPackageEntryPointInput.value = result.entryPoint;
}
if (dataPackageInterfaceNameInput) {
dataPackageInterfaceNameInput.value = result.paramType;
}
//将结构体名称填入对应下拉框的候选列表
const inputStructMapping2 = this.shadowRoot.querySelector('#inputStructMapping2');
const outputStructMapping2 = this.shadowRoot.querySelector('#outputStructMapping2');
const heartStructMapping2 = this.shadowRoot.querySelector('#heartStructMapping2');
// 填充头文件中结构体下拉框
if (inputStructMapping2 && outputStructMapping2 && heartStructMapping2) {
inputStructMapping2.innerHTML = '' + result.memberNames.map(name => ``).join('');
outputStructMapping2.innerHTML = '' + result.memberNames.map(name => ``).join('');
heartStructMapping2.innerHTML = '' + result.memberNames.map(name => ``).join('');
}
alert(`数据包上传成功!\n数据包路径: ${result.packagePath}\n头文件: ${result.headerFile}\n动态库文件: ${result.libraryFile}\n入口点: ${result.entryPoint}\n参数类型: ${result.paramType}`);
} else {
throw new Error(result.message || '上传失败');
}
} catch (error) {
console.error('数据包上传失败:', error);
alert(`数据包上传失败: ${error.message}`);
} finally {
// 恢复按钮状态
const uploadButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(3)');
if (uploadButton) {
uploadButton.textContent = '数据包模型上传';
uploadButton.disabled = false;
}
// 清理文件输入元素
document.body.removeChild(fileInput);
}
});
// 触发文件选择对话框
fileInput.click();
} catch (error) {
console.error('数据包上传初始化失败:', error);
alert(`数据包上传失败: ${error.message}`);
}
}
/**
* 生成模板代码
*/
async generateTemplateCode() {
try {
// 获取当前选择的构型信息
const savedSelection = localStorage.getItem('xnsim-selection');
const selection = savedSelection ? JSON.parse(savedSelection) : {};
if (!selection.configurationName) {
alert('请先选择构型!');
return;
}
// 获取表单数据
const className = this.currentVersion.ClassName;
const version = this.currentVersion.Version;
const planeName = this.currentVersion.PlaneName;
if (!className || !version || !planeName) {
alert('缺少必要信息:类名、版本号或飞机名称!');
return;
}
// 显示生成进度
const generateButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(4)');
if (generateButton) {
const originalText = generateButton.textContent;
generateButton.textContent = '生成中...';
generateButton.disabled = true;
}
// 调用后端API生成模板代码
const response = await fetch('/api/model-code-gen', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
className: className,
version: version,
planeName: planeName
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || `生成失败: ${response.status}`);
}
const result = await response.json();
if (result.result && result.result.includes('成功')) {
alert('模板代码生成成功!');
} else {
throw new Error(result.result || '生成失败');
}
} catch (error) {
console.error('生成模板代码失败:', error);
alert(`生成模板代码失败: ${error.message}`);
} finally {
// 恢复按钮状态
const generateButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(4)');
if (generateButton) {
generateButton.textContent = '模板代码生成';
generateButton.disabled = false;
}
}
}
/**
* 下载模板代码
*/
async downloadTemplateCode() {
try {
// 获取当前选择的构型信息
const savedSelection = localStorage.getItem('xnsim-selection');
const selection = savedSelection ? JSON.parse(savedSelection) : {};
if (!selection.configurationName) {
alert('请先选择构型!');
return;
}
// 获取表单数据
const className = this.currentVersion.ClassName;
const version = this.currentVersion.Version;
const planeName = this.currentVersion.PlaneName;
if (!className || !version || !planeName) {
alert('缺少必要信息:类名、版本号或飞机名称!');
return;
}
// 显示下载进度
const downloadButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(5)');
if (downloadButton) {
const originalText = downloadButton.textContent;
downloadButton.textContent = '生成中...';
downloadButton.disabled = true;
}
// 第一步:调用后端API生成压缩文件
const zipResponse = await fetch('/api/model-code-zip', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
className: className,
version: version,
planeName: planeName
})
});
if (!zipResponse.ok) {
const errorData = await zipResponse.json();
throw new Error(errorData.error || `生成压缩文件失败: ${zipResponse.status}`);
}
const zipResult = await zipResponse.json();
if (!zipResult.success || !zipResult.dstPath) {
throw new Error(zipResult.message || '生成压缩文件失败');
}
// 第二步:触发文件下载,直接使用绝对路径
const downloadUrl = `/api/filesystem/download-zip?filePath=${encodeURIComponent(zipResult.dstPath)}`;
window.open(downloadUrl);
alert('模板代码下载成功!');
} catch (error) {
console.error('下载模板代码失败:', error);
alert(`下载模板代码失败: ${error.message}`);
} finally {
// 恢复按钮状态
const downloadButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(5)');
if (downloadButton) {
downloadButton.textContent = '模板代码下载';
downloadButton.disabled = false;
}
}
}
/**
* 上传封装代码
*/
async uploadWrapperCode() {
try {
// 获取当前选择的构型信息
const savedSelection = localStorage.getItem('xnsim-selection');
const selection = savedSelection ? JSON.parse(savedSelection) : {};
if (!selection.configurationName) {
alert('请先选择构型!');
return;
}
// 创建文件输入元素
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = '.zip';
fileInput.style.display = 'none';
// 添加到DOM
document.body.appendChild(fileInput);
// 监听文件选择
fileInput.addEventListener('change', async (event) => {
try {
const files = Array.from(event.target.files);
if (files.length === 0) {
alert('请选择ZIP文件!');
return;
}
const file = files[0];
if (!file.name.toLowerCase().endsWith('.zip')) {
alert('请选择ZIP格式的文件!');
return;
}
// 创建FormData
const formData = new FormData();
formData.append('confName', selection.configurationName);
formData.append('file', file);
// 显示上传进度
const uploadButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(6)');
if (uploadButton) {
const originalText = uploadButton.textContent;
uploadButton.textContent = '上传中...';
uploadButton.disabled = true;
}
// 发送上传请求
const response = await fetch('/api/filesystem/upload-zip', {
method: 'POST',
body: formData
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.message || `上传失败: ${response.status}`);
}
const result = await response.json();
if (result.success) {
// 检查文件名是否符合 className_version.zip 格式
const fileName = result.file.name;
const expectedFileName = `${this.currentVersion.ClassName}_${this.currentVersion.Version}.zip`;
if (fileName === expectedFileName) {
// 文件名符合格式,自动调用解压
uploadButton.textContent = '解压中...';
try {
const unzipResponse = await fetch('/api/model-code-unzip', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
className: this.currentVersion.ClassName,
version: this.currentVersion.Version,
planeName: this.currentVersion.PlaneName,
srcPath: result.file.path
})
});
if (!unzipResponse.ok) {
const unzipErrorData = await unzipResponse.json();
throw new Error(unzipErrorData.error || `解压失败: ${unzipResponse.status}`);
}
const unzipResult = await unzipResponse.json();
if (unzipResult.result && unzipResult.result.includes('成功')) {
alert(`封装代码上传并解压成功!\n文件名: ${result.file.name}\n大小: ${(result.file.size / 1024 / 1024).toFixed(2)} MB\n解压状态: 成功`);
} else {
throw new Error(unzipResult.result || '解压失败');
}
} catch (unzipError) {
console.error('自动解压失败:', unzipError);
alert(`封装代码上传成功,但自动解压失败: ${unzipError.message}\n文件名: ${result.file.name}\n大小: ${(result.file.size / 1024 / 1024).toFixed(2)} MB`);
}
} else {
// 文件名不符合格式,只显示上传成功
alert(`封装代码上传成功!\n文件名: ${result.file.name}\n大小: ${(result.file.size / 1024 / 1024).toFixed(2)} MB\n注意: 文件名不符合 ${expectedFileName} 格式,未自动解压`);
}
} else {
throw new Error(result.message || '上传失败');
}
} catch (error) {
console.error('封装代码上传失败:', error);
alert(`封装代码上传失败: ${error.message}`);
} finally {
// 恢复按钮状态
const uploadButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(6)');
if (uploadButton) {
uploadButton.textContent = '封装代码上传';
uploadButton.disabled = false;
}
// 清理文件输入元素
document.body.removeChild(fileInput);
}
});
// 触发文件选择对话框
fileInput.click();
} catch (error) {
console.error('封装代码上传初始化失败:', error);
alert(`封装代码上传失败: ${error.message}`);
}
}
/**
* 模型编译发布
*/
async compileAndPublishModel() {
try {
// 获取当前选择的构型信息
const savedSelection = localStorage.getItem('xnsim-selection');
const selection = savedSelection ? JSON.parse(savedSelection) : {};
if (!selection.configurationName) {
alert('请先选择构型!');
return;
}
// 获取表单数据
const className = this.currentVersion.ClassName;
const version = this.currentVersion.Version;
const planeName = this.currentVersion.PlaneName;
if (!className || !version || !planeName) {
alert('缺少必要信息:类名、版本号或飞机名称!');
return;
}
// 显示编译进度
const compileButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(7)');
if (compileButton) {
const originalText = compileButton.textContent;
compileButton.textContent = '编译中...';
compileButton.disabled = true;
}
// 调用后端API编译模型
const response = await fetch('/api/model-code-compile', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
className: className,
version: version,
planeName: planeName
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || `编译失败: ${response.status}`);
}
const result = await response.json();
if (result.result && result.result.includes('成功')) {
alert('模型编译发布成功!');
} else {
throw new Error(result.result || '编译失败');
}
} catch (error) {
console.error('模型编译发布失败:', error);
alert(`模型编译发布失败: ${error.message}`);
} finally {
// 恢复按钮状态
const compileButton = this.shadowRoot.querySelector('.toolbar-button:nth-child(7)');
if (compileButton) {
compileButton.textContent = '模型编译发布';
compileButton.disabled = false;
}
}
}
}
customElements.define('model-development', ModelDevelopment);