增加了构型选择功能,修改了数据库结构

This commit is contained in:
jinchao 2025-04-30 13:47:35 +08:00
parent f753a8c94d
commit aa42e28173
14 changed files with 336 additions and 48 deletions

Binary file not shown.

View File

@ -1,2 +0,0 @@
USER_ID,USER_NAME,USER_PASSWORD,USER_ACCESS
0,admin,4a0f4f90ee7c4bcfe1aaed38f83270c2e04d24250bf4370617f4cb792a9e5a3b,4
1 USER_ID USER_NAME USER_PASSWORD USER_ACCESS
2 0 admin 4a0f4f90ee7c4bcfe1aaed38f83270c2e04d24250bf4370617f4cb792a9e5a3b 4

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -0,0 +1,171 @@
class HeaderTools extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.render();
this.addEventListeners();
}
// 添加getter方法
get selectedProduct() {
return this.shadowRoot.getElementById('productSelect').value;
}
render() {
this.shadowRoot.innerHTML = `
<style>
:host {
display: flex;
align-items: center;
gap: 12px;
margin-left: auto;
}
.tool-item {
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
}
.tool-item:hover {
background-color: rgba(0, 0, 0, 0.05);
}
.tool-item img {
width: 20px;
height: 20px;
}
.product-container {
display: flex;
align-items: center;
gap: 8px;
}
.product-label {
font-size: 14px;
color: #666;
}
.product-select {
min-width: 120px;
height: 32px;
padding: 0 8px;
border: 1px solid #e0e0e0;
border-radius: 4px;
background-color: white;
cursor: pointer;
}
.search-box {
display: flex;
align-items: center;
height: 32px;
border: 1px solid #e0e0e0;
border-radius: 4px;
background-color: white;
padding: 0 8px;
margin-left: 50px; /* 增加与下拉框的间距 */
}
.search-box input {
border: none;
outline: none;
padding: 0 8px;
width: 120px;
}
.search-box img {
width: 16px;
height: 16px;
opacity: 0.5;
}
</style>
<div class="product-container">
<span class="product-label">构型</span>
<select class="product-select" id="productSelect">
<option value="">选择构型</option>
</select>
</div>
<div class="search-box">
<input type="text" placeholder="搜索...">
<img src="assets/icons/png/search_b.png" alt="搜索">
</div>
<div class="tool-item" id="fontSizeBtn" title="调整字体大小">
<img src="assets/icons/png/font_b.png" alt="字体">
</div>
<div class="tool-item" id="themeBtn" title="调整主题">
<img src="assets/icons/png/palette_b.png" alt="主题">
</div>
<div class="tool-item" id="notificationBtn" title="通知">
<img src="assets/icons/png/bell_b.png" alt="通知">
</div>
<div class="tool-item" id="onlineUsersBtn" title="在线用户">
<img src="assets/icons/png/users_b.png" alt="在线用户">
</div>
`;
}
addEventListeners() {
// 加载构型列表
this.loadProducts();
// 字体大小调整
this.shadowRoot.getElementById('fontSizeBtn').addEventListener('click', () => {
this.dispatchEvent(new CustomEvent('font-size-click'));
});
// 主题调整
this.shadowRoot.getElementById('themeBtn').addEventListener('click', () => {
this.dispatchEvent(new CustomEvent('theme-click'));
});
// 通知
this.shadowRoot.getElementById('notificationBtn').addEventListener('click', () => {
this.dispatchEvent(new CustomEvent('notification-click'));
});
// 在线用户
this.shadowRoot.getElementById('onlineUsersBtn').addEventListener('click', () => {
this.dispatchEvent(new CustomEvent('online-users-click'));
});
// 构型选择
this.shadowRoot.getElementById('productSelect').addEventListener('change', (e) => {
this.dispatchEvent(new CustomEvent('product-change', {
detail: { product: e.target.value }
}));
});
}
async loadProducts() {
try {
const response = await fetch('/api/products');
if (!response.ok) {
throw new Error('获取构型列表失败');
}
const products = await response.json();
const select = this.shadowRoot.getElementById('productSelect');
// 清空现有选项
select.innerHTML = '<option value="">选择构型</option>';
products.forEach(product => {
const option = document.createElement('option');
option.value = product.ProductName;
option.textContent = product.ProductName;
select.appendChild(option);
});
} catch (error) {
console.error('加载构型列表失败:', error);
// 可以在这里添加错误提示UI
}
}
}
customElements.define('header-tools', HeaderTools);

