基于 Electron + FastAPI 的目标检测系统前端说明文档(二)
本文介绍了一个基于Electron和原生JavaScript构建的目标检测系统桌面应用。系统采用SPA架构,通过PageManager管理页面切换,包含图像检测、视频检测、系统日志等功能模块。前端实现包括:1)基于Material Design的UI组件系统;2)CSS变量统一管理设计规范;3)性能优化策略如图片懒加载、防抖节流;4)安全措施包括CSP策略和XSS防护。系统遵循ES6+规范,提供完
1. 项目概述
本项目是一个基于 Electron + 原生 JavaScript 构建的桌面应用程序,用于目标检测系统的前端交互界面。

核心技术组成:
-
桌面框架: Electron v33.0.0
-
前端语言: 原生 JavaScript (ES6+)
-
样式方案: 原生 CSS3 (无预处理器)
-
UI 设计规范: Google Material Design 3
-
后端通信: Fetch API / HTTP
5. 核心代码实现
5.1 页面管理器(Page Manager)
系统采用单页应用(SPA)架构,通过 PageManager 管理页面切换:
const PageManager = {
currentPage: null,
pages: {
'image-detection': ImageDetectionPage,
'video-detection': VideoDetectionPage,
'system-logs': SystemLogsPage,
'system-monitor': SystemMonitorPage,
'llm-chat': LLMChatPage,
'multimodal-chat': MultimodalChatPage
},
navigate(pageName) {
// 清理当前页面
if (this.currentPage && this.currentPage.destroy) {
this.currentPage.destroy();
}
// 渲染新页面
const PageClass = this.pages[pageName];
if (PageClass) {
this.currentPage = new PageClass();
this.currentPage.render();
}
}
};
5.2 图像检测页面实现
function ImageDetectionPage() {
this.images = [];
this.detectedImages = [];
this.selectedImages = new Set();
}
ImageDetectionPage.prototype = {
async render() {
const container = document.getElementById('page-container');
container.innerHTML = `
<div class="card">
<div class="card-header">
<span class="card-title">上传图像</span>
</div>
<div class="upload-area" id="upload-area">
<div class="upload-icon">📤</div>
<div class="upload-text">点击或拖拽图像到此处上传</div>
<input type="file" id="file-input" multiple accept=".png,.jpg,.jpeg,.gif,.bmp" style="display:none">
</div>
</div>
<div class="card">
<div class="card-header">
<span class="card-title">已上传图像</span>
<div class="card-actions">
<button class="btn btn-secondary btn-sm" οnclick="imageDetectionPage.selectAll()">全选</button>
<button class="btn btn-secondary btn-sm" οnclick="imageDetectionPage.detectSelected()">批量检测</button>
<button class="btn btn-danger btn-sm" οnclick="imageDetectionPage.deleteSelected()">批量删除</button>
</div>
</div>
<div id="image-grid" class="media-grid"></div>
</div>
`;
5.3 模态框系统
const Utils = {
previousActiveElement: null,
createModal(title, content, footer = '') {
// 保存当前焦点元素
this.previousActiveElement = document.activeElement;
const container = document.getElementById('modal-container');
container.innerHTML = `
<div class="modal-overlay active" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<div class="modal" tabindex="-1">
<div class="modal-header">
<h3 id="modal-title" class="modal-title">${title}</h3>
<button class="modal-close" οnclick="Utils.closeModal()" aria-label="关闭">×</button>
</div>
<div class="modal-body">${content}</div>
${footer ? `<div class="modal-footer">${footer}</div>` : ''}
</div>
</div>
`;
6. 样式系统详解
6.1 CSS 变量系统
所有设计 Token 通过 CSS 变量统一管理:
:root {
/* 颜色 */
--primary-color: #6200EE;
--background-color: #FAFAFA;
--surface-color: #FFFFFF;
/* 文本 */
--on-surface: #1C1B1F;
--on-surface-medium: #49454F;
/* 圆角 */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
/* 间距 */
--sidebar-width: 280px;
--header-height: 72px;
}
6.2 组件样式规范
6.2.1 按钮(Button)
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 12px 28px;
border: 2px solid transparent;
border-radius: var(--radius-md);
font-size: 16px;
font-weight: 600;
min-height: 48px;
cursor: pointer;
transition: var(--transition-fast);
position: relative;
overflow: hidden;
}
/* 涟漪效果 */
.btn::after {
content: '';
position: absolute;
inset: 0;
background: currentColor;
opacity: 0;
transition: var(--transition-fast);
}
.btn:hover::after {
opacity: 0.08;
}
.btn:active::after {
opacity: 0.12;
}
6.2.2 卡片(Card)
.card {
background: var(--surface-color);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-1);
padding: 24px;
margin-bottom: 24px;
transition: var(--transition-normal);
}
.card:hover {
box-shadow: var(--shadow-2);
}
6.2.3 表单元素(Form)
.form-input {
width: 100%;
padding: 14px 16px;
border: 2px solid var(--outline-color);
border-radius: var(--radius-md);
font-size: 16px;
transition: var(--transition-fast);
}
.form-input:focus {
outline: 3px solid var(--primary-color);
outline-offset: 2px;
border-color: var(--primary-color);
box-shadow: 0 0 0 4px rgba(98, 0, 238, 0.1);
}
7. 性能优化策略
7.1 图片懒加载
renderImages() {
const grid = document.getElementById('image-grid');
grid.innerHTML = this.images.map(img => `
<div class="media-item">
<img src="${API_BASE}${img.url}"
alt="${img.filename}"
loading="lazy"
οnclick="imageDetectionPage.preview('${img.filename}')">
</div>
`).join('');
}
7.2 防抖与节流
// 防抖(Debounce)- 用于搜索输入
function debounce(func, wait) {
let timeout;
return function(...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
// 节流(Throttle)- 用于滚动事件
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
7.3 虚拟滚动(大数据列表)
对于超过 100 项的列表,建议实现虚拟滚动:
class VirtualScroller {
constructor(container, items, itemHeight) {
this.container = container;
this.items = items;
this.itemHeight = itemHeight;
this.visibleCount = Math.ceil(container.clientHeight / itemHeight);
this.render();
}
render() {
const scrollTop = this.container.scrollTop;
const startIndex = Math.floor(scrollTop / this.itemHeight);
const endIndex = startIndex + this.visibleCount;
const visibleItems = this.items.slice(startIndex, endIndex);
// 渲染可见项...
}
}
8. 安全性考虑
8.1 内容安全策略(CSP)
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: http://localhost:10077; media-src 'self' http://localhost:10077; connect-src 'self' http://localhost:10077">
8.2 XSS 防护
所有用户输入必须经过转义:
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, m => map[m]);
}
9. 开发与部署
9.1 开发环境启动
# 安装依赖 npm install # 启动 Electron 应用 npm run dev
9.2 生产构建
# 打包应用(需配置 electron-builder) npm run build
9.3 调试技巧
-
开发者工具:
Ctrl+Shift+I(Windows/Linux) 或Cmd+Option+I(Mac) -
重新加载:
Ctrl+R或Cmd+R -
控制台日志: 所有 API 错误会自动输出到控制台
10. 最佳实践总结
10.1 代码规范
-
使用 ES6+ 语法(箭头函数、解构、模板字符串)
-
所有异步操作使用
async/await -
函数命名采用驼峰命名法(camelCase)
-
类名采用帕斯卡命名法(PascalCase)
10.2 性能建议
-
避免在循环中进行 DOM 操作
-
使用事件委托处理大量元素的事件
-
图片资源使用 WebP 格式
-
启用浏览器缓存
10.3 可维护性
-
每个页面独立封装为一个类
-
工具函数统一放在
Utils对象中 -
CSS 变量集中管理设计 Token
-
API 调用统一封装
11. 常见问题(FAQ)
Q1: 如何添加新页面?
-
在
renderer.js中创建新的页面类 -
在
PageManager.pages中注册 -
在
index.html的导航菜单中添加入口
Q2: 如何修改主题颜色?
修改 styles.css 中的 CSS 变量:
:root {
--primary-color: #YOUR_COLOR;
}
Q3: 如何调用新的后端接口?
参考 Utils.request 方法,示例:
const result = await Utils.request('/api/your-endpoint', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ data: 'value' })
});
更多推荐
所有评论(0)