XNSim/XNSimHtml/components/model-development.js

1886 lines
77 KiB
JavaScript
Raw Permalink 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.

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 = `
<style>
:host {
display: block;
padding: 20px;
color: #333;
box-sizing: border-box;
height: calc(100vh - 40px); /* 限制总高度 */
overflow: hidden; /* 防止整体滚动 */
}
*, *:before, *:after {
box-sizing: inherit;
}
.page-title {
color: #5c6bc0;
margin-bottom: 20px;
font-weight: 500;
font-size: 24px;
}
.subtitle {
font-size: 16px;
color: #666;
font-weight: normal;
margin-left: 10px;
}
.model-dev-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
max-height: calc(100vh - 200px); /* 进一步减小容器高度 */
overflow-y: auto;
padding-right: 10px;
padding-top: 10px; /* 添加上边距,解决悬停时上边界问题 */
padding-bottom: 30px; /* 添加底部边距,确保最后一行完全可见 */
width: 100%;
margin-bottom: 50px; /* 大幅增加底部边距,确保滚动条箭头完全可见 */
}
.chapter-card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
cursor: pointer;
height: 160px; /* 增加高度以容纳更多内容 */
display: flex;
flex-direction: column;
overflow: hidden; /* 防止内容溢出 */
}
.chapter-card:hover {
transform: translateY(-3px); /* 减小上移距离,防止超出可见区域 */
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15);
}
.chapter-title {
font-size: 20px;
font-weight: 600;
margin-bottom: 10px;
color: #333;
}
.chapter-subtitle {
font-size: 16px;
color: #666;
}
.model-card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
transition: transform 0.3s ease;
cursor: pointer;
height: 160px; /* 与章节卡片保持一致 */
display: flex;
flex-direction: column;
overflow: hidden; /* 防止内容溢出 */
}
.model-card:hover {
transform: translateY(-3px);
}
.model-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 8px;
color: #333;
}
.model-subtitle {
font-size: 15px;
color: #555;
margin-bottom: 12px;
}
.model-description {
font-size: 14px;
color: #666;
margin-bottom: 12px;
}
.model-class {
font-size: 13px;
color: #888;
font-style: italic;
}
.version-card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
transition: transform 0.3s ease;
cursor: pointer;
height: 100%;
display: flex;
flex-direction: column;
}
.version-card:hover {
transform: translateY(-3px);
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.12);
}
.version-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.version-title {
font-size: 18px;
font-weight: 600;
color: #333;
}
.version-number {
font-size: 15px;
font-weight: 500;
color: #5c6bc0;
padding: 3px 8px;
background: #eef0ff;
border-radius: 4px;
}
.version-details {
display: grid;
grid-template-columns: 1fr;
gap: 6px;
margin-bottom: 12px;
padding-bottom: 12px;
border-bottom: 1px solid #eee;
}
.version-detail {
font-size: 14px;
color: #666;
}
.label {
font-weight: 500;
color: #555;
margin-right: 5px;
}
.version-description {
font-size: 14px;
color: #666;
line-height: 1.5;
flex-grow: 1;
}
.back-button {
grid-column: 1 / -1;
cursor: pointer;
color: #667eea;
font-weight: 500;
margin-bottom: 15px;
}
.back-button:hover {
text-decoration: underline;
}
.add-version-button {
background-color: #667eea;
color: white;
border: none;
border-radius: 4px;
padding: 8px 16px;
font-size: 14px;
cursor: pointer;
margin-bottom: 20px;
}
.add-version-button:hover {
background-color: #5c6bc0;
}
.models-grid, .versions-grid {
display: grid;
grid-column: 1 / -1;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
grid-auto-rows: 1fr; /* 确保每行高度一致 */
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #333;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.form-group textarea {
min-height: 100px;
resize: vertical;
}
.form-actions {
margin-top: 20px;
text-align: right;
}
.save-button {
background-color: #667eea;
color: white;
border: none;
border-radius: 4px;
padding: 10px 20px;
font-size: 14px;
cursor: pointer;
}
.save-button:hover {
background-color: #5c6bc0;
}
.version-form {
grid-column: 1 / -1;
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
width: 100%;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
grid-column: 1 / -1;
}
.error-state {
text-align: center;
padding: 40px;
color: #e74c3c;
grid-column: 1 / -1;
}
.retry-btn {
background: #667eea;
color: white;
border: none;
border-radius: 4px;
padding: 8px 16px;
cursor: pointer;
font-size: 14px;
margin-top: 15px;
}
.empty-state {
text-align: center;
padding: 40px;
color: #666;
grid-column: 1 / -1;
}
/* 新建版本卡片样式 */
.new-version-card {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
background-color: #f8fafc;
border: 1px solid #cbd5e1;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
transition: all 0.3s ease;
}
.new-version-card:hover {
background-color: #f1f5f9;
border-color: #94a3b8;
transform: translateY(-3px);
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.12);
}
.new-version-icon {
display: flex;
justify-content: center;
align-items: center;
width: 50px;
height: 50px;
background-color: #eef2ff;
border-radius: 50%;
margin-bottom: 12px;
}
.new-version-icon svg {
width: 25px;
height: 25px;
fill: #6366f1;
}
.new-version-title {
font-size: 16px;
font-weight: 600;
color: #4f46e5;
margin-bottom: 5px;
}
.new-version-description {
font-size: 13px;
color: #64748b;
line-height: 1.4;
margin-bottom: 0;
}
</style>
<div class="model-development">
<h2 class="page-title">ATA章节</h2>
<div class="model-dev-container">
<div class="loading">正在加载数据...</div>
</div>
</div>
`;
// 开始初始化数据
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 headerTools = document.querySelector('header-tools');
const productName = headerTools.selectedProduct;
const response = await fetch(`/api/chapter-models/${chapterId}?productName=${encodeURIComponent(productName)}`);
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 headerTools = document.querySelector('header-tools');
const productName = headerTools.selectedProduct;
const response = await fetch(`/api/model-versions/${encodeURIComponent(className)}?productName=${encodeURIComponent(productName)}`);
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 headerTools = document.querySelector('header-tools');
const productName = headerTools.selectedProduct;
versionData.ProductName = productName;
const response = await fetch('/api/model-versions', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(versionData)
});
if (!response.ok) {
throw new 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 = `
<div class="chapter-title">${chapter.ID} - ${chapter.Name}</div>
<div class="chapter-subtitle">${chapter.Name_CN || '无中文名称'}</div>
`;
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} <span class="subtitle">${this.currentChapter.Name_CN || ''}</span>`;
}
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 = `<span>← 返回章节列表</span>`;
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 = `
<div class="model-title">${model.ModelName}</div>
<div class="model-subtitle">${model.ModelName_CN || '无中文名称'}</div>
<div class="model-description">${model.Description || '无描述'}</div>
<div class="model-class">类名: ${model.ClassName}</div>
`;
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 = `<span>← 返回模型列表</span>`;
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 = `
<div class="new-version-icon">
<svg viewBox="0 0 24 24">
<path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</svg>
</div>
<div class="new-version-title">新建模型版本</div>
<div class="new-version-description" style="margin-bottom: 0;">为${this.currentModel.name}创建新的版本</div>
`;
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 = `
<div class="version-header">
<div class="version-title">${version.Name || this.currentModel.name}</div>
<div class="version-number">v${version.Version || '未知版本'}</div>
</div>
<div class="version-details">
<div class="version-detail"><span class="label">作者:</span> ${version.Author || '未知'}</div>
<div class="version-detail"><span class="label">创建时间:</span> ${version.CreatTime || '未知'}</div>
<div class="version-detail"><span class="label">修改时间:</span> ${version.ChangeTime || '未知'}</div>
</div>
<div class="version-description">${version.Description || '无描述'}</div>
`;
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';
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 = `<span>← 返回版本列表</span>`;
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: '#805ad5', action: () => alert('生成代码功能即将上线') },
{ text: '编辑代码', color: '#d69e2e', action: () => alert('编辑代码功能即将上线') },
{ text: '模型编译', color: '#dd6b20', action: () => alert('模型编译功能即将上线') },
{ text: '模型提交', color: '#e53e3e', action: () => alert('模型提交功能即将上线') }
];
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 = `
<div class="form-group">
<label for="className">类名 (ClassName)*</label>
<input type="text" id="className" name="ClassName" value="${this.currentVersion.ClassName || ''}" readonly title="模型类名,不可修改" required>
</div>
<div class="form-group">
<label for="name">名称 (Name)*</label>
<input type="text" id="name" name="Name" value="${this.currentVersion.Name || ''}" readonly title="模型名称,不可修改" required>
</div>
<div class="form-group">
<label for="version">版本 (Version)*</label>
<input type="text" id="version" name="Version" value="${this.currentVersion.Version || '1.0.0'}" pattern="[0-9.]+" title="版本号只能包含数字和小数点例如1.0.0" required>
</div>
<div class="form-group">
<label for="author">作者 (Author)*</label>
<input type="text" id="author" name="Author" value="${this.currentVersion.Author || ''}" title="模型版本的作者姓名" required>
</div>
<div class="form-group">
<label for="changeTime">修改时间 (ChangeTime)*</label>
<div style="display: flex; gap: 8px;">
<input type="text" id="changeTime" name="ChangeTime" value="${this.currentVersion.ChangeTime || this.getCurrentDateTime()}" title="最后修改时间" style="flex: 1;" required>
<button type="button" id="dateTimePickerButton" style="background-color: #667eea; color: white; border: none; border-radius: 4px; width: 40px; display: flex; justify-content: center; align-items: center;">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
<line x1="16" y1="2" x2="16" y2="6"></line>
<line x1="8" y1="2" x2="8" y2="6"></line>
<line x1="3" y1="10" x2="21" y2="10"></line>
</svg>
</button>
</div>
</div>
`;
// 右列 - 描述和其他信息
const rightColumn = document.createElement('div');
rightColumn.className = 'form-column';
rightColumn.innerHTML = `
<div class="form-group" style="height: 100%;">
<label for="description">描述 (Description)</label>
<textarea id="description" name="Description" style="height: calc(100% - 30px);" title="模型版本的详细描述,包括主要功能和改进">${this.currentVersion.Description || ''}</textarea>
</div>
`;
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 = `
<div class="form-group">
<label for="codePath">代码路径 (CodePath)</label>
<input type="text" id="codePath" name="CodePath" value="${this.currentVersion.CodePath || ''}" title="模型代码文件的路径">
</div>
<div class="form-group">
<label for="dataPackagePath">数据包路径 (DataPackagePath)</label>
<input type="text" id="dataPackagePath" name="DataPackagePath" value="${this.currentVersion.DataPackagePath || ''}" title="数据包文件的路径">
</div>
<div class="form-group">
<label for="dataPackageHeaderPath">数据包头文件路径 (DataPackageHeaderPath)</label>
<input type="text" id="dataPackageHeaderPath" name="DataPackageHeaderPath" value="${this.currentVersion.DataPackageHeaderPath || ''}" title="数据包头文件的路径">
</div>
`;
// 添加右列输入框
const rightAdvancedColumn = document.createElement('div');
rightAdvancedColumn.innerHTML = `
<div class="form-group">
<label for="runFreqGroup">运行频率组 (RunFreqGroup)</label>
<select id="runFreqGroup" name="RunFreqGroup" title="模型的运行频率组">
<option value="0" ${this.currentVersion.RunFreqGroup === '0' ? 'selected' : ''}>基频 (0)</option>
<option value="1" ${this.currentVersion.RunFreqGroup === '1' ? 'selected' : ''}>半频 (1)</option>
<option value="2" ${this.currentVersion.RunFreqGroup === '2' ? 'selected' : ''}>1/4频 (2)</option>
<option value="3" ${this.currentVersion.RunFreqGroup === '3' ? 'selected' : ''}>1/8频 (3)</option>
<option value="4" ${this.currentVersion.RunFreqGroup === '4' ? 'selected' : ''}>1/16频 (4)</option>
<option value="5" ${this.currentVersion.RunFreqGroup === '5' ? 'selected' : ''}>1/32频 (5)</option>
</select>
</div>
<div class="form-group">
<label for="runNode">运行节点 (RunNode)</label>
<select id="runNode" name="RunNode" title="模型的运行节点">
<!-- 选项将由JavaScript动态生成 -->
</select>
</div>
<div class="form-group">
<label for="priority">优先级 (Priority)</label>
<input type="number" id="priority" name="Priority" value="${this.currentVersion.Priority || '0'}" min="0" max="99" title="模型运行的优先级范围0-99">
</div>
`;
// 第三行 - 添加剩余输入框
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 = `
<div class="form-group">
<label for="dataPackageEntryPoint">数据包入口点 (DataPackageEntryPoint)</label>
<input type="text" id="dataPackageEntryPoint" name="DataPackageEntryPoint" value="${this.currentVersion.DataPackageEntryPoint || ''}" title="数据包的入口函数名称">
</div>
`;
const bottomRightColumn = document.createElement('div');
bottomRightColumn.innerHTML = `
<div class="form-group">
<label for="dataPackageInterfaceName">数据包接口参数名称 (DataPackageInterfaceName)</label>
<input type="text" id="dataPackageInterfaceName" name="DataPackageInterfaceName" value="${this.currentVersion.DataPackageInterfaceName || ''}" title="数据包接口参数的名称">
</div>
`;
bottomRow.appendChild(bottomLeftColumn);
bottomRow.appendChild(bottomRightColumn);
// 装配高级设置部分
gridLayout.appendChild(leftAdvancedColumn);
gridLayout.appendChild(rightAdvancedColumn);
advancedSection.appendChild(gridLayout);
advancedSection.appendChild(bottomRow);
// 表单操作按钮
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;';
formActions.innerHTML = `
<button type="submit" class="save-button">${this.isEditMode ? '保存修改' : '创建版本'}</button>
`;
// 组装表单
form.appendChild(basicInfoSection);
form.appendChild(advancedSection);
form.appendChild(formActions);
formContainer.appendChild(form);
container.appendChild(formContainer);
// 为日期时间按钮添加事件监听器
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();
});
}
}, 0);
// 添加表单提交事件
form.addEventListener('submit', async (e) => {
e.preventDefault();
try {
// 显示加载提示
const saveButton = form.querySelector('.save-button');
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之间的整数');
}
}
// 构建版本数据
const versionData = {
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,
CodePath: form.querySelector('#codePath').value,
RunFreqGroup: form.querySelector('#runFreqGroup').value,
RunNode: form.querySelector('#runNode').value,
Priority: form.querySelector('#priority').value,
DataPackagePath: form.querySelector('#dataPackagePath').value,
DataPackageHeaderPath: form.querySelector('#dataPackageHeaderPath').value,
DataPackageEntryPoint: form.querySelector('#dataPackageEntryPoint').value,
DataPackageInterfaceName: form.querySelector('#dataPackageInterfaceName').value,
isUpdate: this.isEditMode,
originalVersion: this.isEditMode ? this.currentVersion.Version : null
};
// 验证必填字段
const requiredFields = ['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 = form.querySelector('.save-button');
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-60表示周日
// 生成月份选项
let monthOptions = '';
const monthNames = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'];
monthNames.forEach((name, idx) => {
monthOptions += `<option value="${idx}" ${idx === month ? 'selected' : ''}>${name}</option>`;
});
// 生成年份选项
let yearOptions = '';
const currentYear = new Date().getFullYear();
for (let y = currentYear - 10; y <= currentYear + 10; y++) {
yearOptions += `<option value="${y}" ${y === year ? 'selected' : ''}>${y}</option>`;
}
// 生成日历表格
let calendarRows = '';
let dayCount = 1;
// 添加表头
calendarRows += '<tr>';
['日', '一', '二', '三', '四', '五', '六'].forEach(dayName => {
calendarRows += `<th>${dayName}</th>`;
});
calendarRows += '</tr>';
// 计算行数
const totalCells = firstDay + daysInMonth;
const rowCount = Math.ceil(totalCells / 7);
// 添加日期行
for (let i = 0; i < rowCount; i++) {
calendarRows += '<tr>';
for (let j = 0; j < 7; j++) {
if ((i === 0 && j < firstDay) || dayCount > daysInMonth) {
calendarRows += '<td></td>';
} else {
const isToday = dayCount === day;
calendarRows += `<td class="calendar-day ${isToday ? 'selected' : ''}" data-day="${dayCount}">${dayCount}</td>`;
dayCount++;
}
}
calendarRows += '</tr>';
}
// 解析当前时间为时、分、秒
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 = `
<div class="modal-content" style="background-color: white; margin: auto; padding: 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); max-width: 400px; width: 100%;">
<div class="modal-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px;">
<div class="modal-title" style="font-size: 18px; font-weight: 600; color: #2d3748;">选择日期和时间</div>
<span class="close" style="font-size: 24px; color: #a0aec0; cursor: pointer;">&times;</span>
</div>
<div class="modal-body">
<div class="calendar-container" style="width: 100%;">
<div class="calendar-header" style="display: flex; justify-content: space-between; margin-bottom: 10px;">
<select id="calendarMonth" style="padding: 5px; border: 1px solid #e2e8f0; border-radius: 4px; flex: 1; margin-right: 10px;">${monthOptions}</select>
<select id="calendarYear" style="padding: 5px; border: 1px solid #e2e8f0; border-radius: 4px; flex: 1;">${yearOptions}</select>
</div>
<table class="calendar-table" style="width: 100%; border-collapse: collapse;">
<thead>
${calendarRows}
</thead>
</table>
</div>
<div class="time-container" style="margin-top: 15px;">
<label style="display: block; margin-bottom: 5px; color: #4a5568;">时间:</label>
<div style="display: flex; gap: 5px;">
<div style="flex: 1;">
<label for="hourInput" style="display: block; text-align: center; font-size: 12px; color: #718096;">时</label>
<input type="number" id="hourInput" min="0" max="23" value="${hours}" style="width: 100%; padding: 8px; border: 1px solid #e2e8f0; border-radius: 4px; text-align: center;">
</div>
<div style="display: flex; align-items: center; padding-top: 15px;">:</div>
<div style="flex: 1;">
<label for="minuteInput" style="display: block; text-align: center; font-size: 12px; color: #718096;">分</label>
<input type="number" id="minuteInput" min="0" max="59" value="${minutes}" style="width: 100%; padding: 8px; border: 1px solid #e2e8f0; border-radius: 4px; text-align: center;">
</div>
<div style="display: flex; align-items: center; padding-top: 15px;">:</div>
<div style="flex: 1;">
<label for="secondInput" style="display: block; text-align: center; font-size: 12px; color: #718096;">秒</label>
<input type="number" id="secondInput" min="0" max="59" value="${seconds}" style="width: 100%; padding: 8px; border: 1px solid #e2e8f0; border-radius: 4px; text-align: center;">
</div>
</div>
</div>
</div>
<div class="modal-footer" style="margin-top: 20px; text-align: right;">
<button id="cancelDateTime" style="background-color: #e2e8f0; color: #4a5568; border: none; border-radius: 4px; padding: 8px 16px; margin-right: 10px; cursor: pointer;">取消</button>
<button id="confirmDateTime" style="background-color: #4c6ef5; color: white; border: none; border-radius: 4px; padding: 8px 16px; cursor: pointer;">确定</button>
</div>
</div>
`;
// 添加样式
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);
// 关闭当前对话框
closeModal();
// 更新日期参数并重新显示对话框
currentDate = new Date(selectedYear, selectedMonth, 1);
this.showDateTimeDialog(inputElement);
};
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 = `
<div class="error-state">
<p>加载数据失败: ${message}</p>
<button class="retry-btn">重试</button>
</div>
`;
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 = `
<style>
:host {
display: block;
padding: 20px;
color: #333;
box-sizing: border-box;
height: calc(100vh - 40px); /* 限制总高度 */
overflow: hidden; /* 防止整体滚动 */
}
*, *:before, *:after {
box-sizing: inherit;
}
.page-title {
color: #5c6bc0;
margin-bottom: 20px;
font-weight: 500;
font-size: 24px;
}
.subtitle {
font-size: 16px;
color: #666;
font-weight: normal;
margin-left: 10px;
}
.model-dev-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
max-height: calc(100vh - 160px); /* 进一步减小容器高度,确保滚动条完全可见 */
overflow-y: auto;
padding-right: 10px;
padding-top: 10px; /* 添加上边距,解决悬停时上边界问题 */
padding-bottom: 30px; /* 添加底部边距,确保最后一行完全可见 */
width: 100%;
}
.chapter-card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
cursor: pointer;
height: 160px; /* 增加高度以容纳更多内容 */
display: flex;
flex-direction: column;
overflow: hidden; /* 防止内容溢出 */
}
.chapter-card:hover {
transform: translateY(-3px); /* 减小上移距离,防止超出可见区域 */
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15);
}
.chapter-title {
font-size: 20px;
font-weight: 600;
margin-bottom: 10px;
color: #333;
}
.chapter-subtitle {
font-size: 16px;
color: #666;
}
.model-card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
transition: transform 0.3s ease;
cursor: pointer;
height: 160px; /* 与章节卡片保持一致 */
display: flex;
flex-direction: column;
overflow: hidden; /* 防止内容溢出 */
}
.model-card:hover {
transform: translateY(-3px);
}
.model-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 8px;
color: #333;
}
.model-subtitle {
font-size: 15px;
color: #555;
margin-bottom: 12px;
}
.model-description {
font-size: 14px;
color: #666;
margin-bottom: 12px;
}
.model-class {
font-size: 13px;
color: #888;
font-style: italic;
}
.version-card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
transition: transform 0.3s ease;
cursor: pointer;
height: 100%;
display: flex;
flex-direction: column;
}
.version-card:hover {
transform: translateY(-3px);
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.12);
}
.version-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.version-title {
font-size: 18px;
font-weight: 600;
color: #333;
}
.version-number {
font-size: 15px;
font-weight: 500;
color: #5c6bc0;
padding: 3px 8px;
background: #eef0ff;
border-radius: 4px;
}
.version-details {
display: grid;
grid-template-columns: 1fr;
gap: 6px;
margin-bottom: 12px;
padding-bottom: 12px;
border-bottom: 1px solid #eee;
}
.version-detail {
font-size: 14px;
color: #666;
}
.label {
font-weight: 500;
color: #555;
margin-right: 5px;
}
.version-description {
font-size: 14px;
color: #666;
line-height: 1.5;
flex-grow: 1;
}
/* 新建版本卡片样式 */
.new-version-card {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
background-color: #f8fafc;
border: 1px solid #cbd5e1;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
transition: all 0.3s ease;
}
.new-version-card:hover {
background-color: #f1f5f9;
border-color: #94a3b8;
transform: translateY(-3px);
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.12);
}
.new-version-icon {
display: flex;
justify-content: center;
align-items: center;
width: 50px;
height: 50px;
background-color: #eef2ff;
border-radius: 50%;
margin-bottom: 12px;
}
.new-version-icon svg {
width: 25px;
height: 25px;
fill: #6366f1;
}
.new-version-title {
font-size: 16px;
font-weight: 600;
color: #4f46e5;
margin-bottom: 5px;
}
.new-version-description {
font-size: 13px;
color: #64748b;
line-height: 1.4;
margin-bottom: 0;
}
.back-button {
grid-column: 1 / -1;
cursor: pointer;
color: #667eea;
font-weight: 500;
margin-bottom: 15px;
}
.back-button:hover {
text-decoration: underline;
}
.add-version-button {
background-color: #667eea;
color: white;
border: none;
border-radius: 4px;
padding: 8px 16px;
font-size: 14px;
cursor: pointer;
margin-bottom: 20px;
}
.add-version-button:hover {
background-color: #5c6bc0;
}
.models-grid, .versions-grid {
display: grid;
grid-column: 1 / -1;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
grid-auto-rows: 1fr; /* 确保每行高度一致 */
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #333;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.form-group textarea {
min-height: 100px;
resize: vertical;
}
.form-actions {
margin-top: 20px;
text-align: right;
}
.save-button {
background-color: #667eea;
color: white;
border: none;
border-radius: 4px;
padding: 10px 20px;
font-size: 14px;
cursor: pointer;
}
.save-button:hover {
background-color: #5c6bc0;
}
.version-form {
grid-column: 1 / -1;
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
width: 100%;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
grid-column: 1 / -1;
}
.error-state {
text-align: center;
padding: 40px;
color: #e74c3c;
grid-column: 1 / -1;
}
.retry-btn {
background: #667eea;
color: white;
border: none;
border-radius: 4px;
padding: 8px 16px;
cursor: pointer;
font-size: 14px;
margin-top: 15px;
}
.empty-state {
text-align: center;
padding: 40px;
color: #666;
grid-column: 1 / -1;
}
</style>
<div class="model-development">
<h2 class="page-title">ATA章节</h2>
<div class="model-dev-container">
<div class="loading">正在加载数据...</div>
</div>
</div>
`;
}
// 组件被重新激活时调用
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(),
CodePath: '',
RunFreqGroup: '0',
RunNode: '0',
Priority: '0',
DataPackagePath: '',
DataPackageHeaderPath: '',
DataPackageEntryPoint: '',
DataPackageInterfaceName: ''
};
// 打开编辑界面
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}`;
}
saveVersionChanges(form) {
// 收集表单数据
const formData = new FormData(form);
const updatedVersion = {};
for (const [key, value] of formData.entries()) {
updatedVersion[key] = value;
}
// 确保重要字段值不变
updatedVersion.ClassName = this.currentVersion.ClassName;
// 在这里可以添加保存逻辑例如调用API
console.log('要保存的版本数据:', updatedVersion);
// 提示用户
alert('版本数据保存功能正在开发中...');
// 保存成功后不再自动返回版本列表,留在当前编辑页面
}
}
customElements.define('model-development', ModelDevelopment);