View File

@ -393,10 +393,14 @@ class ModelDevelopment extends HTMLElement {
async fetchModels(chapterId) { async fetchModels(chapterId) {
try { try {
const response = await fetch(`/api/chapter-models/${chapterId}`); const headerTools = document.querySelector('header-tools');
const productName = headerTools.selectedProduct;
const response = await fetch(`/api/chapter-models/${chapterId}?productName=${encodeURIComponent(productName)}`);
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
this.currentModels = await response.json(); this.currentModels = await response.json();
this.currentChapter = this.chapters.find(chapter => chapter.ID === chapterId); this.currentChapter = this.chapters.find(chapter => chapter.ID === chapterId);
this.renderModels(); this.renderModels();
@ -408,7 +412,10 @@ class ModelDevelopment extends HTMLElement {
async fetchModelVersions(className, modelName) { async fetchModelVersions(className, modelName) {
try { try {
const response = await fetch(`/api/model-versions/${encodeURIComponent(className)}`); 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) { if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
@ -423,6 +430,10 @@ class ModelDevelopment extends HTMLElement {
async saveModelVersion(versionData) { async saveModelVersion(versionData) {
try { try {
const headerTools = document.querySelector('header-tools');
const productName = headerTools.selectedProduct;
versionData.ProductName = productName;
const response = await fetch('/api/model-versions', { const response = await fetch('/api/model-versions', {
method: 'POST', method: 'POST',
headers: { headers: {
@ -432,12 +443,10 @@ class ModelDevelopment extends HTMLElement {
}); });
if (!response.ok) { if (!response.ok) {
const errorData = await response.json(); throw new Error(`HTTP error! status: ${response.status}`);
throw new Error(errorData.error || `HTTP error! status: ${response.status}`);
} }
const result = await response.json(); return await response.json();
return result;
} catch (error) { } catch (error) {
console.error('保存模型版本失败:', error); console.error('保存模型版本失败:', error);
throw error; throw error;
@ -1753,7 +1762,6 @@ class ModelDevelopment extends HTMLElement {
// 组件被重新激活时调用 // 组件被重新激活时调用
reactivate() { reactivate() {
console.log('组件被重新激活');
if (this.currentView === 'chapters') { if (this.currentView === 'chapters') {
this.init(); this.init();
} else if (this.currentView === 'models' && this.currentChapter) { } else if (this.currentView === 'models' && this.currentChapter) {

View File

@ -720,7 +720,7 @@ class ServiceDevelopment extends HTMLElement {
// 组件被重新激活时调用 // 组件被重新激活时调用
reactivate() { reactivate() {
console.log('服务开发组件被重新激活');
} }
showVersionEditor(versionData) { showVersionEditor(versionData) {

View File

@ -142,6 +142,13 @@
height: 20px; height: 20px;
} }
/* 添加新的样式 */
.header-right {
display: flex;
align-items: center;
gap: 16px;
}
/* 重置body样式 */ /* 重置body样式 */
body { body {
margin: 0; margin: 0;
@ -185,8 +192,11 @@
<img src="assets/icons/png/con_b.png" alt="主页 / 概览" class="icon-small"> <img src="assets/icons/png/con_b.png" alt="主页 / 概览" class="icon-small">
<span id="currentPath">主页 / 概览</span> <span id="currentPath">主页 / 概览</span>
</div> </div>
<div class="header-right">
<header-tools></header-tools>
<user-info></user-info> <user-info></user-info>
</div> </div>
</div>
<div class="content-area-container"> <div class="content-area-container">
<tabs-container></tabs-container> <tabs-container></tabs-container>
<content-area></content-area> <content-area></content-area>
@ -195,6 +205,7 @@
</div> </div>
</div> </div>
<script src="components/header-tools.js"></script>
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const tabsContainer = document.querySelector('tabs-container'); const tabsContainer = document.querySelector('tabs-container');

View File

@ -22,12 +22,13 @@ router.get('/ata-chapters', (req, res) => {
router.get('/chapter-models/:chapterId', (req, res) => { router.get('/chapter-models/:chapterId', (req, res) => {
try { try {
const { chapterId } = req.params; const { chapterId } = req.params;
const { productName } = req.query;
if (!chapterId) { if (!chapterId) {
return res.status(400).json({ error: '缺少章节ID参数' }); return res.status(400).json({ error: '缺少章节ID参数' });
} }
const models = getModelsByChapterId(chapterId); const models = getModelsByChapterId(chapterId, productName);
res.json(models); res.json(models);
} catch (error) { } catch (error) {
console.error('处理章节模型请求时出错:', error); console.error('处理章节模型请求时出错:', error);
@ -39,11 +40,13 @@ router.get('/chapter-models/:chapterId', (req, res) => {
router.get('/model-versions/:className', (req, res) => { router.get('/model-versions/:className', (req, res) => {
try { try {
const className = req.params.className; const className = req.params.className;
const { productName } = req.query;
if (!className) { if (!className) {
return res.status(400).json({ error: '模型类名不能为空' }); return res.status(400).json({ error: '模型类名不能为空' });
} }
const versions = getModelVersionsByClassName(className); const versions = getModelVersionsByClassName(className, productName);
res.json(versions); res.json(versions);
} catch (error) { } catch (error) {
console.error(`获取模型版本失败: ${error.message}`); console.error(`获取模型版本失败: ${error.message}`);
@ -55,11 +58,19 @@ router.get('/model-versions/:className', (req, res) => {
router.post('/model-versions', (req, res) => { router.post('/model-versions', (req, res) => {
try { try {
const versionData = req.body; const versionData = req.body;
const { productName } = req.query;
if (!versionData || !versionData.ClassName) { if (!versionData || !versionData.ClassName) {
return res.status(400).json({ error: '缺少必要的模型版本数据' }); return res.status(400).json({ error: '缺少必要的模型版本数据' });
} }
// 如果请求中没有productName则使用versionData中的ProductName
if (!productName && versionData.ProductName) {
versionData.ProductName = versionData.ProductName;
} else if (productName) {
versionData.ProductName = productName;
}
const result = saveModelVersion(versionData); const result = saveModelVersion(versionData);
res.json(result); res.json(result);
} catch (error) { } catch (error) {

View File

@ -0,0 +1,16 @@
const express = require('express');
const router = express.Router();
const { getProducts } = require('../utils/db-utils');
// 获取所有构型列表
router.get('/products', (req, res) => {
try {
const products = getProducts();
res.json(products);
} catch (error) {
console.error('获取构型列表失败:', error);
res.status(500).json({ error: '获取构型列表失败', details: error.message });
}
});
module.exports = router;

View File

@ -18,6 +18,7 @@ const projectModelRoutes = require('./routes/project-model');
const ataChaptersRoutes = require('./routes/model-dev'); const ataChaptersRoutes = require('./routes/model-dev');
const simulationRoutes = require('./routes/run-simulation'); const simulationRoutes = require('./routes/run-simulation');
const udpMonitorRoutes = require('./routes/udp-monitor'); const udpMonitorRoutes = require('./routes/udp-monitor');
const productsRoutes = require('./routes/products');
const app = express(); const app = express();
const PORT = process.env.PORT || 3000; const PORT = process.env.PORT || 3000;
@ -66,6 +67,7 @@ app.use('/api', projectModelRoutes);
app.use('/api', ataChaptersRoutes); app.use('/api', ataChaptersRoutes);
app.use('/api', simulationRoutes); app.use('/api', simulationRoutes);
app.use('/api/udp-monitor', udpMonitorRoutes); app.use('/api/udp-monitor', udpMonitorRoutes);
app.use('/api', productsRoutes);
// 主页路由 // 主页路由
app.get('/', (req, res) => { app.get('/', (req, res) => {

View File

@ -30,7 +30,7 @@ function getATAChapters() {
} }
// 根据章节ID查询XNModels表中的模型 // 根据章节ID查询XNModels表中的模型
function getModelsByChapterId(chapterId) { function getModelsByChapterId(chapterId, productName) {
try { try {
const xnCorePath = getXNCorePath(); const xnCorePath = getXNCorePath();
if (!xnCorePath) { if (!xnCorePath) {
@ -45,13 +45,29 @@ function getModelsByChapterId(chapterId) {
// 打开数据库连接 // 打开数据库连接
const db = new Database(dbPath, { readonly: true }); const db = new Database(dbPath, { readonly: true });
// 查询该章节下的所有模型 // 根据productName是否为空构建不同的查询
const models = db.prepare(` let query;
SELECT Chapters_ID, ModelName, ModelName_CN, Description, ClassName let params;
if (!productName || productName === '' || productName === 'undefined') {
query = `
SELECT ProductName, Chapters_ID, ModelName, ModelName_CN, Description, ClassName
FROM 'XNModels' FROM 'XNModels'
WHERE Chapters_ID = ? WHERE Chapters_ID = ?
ORDER BY ModelName ORDER BY ModelName
`).all(chapterId); `;
params = [chapterId];
} else {
query = `
SELECT ProductName, Chapters_ID, ModelName, ModelName_CN, Description, ClassName
FROM 'XNModels'
WHERE Chapters_ID = ? AND ProductName = ?
ORDER BY ModelName
`;
params = [chapterId, productName];
}
const models = db.prepare(query).all(...params);
db.close(); db.close();
@ -63,7 +79,7 @@ function getModelsByChapterId(chapterId) {
} }
// 根据ClassName查询XNModelsVersion表中的模型版本 // 根据ClassName查询XNModelsVersion表中的模型版本
function getModelVersionsByClassName(className) { function getModelVersionsByClassName(className, productName) {
try { try {
const xnCorePath = getXNCorePath(); const xnCorePath = getXNCorePath();
if (!xnCorePath) { if (!xnCorePath) {
@ -78,15 +94,33 @@ function getModelVersionsByClassName(className) {
// 打开数据库连接 // 打开数据库连接
const db = new Database(dbPath, { readonly: true }); const db = new Database(dbPath, { readonly: true });
// 查询该类名下的所有版本 // 根据productName是否为空构建不同的查询
const versions = db.prepare(` let query;
SELECT ClassName, Name, Version, CodePath, Author, Description, let params;
if (!productName || productName === '') {
query = `
SELECT ProductName, ClassName, Name, Version, CodePath, Author, Description,
CreatTime, ChangeTime, RunFreqGroup, RunNode, Priority, CreatTime, ChangeTime, RunFreqGroup, RunNode, Priority,
DataPackagePath, DataPackageHeaderPath, DataPackageEntryPoint, DataPackageInterfaceName DataPackagePath, DataPackageHeaderPath, DataPackageEntryPoint, DataPackageInterfaceName
FROM 'XNModelsVersion' FROM 'XNModelsVersion'
WHERE ClassName = ? WHERE ClassName = ?
ORDER BY Version DESC ORDER BY Version DESC
`).all(className); `;
params = [className];
} else {
query = `
SELECT ProductName, ClassName, Name, Version, CodePath, Author, Description,
CreatTime, ChangeTime, RunFreqGroup, RunNode, Priority,
DataPackagePath, DataPackageHeaderPath, DataPackageEntryPoint, DataPackageInterfaceName
FROM 'XNModelsVersion'
WHERE ClassName = ? AND ProductName = ?
ORDER BY Version DESC
`;
params = [className, productName];
}
const versions = db.prepare(query).all(...params);
db.close(); db.close();
@ -101,7 +135,7 @@ function getModelVersionsByClassName(className) {
function saveModelVersion(versionData) { function saveModelVersion(versionData) {
try { try {
// 验证必填字段 // 验证必填字段
const requiredFields = ['ClassName', 'Name', 'Version', 'Author']; const requiredFields = ['ClassName', 'Name', 'Version', 'Author', 'ProductName'];
for (const field of requiredFields) { for (const field of requiredFields) {
if (!versionData[field]) { if (!versionData[field]) {
throw new Error(`${field} 是必填字段`); throw new Error(`${field} 是必填字段`);
@ -126,8 +160,8 @@ function saveModelVersion(versionData) {
// 查询是否存在要更新的版本 // 查询是否存在要更新的版本
const existingVersion = db.prepare(` const existingVersion = db.prepare(`
SELECT COUNT(*) as count FROM 'XNModelsVersion' SELECT COUNT(*) as count FROM 'XNModelsVersion'
WHERE ClassName = ? AND Version = ? WHERE ClassName = ? AND Version = ? AND ProductName = ?
`).get(versionData.ClassName, versionData.originalVersion || versionData.Version); `).get(versionData.ClassName, versionData.originalVersion || versionData.Version, versionData.ProductName);
if (existingVersion.count === 0) { if (existingVersion.count === 0) {
// 不存在要更新的版本,创建新版本 // 不存在要更新的版本,创建新版本
@ -164,15 +198,16 @@ function saveModelVersion(versionData) {
DataPackagePath = ?, DataPackagePath = ?,
DataPackageHeaderPath = ?, DataPackageHeaderPath = ?,
DataPackageEntryPoint = ?, DataPackageEntryPoint = ?,
DataPackageInterfaceName = ? DataPackageInterfaceName = ?,
WHERE ClassName = ? AND Version = ? ProductName = ?
WHERE ClassName = ? AND Version = ? AND ProductName = ?
`).run( `).run(
versionData.Name, versionData.Name,
versionData.Version, versionData.Version,
versionData.Author, versionData.Author,
versionData.Description || '', versionData.Description || '',
versionData.CodePath || '', versionData.CodePath || '',
changeTime, // 使用前端传来的时间或生成的当前时间 changeTime,
versionData.RunFreqGroup || '', versionData.RunFreqGroup || '',
versionData.RunNode || '', versionData.RunNode || '',
versionData.Priority || '0', versionData.Priority || '0',
@ -180,8 +215,10 @@ function saveModelVersion(versionData) {
versionData.DataPackageHeaderPath || '', versionData.DataPackageHeaderPath || '',
versionData.DataPackageEntryPoint || '', versionData.DataPackageEntryPoint || '',
versionData.DataPackageInterfaceName || '', versionData.DataPackageInterfaceName || '',
versionData.ProductName,
versionData.ClassName, versionData.ClassName,
versionData.originalVersion || versionData.Version versionData.originalVersion || versionData.Version,
versionData.ProductName
); );
db.close(); db.close();
@ -208,8 +245,8 @@ function saveNewVersion(db, versionData) {
// 检查版本是否已存在 // 检查版本是否已存在
const existingVersion = db.prepare(` const existingVersion = db.prepare(`
SELECT COUNT(*) as count FROM 'XNModelsVersion' SELECT COUNT(*) as count FROM 'XNModelsVersion'
WHERE ClassName = ? AND Version = ? WHERE ClassName = ? AND Version = ? AND ProductName = ?
`).get(versionData.ClassName, versionData.Version); `).get(versionData.ClassName, versionData.Version, versionData.ProductName);
if (existingVersion.count > 0) { if (existingVersion.count > 0) {
db.close(); db.close();
@ -233,19 +270,20 @@ function saveNewVersion(db, versionData) {
// 插入新版本 // 插入新版本
const insertResult = db.prepare(` const insertResult = db.prepare(`
INSERT INTO 'XNModelsVersion' ( INSERT INTO 'XNModelsVersion' (
ClassName, Name, Version, CodePath, Author, Description, ProductName, ClassName, Name, Version, CodePath, Author, Description,
CreatTime, ChangeTime, RunFreqGroup, RunNode, Priority, CreatTime, ChangeTime, RunFreqGroup, RunNode, Priority,
DataPackagePath, DataPackageHeaderPath, DataPackageEntryPoint, DataPackageInterfaceName DataPackagePath, DataPackageHeaderPath, DataPackageEntryPoint, DataPackageInterfaceName
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run( `).run(
versionData.ProductName,
versionData.ClassName, versionData.ClassName,
versionData.Name, versionData.Name,
versionData.Version, versionData.Version,
versionData.CodePath || '', versionData.CodePath || '',
versionData.Author, versionData.Author,
versionData.Description || '', versionData.Description || '',
createTime, // 使用前端传来的创建时间或生成的当前时间 createTime,
changeTime, // 使用前端传来的修改时间或生成的当前时间 changeTime,
versionData.RunFreqGroup || '', versionData.RunFreqGroup || '',
versionData.RunNode || '', versionData.RunNode || '',
versionData.Priority || '0', versionData.Priority || '0',
@ -563,6 +601,38 @@ function createService(serviceData) {
} }
} }
// 查询Products表中的所有产品
function getProducts() {
try {
const xnCorePath = getXNCorePath();
if (!xnCorePath) {
throw new Error('XNCore环境变量未设置无法获取数据库路径');
}
const dbPath = xnCorePath + '/database/XNSim.db';
if (!dbPath) {
throw new Error('无法找到数据库文件');
}
// 打开数据库连接
const db = new Database(dbPath, { readonly: true });
// 查询所有产品
const products = db.prepare(`
SELECT ProductName, Description
FROM 'Products'
ORDER BY ProductName
`).all();
db.close();
return products;
} catch (error) {
console.error('获取产品列表数据失败:', error.message);
throw error;
}
}
module.exports = { module.exports = {
getATAChapters, getATAChapters,
getModelsByChapterId, getModelsByChapterId,
@ -571,5 +641,6 @@ module.exports = {
getServices, getServices,
getServiceVersionsByClassName, getServiceVersionsByClassName,
saveServiceVersion, saveServiceVersion,
createService createService,
getProducts
}; };