10.1 项目规划与架构设计
10.1.1 项目需求分析
在开始项目开发之前,我们需要进行详细的需求分析和架构设计。本章将通过一个完整的桌面应用项目来演示Sciter的实际应用。
// 项目需求管理器
namespace ProjectPlanning {
// 需求分析器
class RequirementAnalyzer {
function this() {
this.requirements = new Map();
this.stakeholders = [];
this.priorities = ['low', 'medium', 'high', 'critical'];
this.categories = ['functional', 'non-functional', 'technical', 'business'];
}
// 添加需求
function addRequirement(id, requirement) {
this.requirements.set(id, {
id: id,
title: requirement.title,
description: requirement.description,
category: requirement.category || 'functional',
priority: requirement.priority || 'medium',
stakeholder: requirement.stakeholder,
acceptanceCriteria: requirement.acceptanceCriteria || [],
dependencies: requirement.dependencies || [],
estimatedEffort: requirement.estimatedEffort || 0,
status: 'pending',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
});
stdout.println(`需求已添加: ${id} - ${requirement.title}`);
}
// 更新需求状态
function updateRequirementStatus(id, status) {
var requirement = this.requirements.get(id);
if (requirement) {
requirement.status = status;
requirement.updatedAt = new Date().toISOString();
stdout.println(`需求状态已更新: ${id} -> ${status}`);
}
}
// 获取需求列表
function getRequirements(filter = null) {
var requirements = Array.from(this.requirements.values());
if (filter) {
requirements = requirements.filter(req => {
if (filter.category && req.category !== filter.category) return false;
if (filter.priority && req.priority !== filter.priority) return false;
if (filter.status && req.status !== filter.status) return false;
if (filter.stakeholder && req.stakeholder !== filter.stakeholder) return false;
return true;
});
}
return requirements;
}
// 生成需求报告
function generateReport() {
var requirements = Array.from(this.requirements.values());
var report = {
totalRequirements: requirements.length,
byCategory: {},
byPriority: {},
byStatus: {},
totalEffort: 0
};
// 统计分类
for (var category of this.categories) {
report.byCategory[category] = requirements.filter(req => req.category === category).length;
}
// 统计优先级
for (var priority of this.priorities) {
report.byPriority[priority] = requirements.filter(req => req.priority === priority).length;
}
// 统计状态
var statuses = ['pending', 'in-progress', 'completed', 'cancelled'];
for (var status of statuses) {
report.byStatus[status] = requirements.filter(req => req.status === status).length;
}
// 计算总工作量
report.totalEffort = requirements.reduce((sum, req) => sum + req.estimatedEffort, 0);
return report;
}
}
// 架构设计器
class ArchitectureDesigner {
function this() {
this.components = new Map();
this.layers = ['presentation', 'business', 'data', 'infrastructure'];
this.patterns = ['mvc', 'mvp', 'mvvm', 'component-based'];
this.technologies = [];
}
// 添加组件
function addComponent(id, component) {
this.components.set(id, {
id: id,
name: component.name,
type: component.type,
layer: component.layer,
responsibilities: component.responsibilities || [],
dependencies: component.dependencies || [],
interfaces: component.interfaces || [],
technologies: component.technologies || [],
description: component.description
});
stdout.println(`组件已添加: ${id} - ${component.name}`);
}
// 定义组件依赖
function addDependency(fromComponent, toComponent, type = 'uses') {
var component = this.components.get(fromComponent);
if (component) {
component.dependencies.push({
target: toComponent,
type: type,
description: `${fromComponent} ${type} ${toComponent}`
});
stdout.println(`依赖已添加: ${fromComponent} -> ${toComponent}`);
}
}
// 验证架构
function validateArchitecture() {
var issues = [];
// 检查循环依赖
var cycles = this.detectCycles();
if (cycles.length > 0) {
issues.push({
type: 'circular-dependency',
message: '检测到循环依赖',
details: cycles
});
}
// 检查层级违规
var layerViolations = this.checkLayerViolations();
if (layerViolations.length > 0) {
issues.push({
type: 'layer-violation',
message: '检测到层级违规',
details: layerViolations
});
}
// 检查孤立组件
var orphanComponents = this.findOrphanComponents();
if (orphanComponents.length > 0) {
issues.push({
type: 'orphan-components',
message: '发现孤立组件',
details: orphanComponents
});
}
return {
isValid: issues.length === 0,
issues: issues
};
}
// 检测循环依赖
function detectCycles() {
var visited = new Set();
var recursionStack = new Set();
var cycles = [];
for (var [componentId] of this.components) {
if (!visited.has(componentId)) {
this.detectCyclesHelper(componentId, visited, recursionStack, [], cycles);
}
}
return cycles;
}
// 循环依赖检测辅助方法
function detectCyclesHelper(componentId, visited, recursionStack, path, cycles) {
visited.add(componentId);
recursionStack.add(componentId);
path.push(componentId);
var component = this.components.get(componentId);
if (component) {
for (var dependency of component.dependencies) {
var targetId = dependency.target;
if (!visited.has(targetId)) {
this.detectCyclesHelper(targetId, visited, recursionStack, path, cycles);
} else if (recursionStack.has(targetId)) {
// 找到循环
var cycleStart = path.indexOf(targetId);
var cycle = path.slice(cycleStart).concat([targetId]);
cycles.push(cycle);
}
}
}
path.pop();
recursionStack.delete(componentId);
}
// 检查层级违规
function checkLayerViolations() {
var violations = [];
var layerOrder = this.layers;
for (var [componentId, component] of this.components) {
var componentLayerIndex = layerOrder.indexOf(component.layer);
for (var dependency of component.dependencies) {
var targetComponent = this.components.get(dependency.target);
if (targetComponent) {
var targetLayerIndex = layerOrder.indexOf(targetComponent.layer);
// 上层不应该依赖下层
if (componentLayerIndex < targetLayerIndex) {
violations.push({
from: componentId,
to: dependency.target,
fromLayer: component.layer,
toLayer: targetComponent.layer,
message: `${component.layer}层组件不应依赖${targetComponent.layer}层组件`
});
}
}
}
}
return violations;
}
// 查找孤立组件
function findOrphanComponents() {
var referencedComponents = new Set();
// 收集所有被引用的组件
for (var [componentId, component] of this.components) {
for (var dependency of component.dependencies) {
referencedComponents.add(dependency.target);
}
}
// 查找没有被引用且没有依赖的组件
var orphans = [];
for (var [componentId, component] of this.components) {
if (!referencedComponents.has(componentId) && component.dependencies.length === 0) {
orphans.push(componentId);
}
}
return orphans;
}
// 生成架构图
function generateArchitectureDiagram() {
var diagram = {
nodes: [],
edges: [],
layers: {}
};
// 添加节点
for (var [componentId, component] of this.components) {
diagram.nodes.push({
id: componentId,
label: component.name,
layer: component.layer,
type: component.type
});
// 按层分组
if (!diagram.layers[component.layer]) {
diagram.layers[component.layer] = [];
}
diagram.layers[component.layer].push(componentId);
}
// 添加边
for (var [componentId, component] of this.components) {
for (var dependency of component.dependencies) {
diagram.edges.push({
from: componentId,
to: dependency.target,
type: dependency.type,
label: dependency.type
});
}
}
return diagram;
}
}
}
// 使用示例
function projectPlanningExample() {
// 需求分析示例
var analyzer = new ProjectPlanning.RequirementAnalyzer();
// 添加功能需求
analyzer.addRequirement('REQ-001', {
title: '用户登录功能',
description: '用户可以通过用户名和密码登录系统',
category: 'functional',
priority: 'high',
stakeholder: '最终用户',
acceptanceCriteria: [
'用户可以输入用户名和密码',
'系统验证用户凭据',
'登录成功后跳转到主界面',
'登录失败显示错误信息'
],
estimatedEffort: 8
});
analyzer.addRequirement('REQ-002', {
title: '数据可视化',
description: '以图表形式展示业务数据',
category: 'functional',
priority: 'medium',
stakeholder: '业务用户',
acceptanceCriteria: [
'支持多种图表类型',
'数据实时更新',
'支持交互操作'
],
estimatedEffort: 16
});
analyzer.addRequirement('REQ-003', {
title: '性能要求',
description: '应用启动时间不超过3秒',
category: 'non-functional',
priority: 'high',
stakeholder: '所有用户',
estimatedEffort: 12
});
// 生成需求报告
var report = analyzer.generateReport();
stdout.println('需求分析报告:', JSON.stringify(report, null, 2));
// 架构设计示例
var designer = new ProjectPlanning.ArchitectureDesigner();
// 添加组件
designer.addComponent('ui-layer', {
name: 'UI层',
type: 'layer',
layer: 'presentation',
responsibilities: ['用户界面', '用户交互', '数据展示'],
technologies: ['Sciter', 'CSS', 'JavaScript']
});
designer.addComponent('business-layer', {
name: '业务逻辑层',
type: 'layer',
layer: 'business',
responsibilities: ['业务规则', '数据处理', '业务流程'],
technologies: ['JavaScript', 'Business Rules Engine']
});
designer.addComponent('data-layer', {
name: '数据访问层',
type: 'layer',
layer: 'data',
responsibilities: ['数据存储', '数据检索', '数据持久化'],
technologies: ['SQLite', 'File System']
});
// 定义依赖关系
designer.addDependency('ui-layer', 'business-layer', 'calls');
designer.addDependency('business-layer', 'data-layer', 'uses');
// 验证架构
var validation = designer.validateArchitecture();
stdout.println('架构验证结果:', JSON.stringify(validation, null, 2));
// 生成架构图
var diagram = designer.generateArchitectureDiagram();
stdout.println('架构图:', JSON.stringify(diagram, null, 2));
stdout.println('项目规划示例完成');
}
10.2 完整项目开发
10.2.1 项目结构设计
// 项目结构管理器
namespace ProjectStructure {
// 项目生成器
class ProjectGenerator {
function this() {
this.templates = new Map();
this.projectTypes = ['desktop-app', 'utility-tool', 'dashboard', 'game'];
this.initializeTemplates();
}
// 初始化项目模板
function initializeTemplates() {
// 桌面应用模板
this.templates.set('desktop-app', {
name: '桌面应用',
structure: {
'src/': {
'components/': {
'common/': {},
'pages/': {},
'dialogs/': {}
},
'services/': {
'api/': {},
'data/': {},
'utils/': {}
},
'assets/': {
'images/': {},
'styles/': {},
'fonts/': {}
},
'config/': {},
'main.js': 'entry point',
'app.htm': 'main window'
},
'tests/': {
'unit/': {},
'integration/': {},
'e2e/': {}
},
'docs/': {
'api/': {},
'user-guide/': {}
},
'build/': {},
'dist/': {},
'package.json': 'project configuration',
'README.md': 'project documentation',
'.gitignore': 'git ignore rules'
},
files: {
'src/main.js': this.getMainJsTemplate(),
'src/app.htm': this.getAppHtmlTemplate(),
'package.json': this.getPackageJsonTemplate('desktop-app'),
'README.md': this.getReadmeTemplate()
}
});
// 工具应用模板
this.templates.set('utility-tool', {
name: '工具应用',
structure: {
'src/': {
'core/': {},
'ui/': {},
'plugins/': {},
'utils/': {},
'main.js': 'entry point',
'tool.htm': 'main interface'
},
'plugins/': {},
'config/': {},
'package.json': 'project configuration'
},
files: {
'src/main.js': this.getToolMainTemplate(),
'src/tool.htm': this.getToolHtmlTemplate(),
'package.json': this.getPackageJsonTemplate('utility-tool')
}
});
}
// 生成项目
function generateProject(projectName, projectType, options = {}) {
var template = this.templates.get(projectType);
if (!template) {
throw new Error(`未知的项目类型: ${projectType}`);
}
var project = {
name: projectName,
type: projectType,
structure: this.processStructure(template.structure, options),
files: this.processFiles(template.files, projectName, options),
metadata: {
createdAt: new Date().toISOString(),
generator: 'Sciter Project Generator',
version: '1.0.0'
}
};
stdout.println(`项目已生成: ${projectName} (${template.name})`);
return project;
}
// 处理项目结构
function processStructure(structure, options) {
var processed = {};
for (var key in structure) {
if (typeof structure[key] === 'object' && structure[key] !== null) {
processed[key] = this.processStructure(structure[key], options);
} else {
processed[key] = structure[key];
}
}
return processed;
}
// 处理项目文件
function processFiles(files, projectName, options) {
var processed = {};
for (var filePath in files) {
var content = files[filePath];
// 替换模板变量
content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
content = content.replace(/\{\{AUTHOR\}\}/g, options.author || 'Unknown');
content = content.replace(/\{\{DESCRIPTION\}\}/g, options.description || '');
content = content.replace(/\{\{VERSION\}\}/g, options.version || '1.0.0');
processed[filePath] = content;
}
return processed;
}
// 获取主入口文件模板
function getMainJsTemplate() {
return `// {{PROJECT_NAME}} - 主入口文件
// 作者: {{AUTHOR}}
// 版本: {{VERSION}}
include "src/components/common/app-framework.js";
include "src/services/api/api-client.js";
include "src/services/data/data-manager.js";
// 应用程序类
class Application {
function this() {
this.isInitialized = false;
this.config = null;
this.services = {};
}
// 初始化应用
function initialize() {
if (this.isInitialized) {
return;
}
try {
// 加载配置
this.loadConfiguration();
// 初始化服务
this.initializeServices();
// 设置全局错误处理
this.setupErrorHandling();
// 初始化UI
this.initializeUI();
this.isInitialized = true;
stdout.println('应用初始化完成');
} catch (error) {
stdout.println('应用初始化失败:', error.message);
throw error;
}
}
// 加载配置
function loadConfiguration() {
this.config = {
appName: '{{PROJECT_NAME}}',
version: '{{VERSION}}',
debug: true,
window: {
width: 1200,
height: 800,
resizable: true,
center: true
},
database: {
path: 'data/app.db',
autoCreate: true
}
};
}
// 初始化服务
function initializeServices() {
this.services.dataManager = new DataManager(this.config.database);
this.services.apiClient = new ApiClient(this.config.api);
}
// 设置错误处理
function setupErrorHandling() {
window.addEventListener('error', function(event) {
stdout.println('全局错误:', event.error.message);
// 这里可以添加错误报告逻辑
});
}
// 初始化UI
function initializeUI() {
// 设置窗口属性
if (this.config.window) {
var win = Window.this;
if (this.config.window.center) {
win.center();
}
}
// 初始化主界面
this.initializeMainInterface();
}
// 初始化主界面
function initializeMainInterface() {
// 这里添加主界面初始化逻辑
stdout.println('主界面初始化完成');
}
// 启动应用
function start() {
this.initialize();
stdout.println('{{PROJECT_NAME}} 已启动');
}
// 关闭应用
function shutdown() {
try {
// 清理资源
if (this.services.dataManager) {
this.services.dataManager.close();
}
stdout.println('应用已关闭');
} catch (error) {
stdout.println('关闭应用时发生错误:', error.message);
}
}
}
// 全局应用实例
var app = new Application();
// 文档就绪时启动应用
document.ready = function() {
app.start();
};
// 窗口关闭时清理资源
window.addEventListener('beforeunload', function() {
app.shutdown();
});`;
}
// 获取HTML模板
function getAppHtmlTemplate() {
return `<!DOCTYPE html>
<html>
<head>
<title>{{PROJECT_NAME}}</title>
<meta charset="utf-8">
<style>
@import url("src/assets/styles/main.css");
</style>
<script type="text/tiscript">
include "src/main.js";
</script>
</head>
<body>
<div id="app">
<header class="app-header">
<h1>{{PROJECT_NAME}}</h1>
<nav class="app-nav">
<!-- 导航菜单 -->
</nav>
</header>
<main class="app-main">
<aside class="app-sidebar">
<!-- 侧边栏 -->
</aside>
<section class="app-content">
<!-- 主要内容区域 -->
<div class="welcome-message">
<h2>欢迎使用 {{PROJECT_NAME}}</h2>
<p>这是一个基于Sciter构建的桌面应用程序。</p>
</div>
</section>
</main>
<footer class="app-footer">
<p>© 2024 {{PROJECT_NAME}} v{{VERSION}}</p>
</footer>
</div>
</body>
</html>`;
}
// 获取package.json模板
function getPackageJsonTemplate(projectType) {
return `{
"name": "{{PROJECT_NAME}}",
"version": "{{VERSION}}",
"description": "{{DESCRIPTION}}",
"main": "src/main.js",
"author": "{{AUTHOR}}",
"license": "MIT",
"scripts": {
"start": "sciter src/app.htm",
"build": "sciter-packager src/app.htm",
"test": "sciter-test tests/",
"lint": "eslint src/"
},
"dependencies": {
"sciter-js": "^5.0.0"
},
"devDependencies": {
"sciter-packager": "^1.0.0",
"sciter-test": "^1.0.0",
"eslint": "^8.0.0"
},
"keywords": [
"sciter",
"desktop-app",
"${projectType}"
]
}`;
}
// 获取README模板
function getReadmeTemplate() {
return `# {{PROJECT_NAME}}
{{DESCRIPTION}}
## 功能特性
- 现代化的用户界面
- 高性能的桌面应用
- 跨平台支持
- 易于扩展和维护
## 安装和运行
### 前置要求
- Sciter SDK
- Node.js (可选,用于构建工具)
### 运行应用
\`\`\`bash
# 直接运行
sciter src/app.htm
# 或使用npm脚本
npm start
\`\`\`
### 构建应用
\`\`\`bash
npm run build
\`\`\`
## 项目结构
\`\`\`
{{PROJECT_NAME}}/
├── src/ # 源代码
│ ├── components/ # 组件
│ ├── services/ # 服务层
│ ├── assets/ # 资源文件
│ ├── config/ # 配置文件
│ ├── main.js # 主入口
│ └── app.htm # 主窗口
├── tests/ # 测试文件
├── docs/ # 文档
├── build/ # 构建输出
└── package.json # 项目配置
\`\`\`
## 开发指南
### 添加新功能
1. 在 \`src/components/\` 中创建新组件
2. 在 \`src/services/\` 中添加相关服务
3. 更新主界面和路由
4. 添加相应的测试
### 代码规范
- 使用一致的代码风格
- 添加适当的注释
- 编写单元测试
- 遵循组件化开发原则
## 许可证
MIT License
## 作者
{{AUTHOR}}
## 版本历史
- v{{VERSION}} - 初始版本`;
}
// 获取工具应用主文件模板
function getToolMainTemplate() {
return `// {{PROJECT_NAME}} - 工具应用主文件
class ToolApplication {
function this() {
this.plugins = new Map();
this.config = {};
}
function initialize() {
this.loadPlugins();
this.setupUI();
}
function loadPlugins() {
// 加载插件逻辑
}
function setupUI() {
// 设置用户界面
}
}
var toolApp = new ToolApplication();
document.ready = function() {
toolApp.initialize();
};`;
}
// 获取工具应用HTML模板
function getToolHtmlTemplate() {
return `<!DOCTYPE html>
<html>
<head>
<title>{{PROJECT_NAME}}</title>
<script type="text/tiscript">
include "main.js";
</script>
</head>
<body>
<div id="tool-interface">
<h1>{{PROJECT_NAME}}</h1>
<!-- 工具界面 -->
</div>
</body>
</html>`;
}
// 创建项目文件
function createProjectFiles(project, basePath) {
var createdFiles = [];
try {
// 创建目录结构
this.createDirectoryStructure(project.structure, basePath);
// 创建文件
for (var filePath in project.files) {
var fullPath = `${basePath}/${filePath}`;
var content = project.files[filePath];
// 这里应该使用实际的文件写入API
// System.fs.writeFile(fullPath, content);
createdFiles.push(fullPath);
stdout.println(`文件已创建: ${fullPath}`);
}
stdout.println(`项目创建完成: ${createdFiles.length} 个文件`);
return createdFiles;
} catch (error) {
stdout.println('创建项目文件失败:', error.message);
throw error;
}
}
// 创建目录结构
function createDirectoryStructure(structure, basePath) {
for (var key in structure) {
if (key.endsWith('/')) {
// 这是一个目录
var dirPath = `${basePath}/${key}`;
// System.fs.createDirectory(dirPath);
if (typeof structure[key] === 'object') {
this.createDirectoryStructure(structure[key], dirPath.slice(0, -1));
}
}
}
}
}
}
// 使用示例
function projectStructureExample() {
var generator = new ProjectStructure.ProjectGenerator();
// 生成桌面应用项目
var project = generator.generateProject('MyDesktopApp', 'desktop-app', {
author: 'John Doe',
description: '一个功能强大的桌面应用程序',
version: '1.0.0'
});
stdout.println('生成的项目:', JSON.stringify(project.metadata, null, 2));
// 创建项目文件(在实际环境中)
// var createdFiles = generator.createProjectFiles(project, './my-desktop-app');
stdout.println('项目结构示例完成');
}
10.2.2 核心功能实现
// 核心功能模块
namespace CoreFeatures {
// 用户认证管理器
class AuthenticationManager {
function this() {
this.currentUser = null;
this.sessionTimeout = 30 * 60 * 1000; // 30分钟
this.sessionTimer = null;
this.loginAttempts = new Map();
this.maxLoginAttempts = 3;
this.lockoutDuration = 15 * 60 * 1000; // 15分钟
}
// 用户登录
function login(username, password) {
return new Promise((resolve, reject) => {
try {
// 检查账户是否被锁定
if (this.isAccountLocked(username)) {
reject(new Error('账户已被锁定,请稍后再试'));
return;
}
// 验证用户凭据
this.validateCredentials(username, password)
.then(user => {
// 登录成功
this.currentUser = user;
this.startSession();
this.clearLoginAttempts(username);
// 记录登录日志
this.logLoginEvent(username, 'success');
resolve({
success: true,
user: user,
token: this.generateSessionToken(user)
});
})
.catch(error => {
// 登录失败
this.recordFailedAttempt(username);
this.logLoginEvent(username, 'failure', error.message);
reject(error);
});
} catch (error) {
reject(error);
}
});
}
// 验证用户凭据
function validateCredentials(username, password) {
return new Promise((resolve, reject) => {
// 模拟异步验证过程
setTimeout(() => {
// 这里应该连接到实际的用户数据库
var users = [
{ id: 1, username: 'admin', password: 'admin123', role: 'administrator', name: '管理员' },
{ id: 2, username: 'user', password: 'user123', role: 'user', name: '普通用户' }
];
var user = users.find(u => u.username === username);
if (!user) {
reject(new Error('用户不存在'));
return;
}
// 在实际应用中,应该使用加密的密码比较
if (user.password !== password) {
reject(new Error('密码错误'));
return;
}
// 返回用户信息(不包含密码)
var { password: _, ...userInfo } = user;
resolve(userInfo);
}, 500);
});
}
// 检查账户是否被锁定
function isAccountLocked(username) {
var attempts = this.loginAttempts.get(username);
if (!attempts) {
return false;
}
var now = Date.now();
var recentAttempts = attempts.filter(time => now - time < this.lockoutDuration);
return recentAttempts.length >= this.maxLoginAttempts;
}
// 记录失败的登录尝试
function recordFailedAttempt(username) {
if (!this.loginAttempts.has(username)) {
this.loginAttempts.set(username, []);
}
this.loginAttempts.get(username).push(Date.now());
}
// 清除登录尝试记录
function clearLoginAttempts(username) {
this.loginAttempts.delete(username);
}
// 开始会话
function startSession() {
// 清除现有的会话定时器
if (this.sessionTimer) {
clearTimeout(this.sessionTimer);
}
// 设置会话超时
this.sessionTimer = setTimeout(() => {
this.logout('session_timeout');
}, this.sessionTimeout);
}
// 刷新会话
function refreshSession() {
if (this.currentUser) {
this.startSession();
}
}
// 生成会话令牌
function generateSessionToken(user) {
var payload = {
userId: user.id,
username: user.username,
role: user.role,
timestamp: Date.now()
};
// 简化的令牌生成(实际应用中应使用JWT等标准)
return btoa(JSON.stringify(payload));
}
// 用户登出
function logout(reason = 'user_action') {
if (this.currentUser) {
this.logLoginEvent(this.currentUser.username, 'logout', reason);
this.currentUser = null;
}
if (this.sessionTimer) {
clearTimeout(this.sessionTimer);
this.sessionTimer = null;
}
// 触发登出事件
document.dispatchEvent(new CustomEvent('user-logout', {
detail: { reason: reason }
}));
}
// 检查用户是否已登录
function isLoggedIn() {
return this.currentUser !== null;
}
// 获取当前用户
function getCurrentUser() {
return this.currentUser;
}
// 检查用户权限
function hasPermission(permission) {
if (!this.currentUser) {
return false;
}
// 管理员拥有所有权限
if (this.currentUser.role === 'administrator') {
return true;
}
// 这里应该实现更复杂的权限检查逻辑
var userPermissions = this.getUserPermissions(this.currentUser.role);
return userPermissions.includes(permission);
}
// 获取用户权限列表
function getUserPermissions(role) {
var permissions = {
'administrator': ['read', 'write', 'delete', 'admin'],
'user': ['read', 'write'],
'guest': ['read']
};
return permissions[role] || [];
}
// 记录登录事件
function logLoginEvent(username, event, details = '') {
var logEntry = {
timestamp: new Date().toISOString(),
username: username,
event: event,
details: details,
ip: 'localhost', // 在实际应用中获取真实IP
userAgent: navigator.userAgent
};
// 这里应该将日志保存到数据库或文件
stdout.println('登录日志:', JSON.stringify(logEntry));
}
}
// 数据管理器
class DataManager {
function this(config = {}) {
this.config = Object.assign({
database: 'app.db',
autoBackup: true,
backupInterval: 24 * 60 * 60 * 1000, // 24小时
maxBackups: 7
}, config);
this.connection = null;
this.cache = new Map();
this.backupTimer = null;
}
// 初始化数据库
function initialize() {
return new Promise((resolve, reject) => {
try {
// 这里应该使用实际的数据库连接
// this.connection = new SQLite.Database(this.config.database);
// 创建必要的表
this.createTables()
.then(() => {
// 启动自动备份
if (this.config.autoBackup) {
this.startAutoBackup();
}
stdout.println('数据库初始化完成');
resolve();
})
.catch(reject);
} catch (error) {
reject(error);
}
});
}
// 创建数据表
function createTables() {
return new Promise((resolve, reject) => {
var tables = [
{
name: 'users',
schema: `
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
email TEXT,
role TEXT DEFAULT 'user',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`
},
{
name: 'settings',
schema: `
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT,
type TEXT DEFAULT 'string',
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`
},
{
name: 'logs',
schema: `
CREATE TABLE IF NOT EXISTS logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
level TEXT NOT NULL,
message TEXT NOT NULL,
data TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
`
}
];
// 模拟表创建过程
setTimeout(() => {
stdout.println(`已创建 ${tables.length} 个数据表`);
resolve();
}, 100);
});
}
// 查询数据
function query(sql, params = []) {
return new Promise((resolve, reject) => {
try {
// 检查缓存
var cacheKey = this.getCacheKey(sql, params);
if (this.cache.has(cacheKey)) {
var cached = this.cache.get(cacheKey);
if (Date.now() - cached.timestamp < 60000) { // 1分钟缓存
resolve(cached.data);
return;
}
}
// 模拟数据库查询
setTimeout(() => {
var result = this.simulateQuery(sql, params);
// 缓存结果
this.cache.set(cacheKey, {
data: result,
timestamp: Date.now()
});
resolve(result);
}, 50);
} catch (error) {
reject(error);
}
});
}
// 执行SQL语句
function execute(sql, params = []) {
return new Promise((resolve, reject) => {
try {
// 清除相关缓存
this.clearRelatedCache(sql);
// 模拟SQL执行
setTimeout(() => {
var result = {
affectedRows: 1,
insertId: Date.now(),
success: true
};
resolve(result);
}, 30);
} catch (error) {
reject(error);
}
});
}
// 模拟查询执行
function simulateQuery(sql, params) {
// 这里应该是实际的数据库查询逻辑
if (sql.includes('SELECT * FROM users')) {
return [
{ id: 1, username: 'admin', email: 'admin@example.com', role: 'administrator' },
{ id: 2, username: 'user', email: 'user@example.com', role: 'user' }
];
}
if (sql.includes('SELECT * FROM settings')) {
return [
{ key: 'theme', value: 'dark', type: 'string' },
{ key: 'language', value: 'zh-CN', type: 'string' },
{ key: 'autoSave', value: 'true', type: 'boolean' }
];
}
return [];
}
// 获取缓存键
function getCacheKey(sql, params) {
return btoa(sql + JSON.stringify(params));
}
// 清除相关缓存
function clearRelatedCache(sql) {
// 简化的缓存清除逻辑
if (sql.includes('INSERT') || sql.includes('UPDATE') || sql.includes('DELETE')) {
this.cache.clear();
}
}
// 开始事务
function beginTransaction() {
return new Promise((resolve, reject) => {
// 模拟事务开始
setTimeout(() => {
resolve({
commit: () => this.commitTransaction(),
rollback: () => this.rollbackTransaction()
});
}, 10);
});
}
// 提交事务
function commitTransaction() {
return new Promise((resolve) => {
setTimeout(() => {
stdout.println('事务已提交');
resolve();
}, 10);
});
}
// 回滚事务
function rollbackTransaction() {
return new Promise((resolve) => {
setTimeout(() => {
stdout.println('事务已回滚');
resolve();
}, 10);
});
}
// 备份数据库
function backup() {
return new Promise((resolve, reject) => {
try {
var timestamp = new Date().toISOString().replace(/[:.]/g, '-');
var backupFile = `backup_${timestamp}.db`;
// 模拟备份过程
setTimeout(() => {
stdout.println(`数据库已备份到: ${backupFile}`);
// 清理旧备份
this.cleanupOldBackups();
resolve(backupFile);
}, 1000);
} catch (error) {
reject(error);
}
});
}
// 启动自动备份
function startAutoBackup() {
this.backupTimer = setInterval(() => {
this.backup().catch(error => {
stdout.println('自动备份失败:', error.message);
});
}, this.config.backupInterval);
stdout.println('自动备份已启动');
}
// 停止自动备份
function stopAutoBackup() {
if (this.backupTimer) {
clearInterval(this.backupTimer);
this.backupTimer = null;
stdout.println('自动备份已停止');
}
}
// 清理旧备份
function cleanupOldBackups() {
// 这里应该实现实际的文件清理逻辑
stdout.println('清理旧备份文件');
}
// 关闭数据库连接
function close() {
this.stopAutoBackup();
if (this.connection) {
// this.connection.close();
this.connection = null;
}
this.cache.clear();
stdout.println('数据库连接已关闭');
}
}
// 配置管理器
class ConfigurationManager {
function this() {
this.config = new Map();
this.watchers = new Map();
this.configFile = 'config.json';
this.autoSave = true;
}
// 加载配置
function load() {
return new Promise((resolve, reject) => {
try {
// 模拟从文件加载配置
setTimeout(() => {
var defaultConfig = {
'app.name': 'Sciter Application',
'app.version': '1.0.0',
'app.debug': false,
'ui.theme': 'light',
'ui.language': 'zh-CN',
'ui.fontSize': 14,
'window.width': 1200,
'window.height': 800,
'window.resizable': true,
'window.center': true,
'data.autoSave': true,
'data.saveInterval': 30000,
'security.sessionTimeout': 1800000
};
for (var key in defaultConfig) {
this.config.set(key, defaultConfig[key]);
}
stdout.println('配置已加载');
resolve();
}, 100);
} catch (error) {
reject(error);
}
});
}
// 保存配置
function save() {
return new Promise((resolve, reject) => {
try {
var configObject = {};
for (var [key, value] of this.config) {
configObject[key] = value;
}
var configJson = JSON.stringify(configObject, null, 2);
// 这里应该写入实际文件
// System.fs.writeFile(this.configFile, configJson);
setTimeout(() => {
stdout.println('配置已保存');
resolve();
}, 50);
} catch (error) {
reject(error);
}
});
}
// 获取配置值
function get(key, defaultValue = null) {
return this.config.get(key) || defaultValue;
}
// 设置配置值
function set(key, value) {
var oldValue = this.config.get(key);
this.config.set(key, value);
// 触发监听器
if (this.watchers.has(key)) {
var watchers = this.watchers.get(key);
for (var watcher of watchers) {
try {
watcher(value, oldValue, key);
} catch (error) {
stdout.println('配置监听器错误:', error.message);
}
}
}
// 自动保存
if (this.autoSave) {
this.save().catch(error => {
stdout.println('自动保存配置失败:', error.message);
});
}
}
// 监听配置变化
function watch(key, callback) {
if (!this.watchers.has(key)) {
this.watchers.set(key, []);
}
this.watchers.get(key).push(callback);
// 返回取消监听的函数
return () => {
var watchers = this.watchers.get(key);
if (watchers) {
var index = watchers.indexOf(callback);
if (index > -1) {
watchers.splice(index, 1);
}
}
};
}
// 获取所有配置
function getAll() {
var result = {};
for (var [key, value] of this.config) {
result[key] = value;
}
return result;
}
// 批量设置配置
function setMultiple(configs) {
for (var key in configs) {
this.set(key, configs[key]);
}
}
// 重置配置
function reset() {
this.config.clear();
this.watchers.clear();
return this.load();
}
}
}
// 使用示例
function coreFeaturesExample() {
// 认证管理器示例
var authManager = new CoreFeatures.AuthenticationManager();
authManager.login('admin', 'admin123')
.then(result => {
stdout.println('登录成功:', JSON.stringify(result, null, 2));
// 检查权限
var hasAdminPermission = authManager.hasPermission('admin');
stdout.println('是否有管理员权限:', hasAdminPermission);
})
.catch(error => {
stdout.println('登录失败:', error.message);
});
// 数据管理器示例
var dataManager = new CoreFeatures.DataManager();
dataManager.initialize()
.then(() => {
return dataManager.query('SELECT * FROM users');
})
.then(users => {
stdout.println('用户列表:', JSON.stringify(users, null, 2));
})
.catch(error => {
stdout.println('数据操作失败:', error.message);
});
// 配置管理器示例
var configManager = new CoreFeatures.ConfigurationManager();
configManager.load()
.then(() => {
// 监听主题变化
configManager.watch('ui.theme', (newValue, oldValue) => {
stdout.println(`主题已从 ${oldValue} 更改为 ${newValue}`);
});
// 设置配置
configManager.set('ui.theme', 'dark');
// 获取配置
var theme = configManager.get('ui.theme');
stdout.println('当前主题:', theme);
})
.catch(error => {
stdout.println('配置操作失败:', error.message);
});
stdout.println('核心功能示例完成');
}
10.4 用户界面设计
10.4.1 UI组件库
// UI组件库命名空间
namespace UIComponents {
// 基础组件类
class BaseComponent {
function this(element) {
this.element = element;
this.initialized = false;
this.eventHandlers = {};
}
function initialize() {
if (this.initialized) return;
this.setupEvents();
this.render();
this.initialized = true;
}
function setupEvents() {
// 子类实现
}
function render() {
// 子类实现
}
function destroy() {
// 清理事件监听器
for (var event in this.eventHandlers) {
this.element.off(event, this.eventHandlers[event]);
}
this.initialized = false;
}
function on(event, handler) {
this.eventHandlers[event] = handler;
this.element.on(event, handler);
}
function emit(event, data) {
this.element.post(new Event(event, data));
}
}
// 按钮组件
class Button : BaseComponent {
function this(element, options = {}) {
super(element);
this.options = {
type: "primary",
size: "medium",
disabled: false,
loading: false,
...options
};
}
function setupEvents() {
this.on("click", function(evt) {
if (this.options.disabled || this.options.loading) {
evt.stopPropagation();
return false;
}
this.emit("button-click", { button: this });
});
}
function render() {
this.element.attributes.addClass("btn");
this.element.attributes.addClass("btn-" + this.options.type);
this.element.attributes.addClass("btn-" + this.options.size);
if (this.options.disabled) {
this.element.attributes.addClass("disabled");
}
if (this.options.loading) {
this.element.attributes.addClass("loading");
this.element.html = '<span class="spinner"></span>' + this.element.text;
}
}
function setLoading(loading) {
this.options.loading = loading;
this.render();
}
function setDisabled(disabled) {
this.options.disabled = disabled;
this.render();
}
}
// 输入框组件
class Input : BaseComponent {
function this(element, options = {}) {
super(element);
this.options = {
placeholder: "",
required: false,
pattern: null,
maxLength: null,
validator: null,
...options
};
this.isValid = true;
this.errorMessage = "";
}
function setupEvents() {
this.on("input", function(evt) {
this.validate();
this.emit("input-change", {
value: this.getValue(),
isValid: this.isValid
});
});
this.on("blur", function(evt) {
this.validate();
this.showValidation();
});
}
function render() {
if (this.options.placeholder) {
this.element.attributes["placeholder"] = this.options.placeholder;
}
if (this.options.maxLength) {
this.element.attributes["maxlength"] = this.options.maxLength;
}
if (this.options.pattern) {
this.element.attributes["pattern"] = this.options.pattern;
}
}
function getValue() {
return this.element.value || "";
}
function setValue(value) {
this.element.value = value;
this.validate();
}
function validate() {
var value = this.getValue();
// 必填验证
if (this.options.required && !value) {
this.isValid = false;
this.errorMessage = "此字段为必填项";
return false;
}
// 正则验证
if (this.options.pattern && value) {
var regex = new RegExp(this.options.pattern);
if (!regex.test(value)) {
this.isValid = false;
this.errorMessage = "格式不正确";
return false;
}
}
// 自定义验证
if (this.options.validator && value) {
var result = this.options.validator(value);
if (result !== true) {
this.isValid = false;
this.errorMessage = result || "验证失败";
return false;
}
}
this.isValid = true;
this.errorMessage = "";
return true;
}
function showValidation() {
var container = this.element.parent;
var errorElement = container.$("span.error-message");
if (!this.isValid) {
this.element.attributes.addClass("error");
if (!errorElement) {
errorElement = new Element("span");
errorElement.attributes.addClass("error-message");
container.append(errorElement);
}
errorElement.text = this.errorMessage;
} else {
this.element.attributes.removeClass("error");
if (errorElement) {
errorElement.remove();
}
}
}
}
// 模态框组件
class Modal : BaseComponent {
function this(element, options = {}) {
super(element);
this.options = {
title: "",
closable: true,
maskClosable: true,
width: "500px",
height: "auto",
...options
};
this.isVisible = false;
}
function setupEvents() {
// 关闭按钮事件
var closeBtn = this.element.$(".modal-close");
if (closeBtn) {
closeBtn.on("click", function() {
this.hide();
});
}
// 遮罩点击事件
if (this.options.maskClosable) {
this.element.on("click", function(evt) {
if (evt.target === this.element) {
this.hide();
}
});
}
// ESC键关闭
if (this.options.closable) {
view.on("keydown", function(evt) {
if (evt.keyCode === 27 && this.isVisible) {
this.hide();
}
});
}
}
function render() {
this.element.attributes.addClass("modal");
var dialog = this.element.$(".modal-dialog");
if (dialog) {
dialog.style.width = this.options.width;
if (this.options.height !== "auto") {
dialog.style.height = this.options.height;
}
}
var title = this.element.$(".modal-title");
if (title && this.options.title) {
title.text = this.options.title;
}
}
function show() {
this.element.style.display = "flex";
this.isVisible = true;
this.emit("modal-show", { modal: this });
// 添加动画
this.element.attributes.addClass("show");
}
function hide() {
this.element.attributes.removeClass("show");
// 延迟隐藏以完成动画
view.timer(300, function() {
this.element.style.display = "none";
this.isVisible = false;
this.emit("modal-hide", { modal: this });
});
}
function setTitle(title) {
this.options.title = title;
var titleElement = this.element.$(".modal-title");
if (titleElement) {
titleElement.text = title;
}
}
function setContent(content) {
var body = this.element.$(".modal-body");
if (body) {
body.html = content;
}
}
}
}
10.4.2 主题系统
// 主题管理器
class ThemeManager {
function this() {
this.currentTheme = "light";
this.themes = {
light: {
primary: "#007bff",
secondary: "#6c757d",
success: "#28a745",
danger: "#dc3545",
warning: "#ffc107",
info: "#17a2b8",
background: "#ffffff",
surface: "#f8f9fa",
text: "#212529",
textSecondary: "#6c757d"
},
dark: {
primary: "#0d6efd",
secondary: "#6c757d",
success: "#198754",
danger: "#dc3545",
warning: "#fd7e14",
info: "#0dcaf0",
background: "#212529",
surface: "#343a40",
text: "#ffffff",
textSecondary: "#adb5bd"
}
};
this.loadTheme();
}
function setTheme(themeName) {
if (!this.themes[themeName]) {
throw new Error("主题不存在: " + themeName);
}
this.currentTheme = themeName;
this.applyTheme();
this.saveTheme();
}
function applyTheme() {
var theme = this.themes[this.currentTheme];
var root = document.documentElement;
for (var property in theme) {
root.style.setProperty("--color-" + property, theme[property]);
}
document.body.attributes.removeClass("theme-light", "theme-dark");
document.body.attributes.addClass("theme-" + this.currentTheme);
}
function addTheme(name, colors) {
this.themes[name] = colors;
}
function getTheme(name) {
return this.themes[name || this.currentTheme];
}
function getCurrentTheme() {
return this.currentTheme;
}
function saveTheme() {
Storage.userDataPath("theme.json").save(JSON.stringify({
current: this.currentTheme
}));
}
function loadTheme() {
try {
var data = Storage.userDataPath("theme.json").load();
if (data) {
var config = JSON.parse(data);
this.currentTheme = config.current || "light";
}
} catch (e) {
this.currentTheme = "light";
}
this.applyTheme();
}
}
10.4.3 响应式布局
// 响应式布局管理器
class ResponsiveManager {
function this() {
this.breakpoints = {
xs: 0,
sm: 576,
md: 768,
lg: 992,
xl: 1200,
xxl: 1400
};
this.currentBreakpoint = "lg";
this.listeners = [];
this.init();
}
function init() {
this.updateBreakpoint();
view.on("size", function() {
this.updateBreakpoint();
});
}
function updateBreakpoint() {
var width = view.box(#width);
var newBreakpoint = "xs";
for (var bp in this.breakpoints) {
if (width >= this.breakpoints[bp]) {
newBreakpoint = bp;
}
}
if (newBreakpoint !== this.currentBreakpoint) {
var oldBreakpoint = this.currentBreakpoint;
this.currentBreakpoint = newBreakpoint;
this.notifyListeners(oldBreakpoint, newBreakpoint);
}
}
function addListener(callback) {
this.listeners.push(callback);
}
function removeListener(callback) {
var index = this.listeners.indexOf(callback);
if (index > -1) {
this.listeners.splice(index, 1);
}
}
function notifyListeners(oldBreakpoint, newBreakpoint) {
for (var listener in this.listeners) {
try {
listener(newBreakpoint, oldBreakpoint);
} catch (e) {
stdout.printf("响应式监听器错误: %s\n", e.toString());
}
}
}
function getCurrentBreakpoint() {
return this.currentBreakpoint;
}
function isBreakpoint(bp) {
var bpIndex = Object.keys(this.breakpoints).indexOf(bp);
var currentIndex = Object.keys(this.breakpoints).indexOf(this.currentBreakpoint);
return currentIndex >= bpIndex;
}
function getViewportSize() {
return {
width: view.box(#width),
height: view.box(#height)
};
}
}
10.4.4 应用示例
// 应用初始化
class App {
function this() {
this.themeManager = new ThemeManager();
this.responsiveManager = new ResponsiveManager();
this.components = {};
this.init();
}
function init() {
this.initComponents();
this.setupThemeToggle();
this.setupResponsiveLayout();
}
function initComponents() {
// 初始化按钮组件
var buttons = document.$$("button[data-component='button']");
for (var btn in buttons) {
var options = JSON.parse(btn.attributes["data-options"] || "{}");
this.components[btn.id] = new UIComponents.Button(btn, options);
this.components[btn.id].initialize();
}
// 初始化输入框组件
var inputs = document.$$("input[data-component='input']");
for (var input in inputs) {
var options = JSON.parse(input.attributes["data-options"] || "{}");
this.components[input.id] = new UIComponents.Input(input, options);
this.components[input.id].initialize();
}
// 初始化模态框组件
var modals = document.$$(".modal[data-component='modal']");
for (var modal in modals) {
var options = JSON.parse(modal.attributes["data-options"] || "{}");
this.components[modal.id] = new UIComponents.Modal(modal, options);
this.components[modal.id].initialize();
}
}
function setupThemeToggle() {
var themeToggle = document.$("#theme-toggle");
if (themeToggle) {
themeToggle.on("click", function() {
var currentTheme = this.themeManager.getCurrentTheme();
var newTheme = currentTheme === "light" ? "dark" : "light";
this.themeManager.setTheme(newTheme);
});
}
}
function setupResponsiveLayout() {
this.responsiveManager.addListener(function(newBreakpoint, oldBreakpoint) {
stdout.printf("断点变化: %s -> %s\n", oldBreakpoint, newBreakpoint);
// 根据断点调整布局
var sidebar = document.$("#sidebar");
if (sidebar) {
if (this.responsiveManager.isBreakpoint("md")) {
sidebar.style.display = "block";
} else {
sidebar.style.display = "none";
}
}
});
}
function getComponent(id) {
return this.components[id];
}
function showModal(id, title, content) {
var modal = this.getComponent(id);
if (modal) {
modal.setTitle(title);
modal.setContent(content);
modal.show();
}
}
}
// 应用启动
var app = new App();
10.5 测试与质量保证
10.5.1 单元测试框架
// 简单的测试框架
namespace TestFramework {
class TestSuite {
function this(name) {
this.name = name;
this.tests = [];
this.results = {
passed: 0,
failed: 0,
total: 0
};
}
function test(description, testFunction) {
this.tests.push({
description: description,
testFunction: testFunction
});
}
function run() {
stdout.printf("\n运行测试套件: %s\n", this.name);
stdout.printf("=" * 50 + "\n");
for (var test in this.tests) {
this.runTest(test);
}
this.printResults();
}
function runTest(test) {
try {
var assert = new Assert();
test.testFunction(assert);
if (assert.hasFailures()) {
this.results.failed++;
stdout.printf("✗ %s\n", test.description);
for (var failure in assert.failures) {
stdout.printf(" %s\n", failure);
}
} else {
this.results.passed++;
stdout.printf("✓ %s\n", test.description);
}
} catch (e) {
this.results.failed++;
stdout.printf("✗ %s\n", test.description);
stdout.printf(" 错误: %s\n", e.toString());
}
this.results.total++;
}
function printResults() {
stdout.printf("\n测试结果:\n");
stdout.printf("通过: %d\n", this.results.passed);
stdout.printf("失败: %d\n", this.results.failed);
stdout.printf("总计: %d\n", this.results.total);
var successRate = (this.results.passed / this.results.total * 100).toFixed(2);
stdout.printf("成功率: %s%%\n", successRate);
}
}
class Assert {
function this() {
this.failures = [];
}
function equal(actual, expected, message) {
if (actual !== expected) {
this.failures.push(message ||
String.printf("期望 %v 等于 %v", actual, expected));
}
}
function notEqual(actual, expected, message) {
if (actual === expected) {
this.failures.push(message ||
String.printf("期望 %v 不等于 %v", actual, expected));
}
}
function isTrue(value, message) {
if (value !== true) {
this.failures.push(message ||
String.printf("期望 %v 为 true", value));
}
}
function isFalse(value, message) {
if (value !== false) {
this.failures.push(message ||
String.printf("期望 %v 为 false", value));
}
}
function isNull(value, message) {
if (value !== null) {
this.failures.push(message ||
String.printf("期望 %v 为 null", value));
}
}
function isNotNull(value, message) {
if (value === null) {
this.failures.push(message || "期望值不为 null");
}
}
function throws(func, message) {
var threw = false;
try {
func();
} catch (e) {
threw = true;
}
if (!threw) {
this.failures.push(message || "期望函数抛出异常");
}
}
function hasFailures() {
return this.failures.length > 0;
}
}
}
10.5.2 测试用例示例
// 组件测试
var componentTests = new TestFramework.TestSuite("UI组件测试");
componentTests.test("Button组件初始化", function(assert) {
var element = new Element("button");
var button = new UIComponents.Button(element, {
type: "primary",
size: "large"
});
button.initialize();
assert.isTrue(button.initialized, "按钮应该已初始化");
assert.equal(button.options.type, "primary", "按钮类型应该为primary");
assert.equal(button.options.size, "large", "按钮大小应该为large");
});
componentTests.test("Input组件验证", function(assert) {
var element = new Element("input");
var input = new UIComponents.Input(element, {
required: true,
pattern: "^\\d+$"
});
input.initialize();
// 测试空值验证
input.setValue("");
assert.isFalse(input.isValid, "空值应该验证失败");
// 测试格式验证
input.setValue("abc");
assert.isFalse(input.isValid, "非数字应该验证失败");
// 测试正确值
input.setValue("123");
assert.isTrue(input.isValid, "数字应该验证成功");
});
// 主题测试
var themeTests = new TestFramework.TestSuite("主题系统测试");
themeTests.test("主题切换", function(assert) {
var themeManager = new ThemeManager();
assert.equal(themeManager.getCurrentTheme(), "light", "默认主题应该为light");
themeManager.setTheme("dark");
assert.equal(themeManager.getCurrentTheme(), "dark", "主题应该切换为dark");
});
themeTests.test("自定义主题", function(assert) {
var themeManager = new ThemeManager();
themeManager.addTheme("custom", {
primary: "#ff0000",
background: "#000000"
});
var customTheme = themeManager.getTheme("custom");
assert.equal(customTheme.primary, "#ff0000", "自定义主题颜色应该正确");
});
// 运行所有测试
function runAllTests() {
componentTests.run();
themeTests.run();
}
10.6 打包与部署
10.6.1 构建配置
// 构建工具
class BuildTool {
function this(config = {}) {
this.config = {
srcDir: "src",
distDir: "dist",
assetsDir: "assets",
minify: true,
sourceMaps: false,
...config
};
this.tasks = [];
}
function addTask(name, taskFunction) {
this.tasks.push({
name: name,
execute: taskFunction
});
}
function build() {
stdout.printf("开始构建项目...\n");
// 清理输出目录
this.cleanDist();
// 执行构建任务
for (var task in this.tasks) {
try {
stdout.printf("执行任务: %s\n", task.name);
task.execute(this.config);
stdout.printf("任务完成: %s\n", task.name);
} catch (e) {
stdout.printf("任务失败: %s - %s\n", task.name, e.toString());
return false;
}
}
stdout.printf("构建完成!\n");
return true;
}
function cleanDist() {
var distPath = System.path(this.config.distDir);
if (distPath.exists()) {
distPath.remove(true);
}
distPath.mkdir();
}
function copyFiles(srcPattern, destDir) {
var srcPath = System.path(this.config.srcDir);
var files = srcPath.scan(srcPattern);
for (var file in files) {
var relativePath = file.relativeTo(srcPath);
var destPath = System.path(this.config.distDir, destDir, relativePath);
destPath.parent.mkdir(true);
file.copyTo(destPath);
}
}
function minifyJS(filePath) {
if (!this.config.minify) return;
// 简单的JS压缩(移除注释和多余空白)
var content = filePath.load();
// 移除单行注释
content = content.replace(/\/\/.*$/gm, "");
// 移除多行注释
content = content.replace(/\/\*[\s\S]*?\*\//g, "");
// 移除多余空白
content = content.replace(/\s+/g, " ");
content = content.replace(/;\s*}/g, "}");
filePath.save(content);
}
function generateManifest() {
var manifest = {
name: "Sciter Application",
version: "1.0.0",
description: "A Sciter desktop application",
main: "main.htm",
buildTime: new Date().toISOString()
};
var manifestPath = System.path(this.config.distDir, "manifest.json");
manifestPath.save(JSON.stringify(manifest, null, 2));
}
}
10.6.2 部署脚本
// 部署管理器
class DeploymentManager {
function this(config = {}) {
this.config = {
appName: "SciterApp",
version: "1.0.0",
platform: System.PLATFORM,
outputDir: "release",
...config
};
}
function createInstaller() {
stdout.printf("创建安装程序...\n");
var installerScript = this.generateInstallerScript();
var scriptPath = System.path(this.config.outputDir, "installer.nsi");
scriptPath.save(installerScript);
// 如果有NSIS,可以调用makensis
try {
System.exec("makensis " + scriptPath.toString());
stdout.printf("安装程序创建成功\n");
} catch (e) {
stdout.printf("创建安装程序失败: %s\n", e.toString());
}
}
function generateInstallerScript() {
return `
!define APP_NAME "${this.config.appName}"
!define APP_VERSION "${this.config.version}"
!define PUBLISHER "Your Company"
!define WEB_SITE "https://yourwebsite.com"
!define APP_EXE "${this.config.appName}.exe"
Name "\${APP_NAME}"
OutFile "\${APP_NAME}-\${APP_VERSION}-Setup.exe"
InstallDir "$PROGRAMFILES\\\${APP_NAME}"
Section "MainSection" SEC01
SetOutPath "$INSTDIR"
File /r "dist\\*"
CreateDirectory "$SMPROGRAMS\\\${APP_NAME}"
CreateShortCut "$SMPROGRAMS\\\${APP_NAME}\\\${APP_NAME}.lnk" "$INSTDIR\\\${APP_EXE}"
CreateShortCut "$DESKTOP\\\${APP_NAME}.lnk" "$INSTDIR\\\${APP_EXE}"
SectionEnd
Section "Uninstall"
Delete "$INSTDIR\\*"
RMDir /r "$INSTDIR"
Delete "$SMPROGRAMS\\\${APP_NAME}\\*"
RMDir "$SMPROGRAMS\\\${APP_NAME}"
Delete "$DESKTOP\\\${APP_NAME}.lnk"
SectionEnd
`;
}
function createPortableVersion() {
stdout.printf("创建便携版...\n");
var portableDir = System.path(this.config.outputDir, "portable");
portableDir.mkdir(true);
// 复制应用文件
var distDir = System.path("dist");
distDir.copyTo(portableDir);
// 创建便携版标识文件
var portableFlag = System.path(portableDir, "portable.flag");
portableFlag.save("This is a portable version");
stdout.printf("便携版创建完成\n");
}
function generateUpdateInfo() {
var updateInfo = {
version: this.config.version,
releaseDate: new Date().toISOString(),
downloadUrl: "https://yourwebsite.com/downloads/latest",
changelog: [
"新功能: 添加了主题切换",
"改进: 优化了性能",
"修复: 修复了已知问题"
],
minVersion: "1.0.0"
};
var updatePath = System.path(this.config.outputDir, "update.json");
updatePath.save(JSON.stringify(updateInfo, null, 2));
}
}
10.6.3 自动更新系统
// 自动更新管理器
class UpdateManager {
function this(config = {}) {
this.config = {
updateUrl: "https://yourwebsite.com/api/updates",
currentVersion: "1.0.0",
checkInterval: 24 * 60 * 60 * 1000, // 24小时
...config
};
this.updateTimer = null;
}
function startAutoCheck() {
this.checkForUpdates();
this.updateTimer = view.timer(this.config.checkInterval, function() {
this.checkForUpdates();
});
}
function stopAutoCheck() {
if (this.updateTimer) {
this.updateTimer.close();
this.updateTimer = null;
}
}
function checkForUpdates() {
stdout.printf("检查更新...\n");
try {
var response = this.httpGet(this.config.updateUrl);
var updateInfo = JSON.parse(response);
if (this.isNewerVersion(updateInfo.version, this.config.currentVersion)) {
this.showUpdateDialog(updateInfo);
} else {
stdout.printf("当前已是最新版本\n");
}
} catch (e) {
stdout.printf("检查更新失败: %s\n", e.toString());
}
}
function isNewerVersion(newVersion, currentVersion) {
var newParts = newVersion.split(".").map(function(part) {
return parseInt(part);
});
var currentParts = currentVersion.split(".").map(function(part) {
return parseInt(part);
});
for (var i = 0; i < Math.max(newParts.length, currentParts.length); i++) {
var newPart = newParts[i] || 0;
var currentPart = currentParts[i] || 0;
if (newPart > currentPart) return true;
if (newPart < currentPart) return false;
}
return false;
}
function showUpdateDialog(updateInfo) {
var dialog = `
<div class="update-dialog">
<h3>发现新版本</h3>
<p>版本: ${updateInfo.version}</p>
<p>发布日期: ${updateInfo.releaseDate}</p>
<div class="changelog">
<h4>更新内容:</h4>
<ul>
${updateInfo.changelog.map(function(item) {
return "<li>" + item + "</li>";
}).join("")}
</ul>
</div>
<div class="buttons">
<button id="update-now">立即更新</button>
<button id="update-later">稍后提醒</button>
<button id="skip-version">跳过此版本</button>
</div>
</div>
`;
// 显示更新对话框
app.showModal("update-modal", "软件更新", dialog);
// 绑定按钮事件
document.$("#update-now").on("click", function() {
this.downloadAndInstall(updateInfo);
});
document.$("#update-later").on("click", function() {
app.getComponent("update-modal").hide();
});
document.$("#skip-version").on("click", function() {
this.skipVersion(updateInfo.version);
app.getComponent("update-modal").hide();
});
}
function downloadAndInstall(updateInfo) {
stdout.printf("开始下载更新...\n");
// 这里应该实现实际的下载和安装逻辑
// 可以使用Sciter的HTTP API下载文件
// 然后启动安装程序
try {
var tempPath = System.path(System.TEMP, "update.exe");
this.downloadFile(updateInfo.downloadUrl, tempPath);
// 启动安装程序
System.exec(tempPath.toString() + " /S");
// 退出当前应用
view.close();
} catch (e) {
stdout.printf("更新失败: %s\n", e.toString());
}
}
function skipVersion(version) {
var config = {
skippedVersions: [version]
};
Storage.userDataPath("update-config.json").save(JSON.stringify(config));
}
function httpGet(url) {
// 简化的HTTP GET实现
// 实际应用中应该使用Sciter的HTTP API
return "";
}
function downloadFile(url, localPath) {
// 简化的文件下载实现
// 实际应用中应该使用Sciter的HTTP API
}
}
10.6.4 构建和部署示例
// 构建和部署示例
function buildAndDeploy() {
// 创建构建工具
var buildTool = new BuildTool({
srcDir: "src",
distDir: "dist",
minify: true
});
// 添加构建任务
buildTool.addTask("复制HTML文件", function(config) {
buildTool.copyFiles("*.htm", "");
});
buildTool.addTask("复制CSS文件", function(config) {
buildTool.copyFiles("*.css", "styles");
});
buildTool.addTask("处理JavaScript文件", function(config) {
buildTool.copyFiles("*.js", "scripts");
// 压缩JS文件
var jsFiles = System.path(config.distDir, "scripts").scan("*.js");
for (var file in jsFiles) {
buildTool.minifyJS(file);
}
});
buildTool.addTask("复制资源文件", function(config) {
buildTool.copyFiles("assets/*", "assets");
});
buildTool.addTask("生成清单文件", function(config) {
buildTool.generateManifest();
});
// 执行构建
if (buildTool.build()) {
stdout.printf("构建成功,开始部署...\n");
// 创建部署管理器
var deploymentManager = new DeploymentManager({
appName: "MySciterApp",
version: "1.0.0"
});
// 创建安装程序
deploymentManager.createInstaller();
// 创建便携版
deploymentManager.createPortableVersion();
// 生成更新信息
deploymentManager.generateUpdateInfo();
stdout.printf("部署完成!\n");
} else {
stdout.printf("构建失败!\n");
}
}
10.7 本章总结
10.7.1 核心要点
项目规划与架构设计
- 需求分析和管理
- 架构设计和组件依赖
- 项目结构规划
核心功能实现
- 用户认证和权限管理
- 数据管理和持久化
- 配置管理和监听
用户界面设计
- 组件化开发
- 主题系统
- 响应式布局
测试与质量保证
- 单元测试框架
- 测试用例编写
- 自动化测试
打包与部署
- 构建配置和任务
- 安装程序创建
- 自动更新系统
10.7.2 最佳实践
项目管理
- 明确需求和目标
- 合理的架构设计
- 模块化开发
代码质量
- 编写测试用例
- 代码审查
- 持续集成
用户体验
- 响应式设计
- 主题定制
- 性能优化
部署策略
- 自动化构建
- 多平台支持
- 版本管理
10.7.3 练习题
基础练习
项目初始化
- 使用ProjectGenerator创建一个新的Sciter项目
- 配置基本的项目结构和文件
- 实现简单的Hello World应用
组件开发
- 创建一个自定义按钮组件
- 实现组件的初始化和事件处理
- 添加组件的样式和主题支持
数据管理
- 实现一个简单的数据管理器
- 支持数据的增删改查操作
- 添加数据验证功能
进阶练习
完整应用开发
- 开发一个待办事项管理应用
- 实现用户认证和数据持久化
- 添加主题切换和设置功能
测试框架应用
- 为你的应用编写完整的测试用例
- 实现自动化测试流程
- 生成测试报告
部署配置
- 配置完整的构建流程
- 创建安装程序和便携版
- 实现自动更新功能
高级练习
企业级应用
- 开发一个多用户的项目管理系统
- 实现角色权限管理
- 支持数据导入导出
插件系统
- 设计和实现插件架构
- 支持动态加载和卸载插件
- 提供插件开发API
跨平台部署
- 配置多平台构建
- 实现平台特定的功能
- 统一的更新和分发机制
通过本章的学习,你已经掌握了Sciter应用开发的完整流程,从项目规划到最终部署。这些知识和技能将帮助你开发出高质量的桌面应用程序。
恭喜你完成了Sciter桌面应用开发教程!
本教程涵盖了从基础概念到高级应用的完整内容,包括: - Sciter基础知识和环境搭建 - TIScript语言详解 - HTML/CSS在Sciter中的应用 - 事件处理和用户交互 - 组件化开发 - 数据绑定和状态管理 - 样式和主题系统 - 原生API集成 - 性能优化与调试 - 项目实战与部署
现在你已经具备了开发专业Sciter应用的能力,可以开始你的桌面应用开发之旅了!