待办事项暂未完全实现

This commit is contained in:
jinchao 2025-05-08 17:01:57 +08:00
parent e03da9a405
commit 4ff8b0b7a0
12 changed files with 645 additions and 9 deletions

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -144,6 +144,9 @@ class ContentArea extends HTMLElement {
case 'system-log': case 'system-log':
contentElement = document.createElement('system-log'); contentElement = document.createElement('system-log');
break; break;
case 'todo':
contentElement = document.createElement('todo-component');
break;
default: default:
contentElement = document.createElement('div'); contentElement = document.createElement('div');
contentElement.textContent = '正在开发中...'; contentElement.textContent = '正在开发中...';

View File

@ -154,9 +154,9 @@ class SubToolbar extends HTMLElement {
<img src="assets/icons/png/clock.png" alt="更新记录" class="icon"> <img src="assets/icons/png/clock.png" alt="更新记录" class="icon">
更新记录 更新记录
</div> </div>
<div class="sub-item" data-icon="server"> <div class="sub-item" data-icon="check-square">
<img src="assets/icons/png/server.png" alt="系统信息" class="icon"> <img src="assets/icons/png/check-square.png" alt="待办事项" class="icon">
系统信息 待办事项
</div> </div>
<div class="sub-item" data-icon="help"> <div class="sub-item" data-icon="help">
<img src="assets/icons/png/help.png" alt="帮助" class="icon"> <img src="assets/icons/png/help.png" alt="帮助" class="icon">
@ -214,6 +214,10 @@ class SubToolbar extends HTMLElement {
</div> </div>
<!-- 监控子菜单 --> <!-- 监控子菜单 -->
<div class="sub-menu" data-parent="monitor"> <div class="sub-menu" data-parent="monitor">
<div class="sub-item" data-icon="server">
<img src="assets/icons/png/server.png" alt="资源监控" class="icon">
资源监控
</div>
<div class="sub-item" data-icon="desktop"> <div class="sub-item" data-icon="desktop">
<img src="assets/icons/png/desktop.png" alt="仿真监控" class="icon"> <img src="assets/icons/png/desktop.png" alt="仿真监控" class="icon">
仿真监控 仿真监控

View File

@ -0,0 +1,311 @@
class TodoComponent extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.todos = [];
this.selectedProject = null;
this.selectedSubproject = null;
this.expandedProjects = new Set();
}
async fetchTodos() {
try {
const response = await fetch('/api/todos');
if (!response.ok) {
throw new Error('获取待办事项失败');
}
this.todos = await response.json();
this.render();
} catch (error) {
console.error('获取待办事项时发生错误:', error);
this.showError('获取待办事项失败,请稍后重试');
}
}
showError(message) {
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message';
errorDiv.textContent = message;
this.shadowRoot.appendChild(errorDiv);
}
// 构建项目树结构
buildProjectTree() {
const tree = {};
this.todos.forEach(todo => {
const project = todo.project || '其它';
const subproject = todo.subproject || '其它';
if (!tree[project]) {
tree[project] = {
name: project,
subprojects: {}
};
}
if (!tree[project].subprojects[subproject]) {
tree[project].subprojects[subproject] = {
name: subproject,
todos: []
};
}
tree[project].subprojects[subproject].todos.push(todo);
});
return tree;
}
// 渲染项目树
renderProjectTree(tree) {
const treeContainer = document.createElement('div');
treeContainer.className = 'project-tree';
Object.values(tree).forEach(project => {
const projectNode = document.createElement('div');
projectNode.className = 'project-node';
const projectHeader = document.createElement('div');
projectHeader.className = 'project-header';
projectHeader.innerHTML = `
<div class="project-header-content">
<span class="expand-icon ${this.expandedProjects.has(project.name) ? 'expanded' : ''}"></span>
<span class="project-name">${project.name}</span>
<span class="todo-count">(${Object.values(project.subprojects).reduce((sum, sub) =>
sum + sub.todos.length, 0)})</span>
</div>
`;
projectHeader.addEventListener('click', () => {
this.selectedProject = project.name;
this.selectedSubproject = null;
this.render();
});
const expandButton = projectHeader.querySelector('.expand-icon');
expandButton.addEventListener('click', (e) => {
e.stopPropagation();
if (this.expandedProjects.has(project.name)) {
this.expandedProjects.delete(project.name);
} else {
this.expandedProjects.add(project.name);
}
this.render();
});
const subprojectsContainer = document.createElement('div');
subprojectsContainer.className = 'subprojects-container';
subprojectsContainer.style.display = this.expandedProjects.has(project.name) ? 'block' : 'none';
Object.values(project.subprojects).forEach(subproject => {
const subprojectNode = document.createElement('div');
subprojectNode.className = 'subproject-node';
subprojectNode.innerHTML = `
<span class="subproject-name">${subproject.name}</span>
<span class="todo-count">(${subproject.todos.length})</span>
`;
subprojectNode.addEventListener('click', (e) => {
e.stopPropagation();
this.selectedProject = project.name;
this.selectedSubproject = subproject.name;
this.render();
});
subprojectsContainer.appendChild(subprojectNode);
});
projectNode.appendChild(projectHeader);
projectNode.appendChild(subprojectsContainer);
treeContainer.appendChild(projectNode);
});
return treeContainer;
}
// 过滤待办事项
filterTodos() {
if (!this.selectedProject) {
return this.todos;
}
return this.todos.filter(todo => {
const matchesProject = todo.project === this.selectedProject;
if (!this.selectedSubproject) {
return matchesProject;
}
return matchesProject && todo.subproject === this.selectedSubproject;
});
}
render() {
// 清空现有内容
this.shadowRoot.innerHTML = '';
// 添加样式
const style = document.createElement('style');
style.textContent = `
.container {
display: flex;
height: 100%;
font-family: Arial, sans-serif;
}
.project-tree {
width: 250px;
background: #f8f9fa;
border-right: 1px solid #e9ecef;
padding: 15px;
overflow-y: auto;
}
.todo-container {
flex: 1;
padding: 20px;
overflow-y: auto;
}
.project-node {
margin-bottom: 10px;
}
.project-header {
padding: 8px;
background: #e9ecef;
border-radius: 4px;
cursor: pointer;
}
.project-header-content {
display: flex;
align-items: center;
gap: 8px;
}
.project-header:hover {
background: #dee2e6;
}
.expand-icon {
font-size: 12px;
transition: transform 0.2s;
color: #495057;
}
.expand-icon.expanded {
transform: rotate(90deg);
}
.subprojects-container {
margin-left: 20px;
margin-top: 5px;
}
.subproject-node {
padding: 6px 8px;
cursor: pointer;
border-radius: 4px;
display: flex;
justify-content: space-between;
align-items: center;
}
.subproject-node:hover {
background: #e9ecef;
}
.todo-count {
font-size: 0.9em;
color: #6c757d;
}
.todo-item {
background: #fff;
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
.todo-item h3 {
margin: 0 0 10px 0;
color: #212529;
}
.todo-item p {
margin: 5px 0;
color: #495057;
}
.todo-item .status {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 0.9em;
margin-top: 10px;
}
.todo-item .status.completed {
background: #d4edda;
color: #155724;
}
.todo-item .status.pending {
background: #fff3cd;
color: #856404;
}
.todo-item .meta {
font-size: 0.85em;
color: #6c757d;
margin-top: 10px;
border-top: 1px solid #e9ecef;
padding-top: 10px;
}
.error-message {
color: #721c24;
padding: 10px;
background: #f8d7da;
border-radius: 4px;
margin: 10px;
}
.selected {
background: #cfe2ff !important;
}
`;
this.shadowRoot.appendChild(style);
// 创建主容器
const container = document.createElement('div');
container.className = 'container';
// 构建并渲染项目树
const tree = this.buildProjectTree();
const treeContainer = this.renderProjectTree(tree);
container.appendChild(treeContainer);
// 创建待办事项列表容器
const todosContainer = document.createElement('div');
todosContainer.className = 'todo-container';
// 渲染过滤后的待办事项
const filteredTodos = this.filterTodos();
filteredTodos.forEach(todo => {
const todoElement = document.createElement('div');
todoElement.className = 'todo-item';
// 格式化日期
const createdDate = new Date(todo.created_at).toLocaleString('zh-CN');
const scheDate = new Date(todo.sche_time).toLocaleString('zh-CN');
const completeDate = todo.complete_time ?
new Date(todo.complete_time).toLocaleString('zh-CN') : '未完成';
todoElement.innerHTML = `
<h3>${todo.title}</h3>
<p>${todo.text || '无详细描述'}</p>
<p>项目${todo.project}</p>
<p>子项目${todo.subproject}</p>
<p>执行人${todo.exeuser || '未分配'}</p>
<p>计划时间${scheDate}</p>
<div class="status ${todo.completed ? 'completed' : 'pending'}">
${todo.completed ? '已完成' : '进行中'}
</div>
<div class="meta">
<p>创建人${todo.adduser}</p>
<p>创建时间${createdDate}</p>
<p>完成时间${completeDate}</p>
</div>
`;
todosContainer.appendChild(todoElement);
});
container.appendChild(todosContainer);
this.shadowRoot.appendChild(container);
}
connectedCallback() {
// 组件被添加到DOM时获取数据
this.fetchTodos();
}
}
customElements.define('todo-component', TodoComponent);

0
XNSimHtml/data/xnsim.db Normal file
View File

View File

@ -32,6 +32,9 @@
<script src="components/user-management.js"></script> <script src="components/user-management.js"></script>
<script src="components/model-development.js"></script> <script src="components/model-development.js"></script>
<script src="components/service-development.js"></script> <script src="components/service-development.js"></script>
<script src="components/header-tools.js"></script>
<script src="components/qa-component.js"></script>
<script src="components/todo-component.js" type="module"></script>
<style> <style>
.icon { .icon {
width: 24px; width: 24px;
@ -206,8 +209,6 @@
</div> </div>
</div> </div>
<script src="components/header-tools.js"></script>
<script src="components/qa-component.js"></script>
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const tabsContainer = document.querySelector('tabs-container'); const tabsContainer = document.querySelector('tabs-container');
@ -317,10 +318,11 @@
return; return;
} }
// 处理系统信息标签页 // 处理资源监控标签页
if (title === '系统信息') { if (title === '资源监控') {
const id = 'system-info'; const id = 'system-info';
tabsContainer.createTab(id, title, icon, parentText, parentTool); tabsContainer.createTab(id, title, icon, parentText, parentTool);
contentArea.loadContent(id);
return; return;
} }
@ -332,6 +334,14 @@
return; return;
} }
// 处理待办事项标签页
if (title === '待办事项') {
const id = 'todo';
tabsContainer.createTab(id, title, icon, parentText, parentTool);
contentArea.loadContent(id);
return;
}
// 处理帮助标签页 // 处理帮助标签页
if (title === '帮助') { if (title === '帮助') {
const id = 'help'; const id = 'help';
@ -457,7 +467,7 @@
'概览': 'dashboard', '概览': 'dashboard',
'更新记录': 'clock', '更新记录': 'clock',
'运行日志': 'file', '运行日志': 'file',
'系统信息': 'server', '资源监控': 'server',
'帮助': 'help', '帮助': 'help',
'Q&A': 'question', 'Q&A': 'question',
'运行环境配置': 'chip', '运行环境配置': 'chip',

50
XNSimHtml/routes/todos.js Normal file
View File

@ -0,0 +1,50 @@
const express = require('express');
const router = express.Router();
const { getTodos, addTodo, updateTodoStatus, deleteTodo } = require('../utils/db-utils');
// 获取所有待办事项
router.get('/', async (req, res) => {
try {
const todos = await getTodos();
res.json(todos);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 添加新的待办事项
router.post('/', async (req, res) => {
try {
const todoData = req.body;
const result = await addTodo(todoData);
res.json(result);
} catch (error) {
console.error('添加待办事项失败:', error);
res.status(500).json({ error: error.message });
}
});
// 更新待办事项状态
router.put('/:id', async (req, res) => {
try {
const { id } = req.params;
const { title, text, exeuser, sche_time, completed } = req.body;
const result = await updateTodoStatus(id, completed, exeuser, title, text, sche_time);
res.json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// 删除待办事项
router.delete('/:id', async (req, res) => {
try {
const { id } = req.params;
const result = await deleteTodo(id);
res.json(result);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
module.exports = router;

22
XNSimHtml/routes/users.js Normal file
View File

@ -0,0 +1,22 @@
const express = require('express');
const router = express.Router();
const { getUsers } = require('../utils/db-utils');
// 获取所有用户信息
router.get('/users', async (req, res) => {
try {
const users = await getUsers();
res.json({
success: true,
users: users
});
} catch (error) {
console.error('获取用户信息失败:', error);
res.status(500).json({
success: false,
message: '获取用户信息失败'
});
}
});
module.exports = router;

View File

@ -22,6 +22,8 @@ const productsRoutes = require('./routes/products');
const interfaceRoutes = require('./routes/interface-config'); const interfaceRoutes = require('./routes/interface-config');
const icdImportRoutes = require('./routes/icd-import'); const icdImportRoutes = require('./routes/icd-import');
const qaRoutes = require('./routes/qa'); const qaRoutes = require('./routes/qa');
const todoRoutes = require('./routes/todos');
const userRoutes = require('./routes/users');
const app = express(); const app = express();
const PORT = process.env.PORT || 3000; const PORT = process.env.PORT || 3000;
@ -75,6 +77,8 @@ app.use('/api', productsRoutes);
app.use('/api/interface', interfaceRoutes); app.use('/api/interface', interfaceRoutes);
app.use('/api/icd', icdImportRoutes); app.use('/api/icd', icdImportRoutes);
app.use('/api/qa', qaRoutes); app.use('/api/qa', qaRoutes);
app.use('/api/todos', todoRoutes);
app.use('/api', userRoutes);
// 主页路由 // 主页路由
app.get('/', (req, res) => { app.get('/', (req, res) => {

View File

@ -1184,6 +1184,233 @@ function deleteAnswer(answerId) {
} }
} }
// 获取所有待办事项
function getTodos() {
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 });
// 创建todos表如果不存在
db.prepare(`
CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project TEXT NOT NULL DEFAULT '其它',
subproject TEXT NOT NULL DEFAULT '其它',
title TEXT NOT NULL,
text TEXT,
adduser TEXT NOT NULL,
exeuser TEXT,
completed BOOLEAN DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
sche_time DATETIME DEFAULT CURRENT_TIMESTAMP,
complete_time DATETIME DEFAULT CURRENT_TIMESTAMP
)
`).run();
const todos = db.prepare('SELECT * FROM todos ORDER BY created_at DESC').all();
db.close();
return todos;
} catch (error) {
console.error('获取待办事项失败:', error);
throw error;
}
}
// 添加待办事项
function addTodo(todoData) {
try {
if (!todoData.title) {
throw new Error('待办事项标题不能为空');
}
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);
const result = db.prepare(`
INSERT INTO todos (
project, subproject, title, text, adduser,
completed, created_at, sche_time
) VALUES (?, ?, ?, ?, ?, 0, CURRENT_TIMESTAMP, ?)
`).run(
todoData.project || '其它',
todoData.subproject || '其它',
todoData.title,
todoData.text || '',
todoData.adduser || '系统',
todoData.sche_time || new Date().toISOString()
);
db.close();
if (result.changes > 0) {
return {
success: true,
id: result.lastInsertRowid,
message: '待办事项添加成功'
};
} else {
throw new Error('待办事项添加失败');
}
} catch (error) {
console.error('添加待办事项失败:', error);
throw error;
}
}
// 更新待办事项状态
function updateTodoStatus(id, completed, exeuser, title, text, sche_time) {
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);
const result = db.prepare(`
UPDATE todos
SET completed = ?,
exeuser = ?,
title = ?,
text = ?,
sche_time = ?,
complete_time = CASE WHEN ? = 1 THEN CURRENT_TIMESTAMP ELSE complete_time END
WHERE id = ?
`).run(
completed ? 1 : 0,
exeuser || null,
title,
text || '',
sche_time || null,
completed ? 1 : 0,
id
);
db.close();
if (result.changes > 0) {
return {
success: true,
message: '待办事项更新成功'
};
} else {
throw new Error('待办事项不存在');
}
} catch (error) {
console.error('更新待办事项失败:', error);
throw error;
}
}
// 删除待办事项
function deleteTodo(id) {
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);
const result = db.prepare('DELETE FROM todos WHERE id = ?').run(id);
db.close();
return {
success: true,
message: result.changes > 0 ? '待办事项删除成功' : '待办事项不存在或已被删除'
};
} catch (error) {
console.error('删除待办事项失败:', error);
throw error;
}
}
// 获取所有用户信息
function getUsers() {
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 });
// 创建users表如果不存在
db.prepare(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
access_level INTEGER DEFAULT 1,
full_name TEXT,
phone TEXT,
email TEXT,
department TEXT,
position TEXT
)
`).run();
const users = db.prepare(`
SELECT
id,
username,
access_level,
full_name,
phone,
email,
department,
position
FROM users
ORDER BY id ASC
`).all();
db.close();
return users;
} catch (error) {
console.error('获取用户信息失败:', error);
throw error;
}
}
module.exports = { module.exports = {
getATAChapters, getATAChapters,
getModelsByChapterId, getModelsByChapterId,
@ -1203,5 +1430,10 @@ module.exports = {
createQuestion, createQuestion,
addAnswer, addAnswer,
deleteQuestion, deleteQuestion,
deleteAnswer deleteAnswer,
getTodos,
addTodo,
updateTodoStatus,
deleteTodo,
getUsers
}; };