2025-04-28 12:25:20 +08:00
|
|
|
|
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 {
|
2025-04-30 13:47:35 +08:00
|
|
|
|
const headerTools = document.querySelector('header-tools');
|
|
|
|
|
const productName = headerTools.selectedProduct;
|
|
|
|
|
const response = await fetch(`/api/chapter-models/${chapterId}?productName=${encodeURIComponent(productName)}`);
|
|
|
|
|
|
2025-04-28 12:25:20 +08:00
|
|
|
|
if (!response.ok) {
|
|
|
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
|
|
|
}
|
2025-04-30 13:47:35 +08:00
|
|
|
|
|
2025-04-28 12:25:20 +08:00
|
|
|
|
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 {
|
2025-04-30 13:47:35 +08:00
|
|
|
|
const headerTools = document.querySelector('header-tools');
|
|
|
|
|
const productName = headerTools.selectedProduct;
|
|
|
|
|
|
|
|
|
|
const response = await fetch(`/api/model-versions/${encodeURIComponent(className)}?productName=${encodeURIComponent(productName)}`);
|
2025-04-28 12:25:20 +08:00
|
|
|
|
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 {
|
2025-04-30 13:47:35 +08:00
|
|
|
|
const headerTools = document.querySelector('header-tools');
|
|
|
|
|
const productName = headerTools.selectedProduct;
|
|
|
|
|
versionData.ProductName = productName;
|
|
|
|
|
|
2025-04-28 12:25:20 +08:00
|
|
|
|
const response = await fetch('/api/model-versions', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify(versionData)
|
|
|
|
|
});
|
2025-04-30 13:47:35 +08:00
|
|
|
|
|
2025-04-28 12:25:20 +08:00
|
|
|
|
if (!response.ok) {
|
2025-04-30 13:47:35 +08:00
|
|
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
2025-04-28 12:25:20 +08:00
|
|
|
|
}
|
2025-04-30 13:47:35 +08:00
|
|
|
|
|
|
|
|
|
return await response.json();
|
2025-04-28 12:25:20 +08:00
|
|
|
|
} 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);
|
2025-04-30 13:47:35 +08:00
|
|
|
|
|
2025-04-28 12:25:20 +08:00
|
|
|
|
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-6,0表示周日
|
|
|
|
|
|
|
|
|
|
// 生成月份选项
|
|
|
|
|
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;">×</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);
|