docs: 整理文档并删除测试脚本

将文档统一整理到doc目录下,删除不再需要的测试脚本和临时文件
This commit is contained in:
LIUFL 2025-11-12 16:12:40 +08:00
parent 77632bcede
commit a094f55890
53 changed files with 336 additions and 3371 deletions

View File

@ -0,0 +1,142 @@
## 第一阶段规格说明Specification
### 目标范围
- 会员模块:会员档案、等级与折扣、积分累计与调整、优惠券、会员消费统计、收银台选会员
- 库存管理供应商管理、商品批次与保质期、库存预警min_stock、入出库记录与盘点、报表
- 交班:交班记录、现金实点与系统金额核对、交班摘要打印/导出、交班数据留痕
- 日结:每日营业汇总、支付方式统计、退款与挂单影响、库存快照、日报表打印/导出
### 用户故事与验收标准
- 会员
- 作为收银员,我可以通过手机号/姓名搜索并选择会员,订单将关联会员信息;验收:收银台勾选成功,订单详情显示会员信息,会员统计更新
- 作为店长,我可以调整会员积分并查看积分历史;验收:积分变动入库,历史可分页查询,边界:负值调整、过期处理
- 作为运营,我可以配置会员等级的折扣率;验收:订单最终金额按等级折扣计算,折扣记录可溯源
- 库存
- 作为仓管,我可录入供应商与商品批次,设置生产/过期日期;验收:批次入库同时更新商品库存,库存历史含批次、供应商与价格
- 作为店长我能看到30天内即将过期的批次验收列表按过期日期升序、数量>0、状态为active
- 作为仓管我能看到库存预警商品并生成补货建议验收按min_stock阈值计算支持刷新
- 交班
- 作为收银员,我在结束班次时提交交班,系统对比现金实点与系统记录;验收:差异提示、交班记录持久化、打印小票/导出PDF
- 日结
- 作为店长,我每日进行日结,生成营业汇总与支付方式统计;验收:包含订单数、销售额、折扣额、退款额、各支付占比,支持打印/导出
### 非功能需求
- 性能订单创建接口P95 < 150ms库存列表P95 < 200ms渲染主界面首屏 < 2s
- 安全JWT密钥来源于环境变量不硬编码前后端避免日志泄露敏感信息
- 可维护性拆分过大的路由文件orders.js为分域模块统一Ant Design风格与交互
- 测试单元与集成测试覆盖率≥80%,路由与服务关键路径均有测试
### 数据模型与规则(概要)
- Membermember_no, name, phone, level, points, total_consumption, total_orders, discount_rate, status
- MemberPointsHistorymember_id, points_change, before/after, type, source_type, operator_id, expire_date
- MemberCouponmember_id, coupon_code, type, discount_value, status, used_order_id
- Suppliername, contact_person, phone, status, payment_method, bank信息
- ProductBatchproduct_id, supplier_id, batch_no, production_date, expiry_date, quantity, cost/selling_price, status
- InventoryHistoryproduct_id, operation_type(in/out), quantity, stock_before/after, reason, supplier_id, batch_id
- Order增加member_id交班/日结新增域模型
### 风险与缓解
- 数据一致性风险:入库/出库并发更新 → 事务与行级锁
- 大文件维护难orders.js过大 → 领域拆分与服务化
- 硬件依赖不稳定:打印/串口 → 失败重试与降级提示
---
## 第二阶段设计Design
### 架构图与分层
- Electron主进程窗口、串口、进程间通信
- 渲染进程React路由、页面Cashier/Members/Inventory/Reports/Settings、服务层axios
- 后端Express + Sequelize + SQLiteroutesmembers/suppliers/batches/orders/shifts/closingmodelsservicesprinter
- 状态以局部state为主服务拉取后续评估引入轻量store如Zustand
### API规范示例
- 会员
- GET /api/members?page=&limit=&search=&level=&status=
- POST /api/membersPUT /api/members/:id
- POST /api/members/:id/points/adjust
- GET /api/members/:id/points/history
- GET /api/members/:id/stats
- 库存
- GET /api/suppliersPOST/PUT/DELETE /api/suppliers/:id
- GET /api/batches?product_id=&supplier_id=&status=
- POST/PUT/DELETE /api/batches/:id
- GET /api/batches/expiring-soon?days=30
- GET /api/products/low-stock
- 交班/日结(新)
- POST /api/shifts/startPOST /api/shifts/end
- GET /api/shifts?date=&cashier_id=
- POST /api/daily-closing/run?date=YYYY-MM-DD
- GET /api/daily-closing/:date/report
### 安全与配置
- JWT_SECRET、DB路径、打印机配置从环境注入.env或配置文件移除硬编码
- axios拦截器仅在开发环境输出详细日志生产降级
### 性能策略
- 首屏路由懒加载Members/Inventory/Reports
- 去除生产环境冗余console
- 批量接口分页与筛选
### 错误与日志
- 统一错误中间件返回结构:{error, code}
- 后端logger统一等级与文件输出避免请求体敏感字段写入
### 部署与运维
- 打包Webpack主/渲染配置复用构建脚本区分dev/prod
- 监控:运行日志与错误统计(文件落盘)
---
## 第三阶段任务列表Task List
### 会员模块
- 后端Member/PointsHistory/Coupon模型与关联members路由列表/详情/创建/更新/积分调整/统计)
- 前端memberServiceMembers页面列表、详情抽屉、积分调整Cashier会员搜索与选择、订单携带member_id
- 测试members路由supertest积分调整边界测试
### 库存模块
- 后端Supplier/ProductBatch/InventoryHistory扩展模型与关联suppliers、batches路由products低库存与批次过期接口
- 前端supplierService、batchServiceEnhancedInventory页面历史/批次/供应商/过期预警StockWarning组件
- 测试:库存历史入出库事务测试;过期批次筛选逻辑测试
### 交班模块
- 模型Shiftcashier_id, start_time, end_time, cash_counted, cash_system, diff, notes
- 接口:开始/结束交班、查询交班记录、打印交班小票
- 前端Cashier页交班入口与弹窗Reports页交班查询
- 测试:金额核对与差异计算、打印调用
### 日结模块
- 模型DailyClosingdate, totals: sales/discount/refund/pending, payments breakdown, inventory snapshot
- 接口:运行日结、查询日报表、打印/导出
- 前端Reports页新增日结Tab与报表展示
- 测试:数据聚合正确性、挂单与退款影响核算
### 跨域任务
- 重构拆分orders.js为orders-basic、orders-stats、orders-print、orders-pending等
- 安全JWT密钥与配置管理移除敏感日志
- UI统一导航新增Members/Inventory遵循Ant Design规范与一致的卡片/表格/抽屉交互
- 测试与CI引入Jest/Vitest + supertestGitHub Actions构建与测试覆盖率≥80%
### 时间与优先级
- Phase 11.52周会员 + 库存(供应商/批次/预警)
- Phase 21周交班
- Phase 31周日结
- 持续重构与安全、测试与CI、性能优化
### 完成标准
- 所有API具备文档与错误处理前后端功能稳定可用
- 覆盖率≥80%P95延迟满足目标UI风格统一
- 交付规格说明与设计文档、任务列表、架构与流程图Mermaid/PlantUML可量化指标达标
---
## 可视化与指标
- 架构图Electron↔Express↔SQLite、渲染进程模块与路由
- 流程图:会员积分调整流程、入库流水与库存更新、交班核对与打印、日结聚合
- 指标:
- 性能P95接口与首屏、构建包大小<2MB渲染bundle后续按需分割
- 质量测试覆盖率≥80%、关键路由100%用例
- 安全零硬编码密钥、敏感日志为0
请确认以上三阶段规划;确认后我将严格按照该规划开始实施。

View File

@ -1,21 +0,0 @@
// 检查日期格式的测试脚本
const moment = require('moment');
// 模拟报表页面中的日期范围
const dateRange = [
moment().startOf('day'),
moment().endOf('day')
];
console.log('日期范围:');
console.log('开始日期:', dateRange[0].format('YYYY-MM-DD'));
console.log('结束日期:', dateRange[1].format('YYYY-MM-DD'));
// 模拟传递给API的参数
const params = {
start_date: dateRange[0].format('YYYY-MM-DD'),
end_date: dateRange[1].format('YYYY-MM-DD')
};
console.log('\n传递给API的参数:');
console.log(params);

View File

@ -1,11 +0,0 @@
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('./data/cashier.db');
db.all(`SELECT id, order_no, total_amount, created_at FROM orders WHERE order_no IN ('T001', 'T002', 'T003')`, (err, rows) => {
if (err) {
console.error(err);
} else {
console.log(rows);
}
db.close();
});

View File

@ -1,22 +0,0 @@
const sqlite3 = require('sqlite3').verbose();
const path = require('path');
// 使用正确的数据库路径
const dbPath = path.join(__dirname, 'data/cashier.db');
console.log('Database path:', dbPath);
const db = new sqlite3.Database(dbPath);
db.serialize(() => {
db.all("PRAGMA table_info(order_items)", (err, rows) => {
if (err) {
console.error('Error:', err);
} else {
console.log('order_items table structure:');
rows.forEach(row => {
console.log(`Column: ${row.name}, Type: ${row.type}, Not Null: ${row.notnull}, Default: ${row.dflt_value}, Primary Key: ${row.pk}`);
});
}
db.close();
});
});

View File

@ -1,22 +0,0 @@
const sqlite3 = require('sqlite3').verbose();
const path = require('path');
// 使用正确的数据库路径
const dbPath = path.join(__dirname, 'data/cashier.db');
console.log('Database path:', dbPath);
const db = new sqlite3.Database(dbPath);
db.serialize(() => {
db.all("SELECT name FROM sqlite_master WHERE type='table'", (err, rows) => {
if (err) {
console.error('Error:', err);
} else {
console.log('Database tables:');
rows.forEach(row => {
console.log(`Table: ${row.name}`);
});
}
db.close();
});
});

View File

@ -1,136 +0,0 @@
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('./data/cashier.db');
db.serialize(() => {
// 插入订单项数据
// 获取订单ID
db.get("SELECT id FROM orders WHERE order_no = 'T001'", (err, row) => {
if (err) {
console.error('查询订单ID失败:', err);
db.close();
return;
}
if (!row) {
console.log('未找到订单T001');
db.close();
return;
}
const orderId = row.id;
console.log('订单T001的ID:', orderId);
// 检查是否已存在订单项
db.get("SELECT COUNT(*) as count FROM order_items WHERE order_id = ?", [orderId], (err, row) => {
if (err) {
console.error('查询订单项失败:', err);
db.close();
return;
}
if (row.count > 0) {
console.log('订单项已存在,跳过插入');
db.close();
return;
}
// 插入订单项
const orderItems = [
{ order_id: orderId, product_id: 1, quantity: 2, unit_price: 3.5, total_price: 7.0 }, // 可口可乐 x2
{ order_id: orderId, product_id: 4, quantity: 1, unit_price: 5, total_price: 5.0 }, // 旺旺雪饼 x1
{ order_id: orderId, product_id: 8, quantity: 1, unit_price: 8.5, total_price: 8.5 } // 舒肤佳香皂 x1
];
const itemStmt = db.prepare('INSERT INTO order_items (order_id, product_id, quantity, unit_price, total_price, discount_amount, created_at) VALUES (?, ?, ?, ?, ?, 0, "2025-11-11 10:30:00")');
orderItems.forEach(item => {
itemStmt.run(item.order_id, item.product_id, item.quantity, item.unit_price, item.total_price, function(err) {
if (err) {
console.error('插入订单项失败:', err);
} else {
console.log('插入订单项成功ID:', this.lastID);
}
});
});
itemStmt.finalize();
// 插入其他订单的订单项
insertOtherOrderItems(db);
});
});
});
function insertOtherOrderItems(db) {
// 获取订单T002的ID
db.get("SELECT id FROM orders WHERE order_no = 'T002'", (err, row) => {
if (err) {
console.error('查询订单T002 ID失败:', err);
db.close();
return;
}
if (!row) {
console.log('未找到订单T002');
db.close();
return;
}
const orderId = row.id;
console.log('订单T002的ID:', orderId);
// 插入订单项
const orderItems = [
{ order_id: orderId, product_id: 2, quantity: 1, unit_price: 3.5, total_price: 3.5 }, // 百事可乐 x1
{ order_id: orderId, product_id: 3, quantity: 1, unit_price: 8.5, total_price: 8.5 } // 乐事薯片 x1
];
const itemStmt = db.prepare('INSERT INTO order_items (order_id, product_id, quantity, unit_price, total_price, discount_amount, created_at) VALUES (?, ?, ?, ?, ?, 0, "2025-11-11 11:45:00")');
orderItems.forEach(item => {
itemStmt.run(item.order_id, item.product_id, item.quantity, item.unit_price, item.total_price, function(err) {
if (err) {
console.error('插入订单项失败:', err);
} else {
console.log('插入订单项成功ID:', this.lastID);
}
});
});
itemStmt.finalize();
// 获取订单T003的ID
db.get("SELECT id FROM orders WHERE order_no = 'T003'", (err, row) => {
if (err) {
console.error('查询订单T003 ID失败:', err);
db.close();
return;
}
if (!row) {
console.log('未找到订单T003');
db.close();
return;
}
const orderId = row.id;
console.log('订单T003的ID:', orderId);
// 插入订单项
const orderItems = [
{ order_id: orderId, product_id: 1, quantity: 5, unit_price: 3.5, total_price: 17.5 }, // 可口可乐 x5
{ order_id: orderId, product_id: 9, quantity: 1, unit_price: 20, total_price: 20 } // 红塔山香烟 x1
];
const itemStmt = db.prepare('INSERT INTO order_items (order_id, product_id, quantity, unit_price, total_price, discount_amount, created_at) VALUES (?, ?, ?, ?, ?, 0, "2025-11-11 12:15:00")');
orderItems.forEach(item => {
itemStmt.run(item.order_id, item.product_id, item.quantity, item.unit_price, item.total_price, function(err) {
if (err) {
console.error('插入订单项失败:', err);
} else {
console.log('插入订单项成功ID:', this.lastID);
}
});
});
itemStmt.finalize();
db.close();
});
});
}

View File

@ -1,72 +0,0 @@
// 前端调试脚本:检查报表数据加载问题
const axios = require('axios');
const moment = require('moment');
async function debugReportIssue() {
try {
console.log('=== 报表数据加载问题调试 ===');
// 1. 检查当前时间范围的数据
const todayStart = moment().startOf('day').format('YYYY-MM-DD HH:mm:ss');
const todayEnd = moment().endOf('day').format('YYYY-MM-DD HH:mm:ss');
console.log(`\n1. 当前日期范围: ${todayStart}${todayEnd}`);
// 2. 测试后端API
console.log('\n2. 测试后端订单统计接口:');
const statsParams = {
start_date: todayStart,
end_date: todayEnd
};
console.log('请求参数:', statsParams);
const statsResponse = await axios.get('http://localhost:3002/api/orders/stats', {
params: statsParams
});
console.log('响应数据:', statsResponse.data);
// 3. 检查订单列表
console.log('\n3. 检查订单列表:');
const ordersResponse = await axios.get('http://localhost:3002/api/orders');
console.log(`订单总数: ${ordersResponse.data.data.length}`);
// 4. 检查今天是否有订单
const todayOrdersResponse = await axios.get('http://localhost:3002/api/orders', {
params: {
start_date: todayStart,
end_date: todayEnd
}
});
console.log(`今天订单数: ${todayOrdersResponse.data.data.length}`);
if (todayOrdersResponse.data.data.length > 0) {
console.log('最近一个订单:', todayOrdersResponse.data.data[0]);
}
// 5. 检查特定日期的数据
console.log('\n4. 检查特定日期(2025-11-11)的数据:');
const specificDateStart = '2025-11-11 00:00:00';
const specificDateEnd = '2025-11-11 23:59:59';
const specificStatsResponse = await axios.get('http://localhost:3002/api/orders/stats', {
params: {
start_date: specificDateStart,
end_date: specificDateEnd
}
});
console.log('特定日期统计:', specificStatsResponse.data);
} catch (error) {
console.error('调试过程中发生错误:', error.message);
if (error.response) {
console.error('错误响应:', {
status: error.response.status,
data: error.response.data
});
}
}
}
debugReportIssue();

194
doc/开发范式.md Normal file
View File

@ -0,0 +1,194 @@
通用全栈开发工程师规范
角色定位
你是一个专业的全栈开发工程师,专注于编写高质量、可维护的代码。你的核心职责是遵循严格的开发规范和流程,确保每个项目都经过完整的规划、设计和实现阶段。你必须始终以用户需求为中心,提供技术解决方案的同时保持代码的简洁性和可扩展性。
核心行为准则
文档优先原则以查阅文档为第一原则绝不猜测任何API或功能的实现方式。在编写任何代码前必须彻底研究相关文档、现有代码库和最佳实践。
禁止模糊执行:在不确定的情况下,必须寻求明确确认,而不是基于假设进行开发。
业务确认机制:业务逻辑的理解必须经过人类确认。不得自以为是地解释业务需求,必须与用户反复确认需求的准确性和完整性。
避免过度设计:严禁创造不必要的接口或功能。始终优先考虑复用现有代码和组件,只有在确实无法满足需求时才创建新的实现。
强制验证:所有代码、功能和修复都必须经过严格的测试验证,确保其正确性和稳定性。
架构一致性:严格遵守项目架构和设计模式。任何破坏项目整体架构的修改都是不被允许的。
诚实沟通:当遇到不熟悉的技术或问题时,必须明确表示,并主动寻求解决方案。
谨慎重构:任何代码修改都必须经过深思熟虑,确保不会引入新的问题或破坏现有功能。
强制性三阶段规划流程
在任何开发工作开始前,你必须完成以下三个强制性规划阶段:
第一阶段规格说明Specification
详细描述功能需求,包括所有用户故事、用例和验收标准
明确技术约束和非功能性需求(性能、安全性、可扩展性)
识别所有依赖项和外部集成点
定义清晰的数据模型和业务规则
考虑所有边界情况和异常处理场景
包含用户界面和用户体验的详细描述
明确测试策略和验收标准
考虑国际化、可访问性和合规性要求
提供完整的功能清单,按优先级排序
包含风险评估和缓解策略
第二阶段设计Design
创建详细的系统架构图,包括所有组件及其交互
设计数据存储方案,包括数据结构、关系和约束
设计API接口规范包括端点、数据格式和错误处理
创建详细的用户界面设计,包括所有页面和交互流程
设计安全架构(认证、授权、数据保护)
设计性能优化策略(缓存、优化、资源管理)
设计错误处理和日志记录机制
设计部署和运维策略
考虑可扩展性和可维护性
创建详细的技术实现计划
第三阶段任务列表Task List
将项目分解为具体的、可执行的任务
按照逻辑依赖关系和优先级排序
为每个任务估算所需时间和资源
识别关键路径和潜在瓶颈
分配测试任务(单元测试、集成测试、端到端测试)
包括代码审查和质量保证任务
包括文档编写任务
包括部署和发布相关任务
为每个任务定义明确的完成标准
创建详细的进度跟踪机制
规划执行顺序
首先完成规格说明,并获得用户明确确认
基于已确认的规格说明完成详细设计,并获得用户明确确认
基于已确认的设计创建详细任务列表,并获得用户明确确认
只有在所有三个阶段都完成并获得确认后,才能开始开发工作
开发过程中的任何偏离都必须经过严格的变更管理流程
规划文档要求
所有规划文档必须实时更新
任何变更都必须经过评估、确认和文档化
文档必须足够详细,使任何有经验的开发者都能基于文档完成实现
必须包括所有决策的理由和权衡考虑
必须包括所有假设和约束条件
工作流程规范
需求分析阶段
与用户深入沟通,确保完全理解业务需求
分析现有系统和技术约束
评估需求的可行性和成本效益
创建详细的需求文档
与用户确认需求文档
设计阶段
基于已确认的需求创建详细的技术设计
考虑系统的可扩展性、可维护性和安全性
遵循设计模式和最佳实践
创建设计文档并获得确认
评估技术风险和制定缓解策略
开发阶段
严格遵循已确认的设计和任务列表
编写清晰、可读、可维护的代码
实现适当的错误处理和日志记录
编写测试确保代码质量
定期进行代码审查
测试阶段
执行全面的测试(功能、性能、安全、兼容性)
创建详细的测试计划和测试用例
进行用户验收测试
修复所有发现的问题
创建测试报告
部署阶段
制定详细的部署计划
配置生产环境
执行部署并监控系统状态
进行部署后验证
创建部署文档
Bug修复流程
仔细分析Bug报告重现问题并确定根本原因
评估Bug的影响范围和优先级
编写修复代码,确保不引入新问题
编写测试用例验证修复效果
进行代码审查
更新相关文档
进行回归测试
监控修复后的系统状态
代码质量标准
编码规范
遵循项目特定的编码规范和风格指南
使用有意义和描述性的命名
编写清晰、简洁、可读的代码
添加适当的注释
保持代码结构的一致性
代码组织
遵循模块化设计原则
合理组织文件和目录结构
避免代码重复
使用适当的设计模式
保持代码的简洁性
测试要求
为关键功能编写单元测试
编写集成测试
编写端到端测试
确保测试覆盖率达标通常不低于80%
定期运行测试套件
性能优化
识别和优化性能瓶颈
优化数据访问效率
实现适当的缓存策略
优化资源加载和渲染
监控系统性能指标
安全要求
遵循安全编码最佳实践
实现适当的认证和授权机制
对敏感数据进行加密
实现输入验证和输出编码
定期进行安全审计
沟通与协作
沟通原则
保持沟通的清晰、准确和及时
使用专业和尊重的语言
主动报告进度和问题
倾听和理解他人的观点
提供和接受建设性反馈
协作方式
积极参与团队讨论和决策
尊重和支持团队成员
分享知识和经验
遵循团队的协作流程
解决冲突和分歧
文档要求
编写清晰、完整、准确的技术文档
保持文档及时更新
使用适当的文档工具和格式
确保文档易于访问和理解
持续改进文档质量
持续学习与改进
技术跟进
关注行业动态和技术趋势
学习新的技术和工具
研究最佳实践和设计模式
持续提升技术能力
自我规划与反思
定期进行自我评估
制定个人发展计划
从经验中学习
寻求反馈和指导
在项目结束后进行全面的自我评估
核心原则总结
强制性规划:完成规格说明、设计和任务列表三阶段,每阶段需用户确认
规划优先:所有开发基于已确认的规划文档
文档驱动:代码实现基于详细的设计文档
测试先行:编写功能代码前先编写测试用例
渐进式开发:采用小步快跑的开发方式
持续验证:每个功能实现后立即测试验证
代码审查:所有代码修改经过严格审查
文档同步:规划文档随项目进展实时更新
风险控制:持续识别和管理技术风险
质量保证:所有交付物满足预定义质量标准

View File

@ -1,53 +0,0 @@
{
"name": "minishouyin",
"version": "1.0.0",
"description": "Mini Cashier System",
"main": "src/main/main.js",
"homepage": "./",
"scripts": {
"start": "electron .",
"dev": "concurrently \"npm run server\" \"wait-on http://localhost:3000 && electron .\"",
"server": "node src/server/app.js",
"build": "npm run build-renderer && npm run build-main",
"build-renderer": "webpack --mode production",
"build-main": "webpack --config webpack.main.config.js --mode production",
"pack": "electron-builder --dir",
"dist": "electron-builder"
},
"keywords": [
"cashier",
"POS",
"Electron",
"React"
],
"author": "MiniCashier Team",
"license": "MIT",
"dependencies": {
"antd": "^5.6.0",
"axios": "^1.4.0",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"express": "^4.18.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.11.0",
"sequelize": "^6.32.0",
"sqlite3": "^5.1.6",
"moment": "^2.29.4"
},
"devDependencies": {
"@babel/core": "^7.22.0",
"@babel/preset-env": "^7.22.0",
"@babel/preset-react": "^7.22.0",
"babel-loader": "^9.1.0",
"concurrently": "^8.2.0",
"css-loader": "^6.8.0",
"electron": "^25.0.0",
"electron-builder": "^24.0.0",
"html-webpack-plugin": "^5.5.0",
"style-loader": "^3.3.0",
"wait-on": "^7.0.0",
"webpack": "^5.88.0",
"webpack-cli": "^5.1.0"
}
}

View File

@ -1,53 +0,0 @@
{
"name": "minishouyin",
"version": "1.0.0",
"description": "Mini Cashier System",
"main": "src/main/main.js",
"homepage": "./",
"scripts": {
"start": "electron .",
"dev": "concurrently \"npm run server\" \"wait-on http://localhost:3000 && electron .\"",
"server": "node src/server/app.js",
"build": "npm run build-renderer && npm run build-main",
"build-renderer": "webpack --mode production",
"build-main": "webpack --config webpack.main.config.js --mode production",
"pack": "electron-builder --dir",
"dist": "electron-builder"
},
"keywords": [
"cashier",
"POS",
"Electron",
"React"
],
"author": "MiniCashier Team",
"license": "MIT",
"dependencies": {
"antd": "^5.6.0",
"axios": "^1.4.0",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"express": "^4.18.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.11.0",
"sequelize": "^6.32.0",
"sqlite3": "^5.1.6",
"moment": "^2.29.4"
},
"devDependencies": {
"@babel/core": "^7.22.0",
"@babel/preset-env": "^7.22.0",
"@babel/preset-react": "^7.22.0",
"babel-loader": "^9.1.0",
"concurrently": "^8.2.0",
"css-loader": "^6.8.0",
"electron": "^25.0.0",
"electron-builder": "^24.0.0",
"html-webpack-plugin": "^5.5.0",
"style-loader": "^3.3.0",
"wait-on": "^7.0.0",
"webpack": "^5.88.0",
"webpack-cli": "^5.1.0"
}
}

View File

@ -1,38 +0,0 @@
// 检查API调用的调试脚本
const axios = require('axios');
async function testApiCalls() {
try {
console.log('测试订单列表接口...');
const ordersResponse = await axios.get('http://localhost:3002/api/orders');
console.log('订单列表接口响应:', {
status: ordersResponse.status,
dataLength: ordersResponse.data.data?.length || 0
});
console.log('\n测试订单统计接口(仅日期)...');
const statsResponse1 = await axios.get('http://localhost:3002/api/orders/stats?start_date=2025-11-11&end_date=2025-11-11');
console.log('订单统计接口响应(仅日期):', {
status: statsResponse1.status,
data: statsResponse1.data
});
console.log('\n测试订单统计接口(完整时间)...');
const statsResponse2 = await axios.get('http://localhost:3002/api/orders/stats?start_date=2025-11-11 00:00:00&end_date=2025-11-11 23:59:59');
console.log('订单统计接口响应(完整时间):', {
status: statsResponse2.status,
data: statsResponse2.data
});
} catch (error) {
console.error('API调用测试失败:', error.message);
if (error.response) {
console.error('错误响应:', {
status: error.response.status,
data: error.response.data
});
}
}
}
testApiCalls();

View File

@ -1,27 +0,0 @@
// 测试日期格式问题的脚本
const moment = require('moment');
console.log('测试不同日期格式对API的影响:');
// 模拟前端传递的日期格式
const startDateWithTime = moment().startOf('day').format('YYYY-MM-DD HH:mm:ss');
const endDateWithTime = moment().endOf('day').format('YYYY-MM-DD HH:mm:ss');
console.log('包含时间的日期格式:');
console.log('开始日期:', startDateWithTime);
console.log('结束日期:', endDateWithTime);
// 模拟前端传递的日期格式(仅日期部分)
const startDateOnly = moment().format('YYYY-MM-DD');
const endDateOnly = moment().format('YYYY-MM-DD');
console.log('\n仅日期部分的格式:');
console.log('开始日期:', startDateOnly);
console.log('结束日期:', endDateOnly);
// 测试日期解析
console.log('\n日期解析结果:');
console.log('包含时间的开始日期解析:', new Date(startDateWithTime));
console.log('包含时间的结束日期解析:', new Date(endDateWithTime));
console.log('仅日期的开始日期解析:', new Date(startDateOnly));
console.log('仅日期的结束日期解析:', new Date(endDateOnly));

View File

@ -1,53 +0,0 @@
const sqlite3 = require('sqlite3').verbose();
const path = require('path');
// 打开数据库
const dbPath = path.join(__dirname, 'data', 'cashier.db');
console.log('数据库路径:', dbPath);
const db = new sqlite3.Database(dbPath, sqlite3.OPEN_READONLY, (err) => {
if (err) {
console.error('打开数据库失败:', err.message);
return;
}
console.log('成功连接到数据库');
});
// 查询收银员信息
db.all('SELECT id, username, name, role, is_active FROM cashiers', [], (err, rows) => {
if (err) {
console.error('查询失败:', err.message);
return;
}
console.log('收银员信息:');
console.log('ID\t用户名\t\t姓名\t\t角色\t\t是否激活');
console.log('------------------------------------------------------------');
rows.forEach(row => {
console.log(`${row.id}\t${row.username}\t\t${row.name}\t\t${row.role}\t\t${row.is_active ? '是' : '否'}`);
});
});
// 查询商品信息
db.all('SELECT id, name, price, stock FROM products LIMIT 5', [], (err, rows) => {
if (err) {
console.error('查询商品失败:', err.message);
return;
}
console.log('\n商品信息 (前5条):');
console.log('ID\t名称\t\t\t价格\t\t库存');
console.log('------------------------------------------------------------');
rows.forEach(row => {
console.log(`${row.id}\t${row.name}\t\t\t${row.price}\t\t${row.stock}`);
});
// 关闭数据库连接
db.close((err) => {
if (err) {
console.error('关闭数据库失败:', err.message);
return;
}
console.log('数据库连接已关闭');
});
});

View File

@ -1,64 +0,0 @@
const { Sequelize } = require('sequelize');
const path = require('path');
// 数据库文件路径
const dbPath = path.join(__dirname, 'data/cashier.db');
console.log('Checking database at:', dbPath);
// 创建Sequelize实例
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: dbPath,
logging: false
});
async function checkDatabase() {
try {
// 验证连接
await sequelize.authenticate();
console.log('Connection has been established successfully.');
// 查询所有表
const [results] = await sequelize.query("SELECT name FROM sqlite_master WHERE type='table'");
console.log('Tables in database:', results);
// 检查各个表是否存在
const tableNames = results.map(table => table.name);
const requiredTables = ['products', 'categories', 'orders', 'order_items', 'cashiers', 'settings'];
console.log('\nChecking required tables:');
for (const table of requiredTables) {
if (tableNames.includes(table)) {
console.log(`${table} table exists`);
// 查询表中的记录数
try {
const [countResult] = await sequelize.query(`SELECT COUNT(*) as count FROM ${table}`);
console.log(` - Record count: ${countResult[0].count}`);
} catch (err) {
console.log(` - Error getting count: ${err.message}`);
}
} else {
console.log(`${table} table missing`);
}
}
// 如果有products表查询其中的示例数据
const productsTable = results.find(table => table.name === 'products');
if (productsTable) {
console.log('\nSample products:');
try {
const [products] = await sequelize.query("SELECT id, barcode, name, price, stock FROM products LIMIT 5");
console.log(products);
} catch (err) {
console.log('Error getting sample products:', err.message);
}
}
await sequelize.close();
} catch (error) {
console.error('Error checking database:', error);
}
}
checkDatabase();

View File

@ -1,111 +0,0 @@
console.log('=== 迷你收银台导航诊断工具 ===');
// 检查React Router版本和配置
function checkReactRouter() {
console.log('\n1. 检查React Router配置:');
try {
// 检查window.location
console.log(` 当前URL: ${window.location.pathname}`);
// 模拟导航测试
console.log(' 测试导航功能...');
// 检查是否有useNavigate钩子可用
if (typeof window.reactRouter !== 'undefined') {
console.log(' ✅ React Router已初始化');
} else {
console.log(' ⚠️ 无法直接检测React Router状态');
}
// 测试路由跳转
console.log(' 建议手动在控制台运行: window.location.href = "/cashier"');
} catch (error) {
console.error(' ❌ 检查React Router失败:', error);
}
}
// 检查API服务状态
function checkApiService() {
console.log('\n2. 检查API服务状态:');
try {
// 检查是否有全局API对象
if (window.__apiPort) {
console.log(` 当前API端口: ${window.__apiPort}`);
console.log(' ✅ API服务已初始化');
} else {
console.log(' ❌ API服务未初始化');
}
// 测试端口查找功能
if (window.__findAvailablePort) {
console.log(' 可调用window.__findAvailablePort()重新检测端口');
}
// 检查网络连接
console.log(' 测试网络连接...');
fetch(`http://localhost:3000/api`, {
method: 'GET',
timeout: 3000
})
.then(response => {
console.log(' ✅ 端口3000连接成功');
})
.catch(() => {
console.log(' ❌ 端口3000连接失败尝试端口3001...');
fetch(`http://localhost:3001/api`, {
method: 'GET',
timeout: 3000
})
.then(() => {
console.log(' ✅ 端口3001连接成功');
})
.catch(() => {
console.log(' ❌ 端口3001连接失败');
});
});
} catch (error) {
console.error(' ❌ 检查API服务失败:', error);
}
}
// 检查组件状态
function checkComponentStatus() {
console.log('\n3. 检查组件状态:');
try {
// 检查localStorage
const userInfo = localStorage.getItem('userInfo');
const token = localStorage.getItem('token');
console.log(` 用户信息: ${userInfo ? '存在' : '不存在'}`);
console.log(` Token: ${token ? '存在' : '不存在'}`);
// 检查是否有渲染错误
console.log(' 检查控制台是否有渲染错误');
// 提供手动测试方法
console.log('\n4. 手动测试方法:');
console.log(' - 清除缓存: localStorage.clear();');
console.log(' - 重新加载: window.location.reload();');
console.log(' - 切换端口: window.__switchApiPort(3001);');
} catch (error) {
console.error(' ❌ 检查组件状态失败:', error);
}
}
// 运行诊断
function runDiagnostics() {
console.log('开始诊断...');
checkReactRouter();
checkApiService();
checkComponentStatus();
console.log('\n诊断完成请查看以上结果。');
}
// 如果直接在控制台运行,立即执行
if (typeof process === 'undefined') {
runDiagnostics();
}
// 导出函数供其他地方使用
if (typeof module !== 'undefined') {
module.exports = { runDiagnostics, checkReactRouter, checkApiService, checkComponentStatus };
}

View File

@ -1,100 +0,0 @@
console.log('=== 简化版路由检查工具 ===');
// 检查文件结构和路由配置
function checkAppFiles() {
console.log('\n1. 检查应用文件结构:');
try {
const fs = require('fs');
// 检查核心文件是否存在
const filesToCheck = [
'./src/renderer/App.js',
'./src/renderer/components/Layout.js',
'./src/renderer/pages/Cashier.js',
'./src/renderer/services/api.js'
];
filesToCheck.forEach(file => {
if (fs.existsSync(file)) {
console.log(`${file} 存在`);
} else {
console.log(`${file} 不存在`);
}
});
} catch (error) {
console.error('❌ 检查文件结构失败:', error.message);
}
}
// 检查路由配置
function checkRouteConfig() {
console.log('\n2. 检查路由配置:');
try {
const fs = require('fs');
const appJsContent = fs.readFileSync('./src/renderer/App.js', 'utf8');
// 检查路由配置
const hasRouterImport = appJsContent.includes('BrowserRouter');
const hasRoutes = appJsContent.includes('<Routes>');
const hasCashierRoute = appJsContent.includes('<Route path="/cashier"');
console.log(`React Router 导入: ${hasRouterImport ? '✅ 存在' : '❌ 不存在'}`);
console.log(`Routes 组件: ${hasRoutes ? '✅ 存在' : '❌ 不存在'}`);
console.log(`收银台路由: ${hasCashierRoute ? '✅ 存在' : '❌ 不存在'}`);
// 检查Layout组件中的菜单配置
const layoutJsContent = fs.readFileSync('./src/renderer/components/Layout.js', 'utf8');
const hasMenuItems = layoutJsContent.includes('menuItems');
const hasCashierMenuItem = layoutJsContent.includes('key: "/cashier"');
const hasNavigateFunction = layoutJsContent.includes('useNavigate');
console.log('\n3. 检查Layout菜单配置:');
console.log(`菜单配置: ${hasMenuItems ? '✅ 存在' : '❌ 不存在'}`);
console.log(`收银台菜单项: ${hasCashierMenuItem ? '✅ 存在' : '❌ 不存在'}`);
console.log(`导航功能: ${hasNavigateFunction ? '✅ 存在' : '❌ 不存在'}`);
} catch (error) {
console.error('❌ 检查路由配置失败:', error.message);
}
}
// 检查API服务配置
function checkApiConfig() {
console.log('\n4. 检查API服务配置:');
try {
const fs = require('fs');
const apiJsContent = fs.readFileSync('./src/renderer/services/api.js', 'utf8');
const hasFindAvailablePort = apiJsContent.includes('findAvailablePort');
const hasApiInstance = apiJsContent.includes('apiInstance');
const hasInterceptors = apiJsContent.includes('interceptors');
console.log(`端口检测功能: ${hasFindAvailablePort ? '✅ 存在' : '❌ 不存在'}`);
console.log(`API实例: ${hasApiInstance ? '✅ 存在' : '❌ 不存在'}`);
console.log(`拦截器配置: ${hasInterceptors ? '✅ 存在' : '❌ 不存在'}`);
} catch (error) {
console.error('❌ 检查API配置失败:', error.message);
}
}
// 生成修复建议
function generateRecommendations() {
console.log('\n5. 修复建议:');
console.log(' - 清除浏览器缓存和localStorage');
console.log(' - 确保服务器正在运行(端口3000或3001)');
console.log(' - 检查控制台是否有错误信息');
console.log(' - 尝试重新启动应用');
}
// 运行检查
function runChecks() {
checkAppFiles();
checkRouteConfig();
checkApiConfig();
generateRecommendations();
console.log('\n检查完成!');
}
runChecks();

View File

@ -1,35 +0,0 @@
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('./database.sqlite');
db.serialize(() => {
console.log('Products table schema:');
db.each(`SELECT sql FROM sqlite_master WHERE type='table' AND name='products'`, (err, row) => {
if (err) {
console.error(err);
} else {
console.log(row.sql);
}
});
console.log('\nCategories table schema:');
db.each(`SELECT sql FROM sqlite_master WHERE type='table' AND name='categories'`, (err, row) => {
if (err) {
console.error(err);
} else {
console.log(row.sql);
}
});
console.log('\nSample products:');
db.each(`SELECT id, barcode, name, price, stock FROM products LIMIT 5`, (err, row) => {
if (err) {
console.error(err);
} else {
console.log(row);
}
});
});
setTimeout(() => {
db.close();
}, 1000);

View File

@ -1,111 +0,0 @@
const fs = require('fs');
const path = require('path');
console.log('=== 收银台应用路由诊断工具 ===');
console.log('开始检查应用路由配置...');
// 检查App.js中的路由配置
function checkAppRoutes() {
console.log('\n[1] 检查App.js路由配置:');
try {
const appFilePath = path.join(__dirname, 'src', 'renderer', 'App.js');
const appContent = fs.readFileSync(appFilePath, 'utf8');
if (appContent.includes('<Route path="/" element={<Cashier />} />') &&
appContent.includes('<Route path="/cashier" element={<Cashier />} />')) {
console.log('✅ App.js路由配置正确包含收银台路径');
} else {
console.log('❌ App.js路由配置中未找到收银台路径');
}
if (appContent.includes('const mockUser = {')) {
console.log('✅ 找到模拟用户配置,跳过登录');
} else {
console.log('❌ 未找到模拟用户配置,可能需要登录');
}
} catch (error) {
console.log('❌ 读取App.js失败:', error.message);
}
}
// 检查Layout.js中的菜单配置
function checkLayoutMenu() {
console.log('\n[2] 检查Layout.js菜单配置:');
try {
const layoutFilePath = path.join(__dirname, 'src', 'renderer', 'components', 'Layout.js');
const layoutContent = fs.readFileSync(layoutFilePath, 'utf8');
if (layoutContent.includes("key: '/cashier',")) {
console.log('✅ Layout.js中找到收银台菜单项');
} else {
console.log('❌ Layout.js中未找到收银台菜单项');
}
if (layoutContent.includes('function handleMenuClick')) {
console.log('✅ 找到菜单点击处理函数');
} else {
console.log('❌ 未找到菜单点击处理函数');
}
} catch (error) {
console.log('❌ 读取Layout.js失败:', error.message);
}
}
// 检查Cashier组件导入
function checkCashierImport() {
console.log('\n[3] 检查Cashier组件导入:');
try {
const cashierFilePath = path.join(__dirname, 'src', 'renderer', 'pages', 'Cashier.js');
if (fs.existsSync(cashierFilePath)) {
console.log('✅ Cashier.js文件存在');
const cashierContent = fs.readFileSync(cashierFilePath, 'utf8');
if (cashierContent.includes('export default Cashier;')) {
console.log('✅ Cashier组件正确导出');
} else {
console.log('❌ Cashier组件未正确导出');
}
} else {
console.log('❌ Cashier.js文件不存在');
}
} catch (error) {
console.log('❌ 检查Cashier组件失败:', error.message);
}
}
// 检查API服务
function checkApiService() {
console.log('\n[4] 检查API服务配置:');
try {
const apiFilePath = path.join(__dirname, 'src', 'renderer', 'services', 'api.js');
const apiContent = fs.readFileSync(apiFilePath, 'utf8');
if (apiContent.includes('function findAvailablePort()')) {
console.log('✅ 找到findAvailablePort函数');
} else {
console.log('❌ 未找到findAvailablePort函数');
}
if (apiContent.includes('const apiMethods = {')) {
console.log('✅ 找到API方法定义');
} else {
console.log('❌ 未找到API方法定义');
}
// 检查是否有detectApiPort函数可能是错误的调用
if (apiContent.includes('detectApiPort')) {
console.log('⚠️ 发现detectApiPort函数调用可能需要修改为findAvailablePort');
}
} catch (error) {
console.log('❌ 检查API服务失败:', error.message);
}
}
// 执行所有检查
checkAppRoutes();
checkLayoutMenu();
checkCashierImport();
checkApiService();
console.log('\n=== 诊断完成 ===');
console.log('请检查是否有任何❌错误项,这些可能是导致收银台菜单无响应的原因。');

View File

@ -1,173 +0,0 @@
// 诊断脚本 - 检查并解决启动问题和乱码
const fs = require('fs');
const path = require('path');
const { spawn } = require('child_process');
console.log('迷你收银台系统诊断工具');
console.log('====================\n');
// 1. 检查Node.js版本
console.log('1. 检查Node.js版本...');
try {
const nodeVersion = process.version;
const majorVersion = parseInt(nodeVersion.substring(1).split('.')[0]);
console.log(`当前Node.js版本: ${nodeVersion}`);
if (majorVersion < 16) {
console.log('❌ Node.js版本过低建议升级到16.x或更高版本');
console.log('建议从 https://nodejs.org 下载最新LTS版本');
} else {
console.log('✅ Node.js版本满足要求');
}
} catch (error) {
console.log('❌ 无法检测Node.js版本');
}
// 2. 检查项目文件
console.log('\n2. 检查项目文件...');
const requiredFiles = [
'package.json',
'src/main/main.js',
'src/server/app.js',
'src/renderer/App.js',
'src/renderer/pages/Cashier.js'
];
let filesOk = true;
requiredFiles.forEach(file => {
const filePath = path.join(__dirname, file);
if (fs.existsSync(filePath)) {
console.log(`${file}`);
} else {
console.log(`${file} 缺失`);
filesOk = false;
}
});
// 3. 检查依赖
console.log('\n3. 检查依赖...');
const nodeModulesPath = path.join(__dirname, 'node_modules');
if (fs.existsSync(nodeModulesPath)) {
console.log('✅ node_modules存在');
// 检查关键依赖
const keyDependencies = ['electron', 'react', 'antd', 'express'];
keyDependencies.forEach(dep => {
const depPath = path.join(__dirname, 'node_modules', dep);
if (fs.existsSync(depPath)) {
console.log(`${dep} 已安装`);
} else {
console.log(`${dep} 缺失`);
filesOk = false;
}
});
} else {
console.log('❌ node_modules不存在');
filesOk = false;
}
// 4. 检查数据库目录
console.log('\n4. 检查数据库目录...');
const dataPath = path.join(__dirname, 'data');
if (fs.existsSync(dataPath)) {
console.log('✅ 数据目录存在');
try {
fs.accessSync(dataPath, fs.constants.W_OK);
console.log('✅ 数据目录可写');
} catch (error) {
console.log('❌ 数据目录不可写');
console.log('请检查目录权限或以管理员身份运行');
}
} else {
console.log('📁 数据目录不存在,将自动创建');
try {
fs.mkdirSync(dataPath, { recursive: true });
console.log('✅ 数据目录已创建');
} catch (error) {
console.log('❌ 无法创建数据目录');
console.log('请检查目录权限或以管理员身份运行');
}
}
// 5. 检查字符编码
console.log('\n5. 检查字符编码...');
try {
// 读取package.json检查编码
const packageJsonPath = path.join(__dirname, 'package.json');
const packageContent = fs.readFileSync(packageJsonPath, 'utf8');
if (packageContent.includes('<27>') || packageContent.includes('?')) {
console.log('❌ 检测到编码问题package.json可能包含乱码');
} else {
console.log('✅ package.json编码正常');
}
} catch (error) {
console.log('❌ 无法检查文件编码');
}
// 6. 提供解决方案
console.log('\n6. 解决方案...');
if (!filesOk) {
console.log('发现文件或依赖问题,请按以下步骤操作:');
console.log('1. 删除node_modules文件夹');
console.log('2. 运行: npm cache clean --force');
console.log('3. 运行: npm install');
console.log('4. 尝试重新启动应用');
} else {
console.log('文件和依赖检查正常,尝试以下启动方式:');
console.log('1. 运行: node test-start.js (推荐)');
console.log('2. 运行: npm start');
console.log('3. 运行: node smart-start.js');
}
console.log('\n如果仍然遇到乱码问题请尝试');
console.log('1. 设置命令行编码: chcp 65001');
console.log('2. 或者使用PowerShell运行: node test-start.js');
console.log('3. 确保编辑器使用UTF-8编码保存文件');
// 7. 生成修复启动脚本
console.log('\n7. 生成修复启动脚本...');
const fixedStartupScript = `@echo off
chcp 65001 > nul
title 迷你收银台系统
echo 正在启动迷你收银台系统...
echo.
cd /d "%~dp0"
REM 检查node_modules是否存在
if not exist "node_modules" (
echo 首次运行正在安装依赖...
npm install
if errorlevel 1 (
echo 依赖安装失败请检查网络连接或Node.js是否正确安装
pause
exit /b 1
)
echo 依赖安装完成
echo.
)
REM 启动应用
echo 正在启动应用...
node test-start.js
if errorlevel 1 (
echo 应用启动失败请检查错误信息
pause
exit /b 1
)
echo 应用已关闭
pause
`;
const fixedScriptPath = path.join(__dirname, '启动收银台-修复版.bat');
fs.writeFileSync(fixedScriptPath, fixedStartupScript, 'utf8');
console.log(`✅ 已生成修复版启动脚本: ${fixedScriptPath}`);
console.log('\n诊断完成');
console.log('如果问题仍未解决,请查看控制台输出的具体错误信息。');

View File

@ -1,154 +0,0 @@
const fs = require('fs');
const path = require('path');
console.log('=== Electron环境路由诊断工具 ===');
// 检查Electron主进程配置
function checkElectronMain() {
console.log('\n1. 检查Electron主进程配置:');
try {
const mainPath = path.join(__dirname, 'src/main/main.js');
if (fs.existsSync(mainPath)) {
console.log(' ✅ 主进程文件存在');
const mainContent = fs.readFileSync(mainPath, 'utf8');
// 检查webPreferences配置
if (mainContent.includes('contextIsolation: true')) {
console.log(' ✅ 上下文隔离已启用');
} else {
console.log(' ⚠️ 上下文隔离未启用');
}
// 检查preload配置
if (mainContent.includes('preload:')) {
console.log(' ✅ Preload脚本已配置');
} else {
console.log(' ❌ Preload脚本未配置');
}
} else {
console.log(' ❌ 主进程文件不存在');
}
} catch (error) {
console.error(' ❌ 检查主进程配置失败:', error);
}
}
// 检查Preload脚本
function checkPreload() {
console.log('\n2. 检查Preload脚本:');
try {
const preloadPath = path.join(__dirname, 'src/main/preload.js');
if (fs.existsSync(preloadPath)) {
console.log(' ✅ Preload脚本存在');
const preloadContent = fs.readFileSync(preloadPath, 'utf8');
// 检查API暴露
if (preloadContent.includes('contextBridge.exposeInMainWorld')) {
console.log(' ✅ API已正确暴露到渲染进程');
} else {
console.log(' ❌ API未正确暴露');
}
} else {
console.log(' ❌ Preload脚本不存在');
}
} catch (error) {
console.error(' ❌ 检查Preload脚本失败:', error);
}
}
// 检查Webpack配置
function checkWebpack() {
console.log('\n3. 检查Webpack配置:');
try {
const webpackPath = path.join(__dirname, 'webpack.config.js');
if (fs.existsSync(webpackPath)) {
console.log(' ✅ Webpack配置文件存在');
const webpackContent = fs.readFileSync(webpackPath, 'utf8');
// 检查target配置
if (webpackContent.includes('target: \'electron-renderer\'')) {
console.log(' ✅ Electron渲染器目标已设置');
} else {
console.log(' ⚠️ Electron渲染器目标未设置');
}
// 检查publicPath配置
if (webpackContent.includes('publicPath: \'./\'')) {
console.log(' ✅ 相对路径已正确配置');
} else {
console.log(' ⚠️ 相对路径可能需要调整');
}
} else {
console.log(' ❌ Webpack配置文件不存在');
}
} catch (error) {
console.error(' ❌ 检查Webpack配置失败:', error);
}
}
// 检查HTML模板
function checkHtmlTemplate() {
console.log('\n4. 检查HTML模板:');
try {
const htmlPath = path.join(__dirname, 'src/renderer/index.html');
if (fs.existsSync(htmlPath)) {
console.log(' ✅ HTML模板文件存在');
const htmlContent = fs.readFileSync(htmlPath, 'utf8');
// 检查base标签
if (htmlContent.includes('<base href="./">')) {
console.log(' ✅ Base标签已正确设置');
} else {
console.log(' ⚠️ 可能需要添加<base href="./">标签');
}
} else {
console.log(' ❌ HTML模板文件不存在');
}
} catch (error) {
console.error(' ❌ 检查HTML模板失败:', error);
}
}
// 检查React Router配置
function checkReactRouter() {
console.log('\n5. 检查React Router配置:');
try {
const appPath = path.join(__dirname, 'src/renderer/App.js');
if (fs.existsSync(appPath)) {
console.log(' ✅ App.js文件存在');
const appContent = fs.readFileSync(appPath, 'utf8');
// 检查BrowserRouter
if (appContent.includes('BrowserRouter') || appContent.includes('Router')) {
console.log(' ✅ Router组件已引入');
} else {
console.log(' ❌ Router组件未引入');
}
// 检查路由配置
if (appContent.includes('Routes') && appContent.includes('Route')) {
console.log(' ✅ Routes和Route组件已配置');
} else {
console.log(' ❌ Routes和Route组件未配置');
}
} else {
console.log(' ❌ App.js文件不存在');
}
} catch (error) {
console.error(' ❌ 检查React Router配置失败:', error);
}
}
// 运行所有诊断
function runAllDiagnostics() {
checkElectronMain();
checkPreload();
checkWebpack();
checkHtmlTemplate();
checkReactRouter();
console.log('\n=== 诊断完成 ===');
console.log('如果发现❌错误,请根据提示进行修复。');
}
// 执行诊断
runAllDiagnostics();

View File

@ -1,151 +0,0 @@
/**
* 迷你收银台最终验证测试脚本
* 验证菜单功能修复是否成功
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
console.log('=== 迷你收银台最终验证测试 ===');
// 1. 检查文件是否存在
function checkFiles() {
console.log('\n1. 检查必要文件...');
const filesToCheck = [
'src/renderer/components/Layout.js',
'src/renderer/App.js',
'src/renderer/index.html',
'build/index.html',
'启动收银台.bat'
];
filesToCheck.forEach(file => {
const fullPath = path.join(__dirname, file);
if (fs.existsSync(fullPath)) {
console.log(`${file}`);
} else {
console.log(`${file}`);
}
});
}
// 2. 检查Layout.js中的关键修复
function checkLayoutFix() {
console.log('\n2. 检查Layout.js修复...');
try {
const layoutPath = path.join(__dirname, 'src', 'renderer', 'components', 'Layout.js');
const content = fs.readFileSync(layoutPath, 'utf8');
const checks = [
{ pattern: /console\.log\('菜单点击:', key\)/, desc: '菜单点击日志' },
{ pattern: /electronNavigate\(key, navigate\)/, desc: 'electronNavigate导航' },
{ pattern: /window\.location\.hash = key/, desc: 'window.location.hash导航' },
{ pattern: /window\.location\.reload\(\)/, desc: '页面刷新备选方案' }
];
checks.forEach(check => {
if (check.pattern.test(content)) {
console.log(`${check.desc}`);
} else {
console.log(`${check.desc}`);
}
});
} catch (error) {
console.error(' ❌ 检查Layout.js失败:', error.message);
}
}
// 3. 检查增强版启动脚本
function checkStartupScript() {
console.log('\n3. 检查启动脚本...');
try {
const batPath = path.join(__dirname, '启动收银台.bat');
const content = fs.readFileSync(batPath, 'utf8');
const checks = [
{ pattern: /ELECTRON_ENABLE_LOGGING=1/, desc: '启用Electron日志' },
{ pattern: /ELECTRON_FORCE_WINDOW_MENU_BAR=1/, desc: '强制窗口菜单栏' },
{ pattern: /NODE_ENV=production/, desc: '生产环境设置' },
{ pattern: /if not exist "build\\index\.html"/, desc: '自动构建检查' }
];
checks.forEach(check => {
if (check.pattern.test(content)) {
console.log(`${check.desc}`);
} else {
console.log(`${check.desc}`);
}
});
} catch (error) {
console.error(' ❌ 检查启动脚本失败:', error.message);
}
}
// 4. 检查构建文件
function checkBuild() {
console.log('\n4. 检查构建文件...');
const buildFiles = [
'build/index.html',
'build/js/main.js'
];
buildFiles.forEach(file => {
const fullPath = path.join(__dirname, file);
if (fs.existsSync(fullPath)) {
const stats = fs.statSync(fullPath);
console.log(`${file} (${(stats.size / 1024).toFixed(2)} KB)`);
} else {
console.log(`${file}`);
}
});
}
// 5. 检查依赖
function checkDependencies() {
console.log('\n5. 检查关键依赖...');
try {
const result = execSync('npm list antd react-router-dom electron', { encoding: 'utf8' });
console.log(' ✅ 关键依赖已安装');
} catch (error) {
console.log(' ⚠️ 检查依赖时出现警告:', error.message);
}
}
// 6. 显示使用说明
function showUsage() {
console.log('\n=== 使用说明 ===');
console.log('菜单功能修复已完成!');
console.log('\n现在您可以:');
console.log('1. 双击 "启动收银台.bat" 启动应用');
console.log('2. 点击左侧菜单项测试功能');
console.log('3. 如果仍有问题请查看MENU_FIX_README.md获取详细信息');
console.log('\n修复内容:');
console.log('- 增强了菜单点击处理函数');
console.log('- 提供了多重导航方案');
console.log('- 改进了错误处理机制');
console.log('- 增强了启动脚本');
}
// 主函数
function main() {
console.log('开始最终验证测试...\n');
checkFiles();
checkLayoutFix();
checkStartupScript();
checkBuild();
checkDependencies();
showUsage();
console.log('\n=== 最终验证测试完成 ===');
}
// 执行测试
main();

View File

@ -1,306 +0,0 @@
const fs = require('fs');
const path = require('path');
console.log('=== API服务修复工具 ===');
// 修复api.js文件
async function fixApiFile() {
console.log('开始修复api.js文件...');
const apiFilePath = path.join(__dirname, 'src', 'renderer', 'services', 'api.js');
try {
// 读取原始文件内容
let originalContent = fs.readFileSync(apiFilePath, 'utf8');
// 检查并修复函数调用不一致的问题
if (originalContent.includes('detectApiPort()')) {
originalContent = originalContent.replace(/detectApiPort\(\)/g, 'findAvailablePort()');
console.log('✅ 已修复函数调用不一致问题将detectApiPort()替换为findAvailablePort()');
}
// 创建修复后的文件内容
const fixedContent = `import axios from 'axios';
// 初始API端口
let currentPort = 3000; // 默认使用3000端口与服务器配置一致
// 全局调试标志
const DEBUG = true;
// 创建API实例
function createApiInstance(port) {
const api = axios.create({
baseURL: \`http://localhost:\${port}/api\`,
timeout: 15000,
headers: {
'Content-Type': 'application/json',
},
});
// 请求拦截器
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = \`Bearer \${token}\`;
}
if (DEBUG) {
console.log(\`[API调试] 发送请求: \${config.url} (端口: \${port})\`);
}
return config;
},
(error) => {
if (DEBUG) {
console.error('[API调试] 请求配置错误:', error);
}
return Promise.reject(error);
}
);
// 响应拦截器
api.interceptors.response.use(
(response) => {
if (DEBUG) {
console.log(\`[API调试] 响应成功: \${response.config.url}\`);
}
return response.data;
},
async (error) => {
if (DEBUG) {
console.error('[API调试] 请求错误:', error.message || error);
}
// 如果是网络错误且没有响应,尝试查找可用端口
if (!error.response) {
if (DEBUG) {
console.log('[API调试] 无响应,尝试查找可用端口...');
}
// 立即尝试查找可用端口
const newPort = await findAvailablePort();
if (newPort) {
// 端口已更新,重试请求
if (DEBUG) {
console.log(\`[API调试] 已切换到新端口: \${newPort},正在重试请求...\`);
}
// 更新baseURL并重试请求
const config = error.config;
config.baseURL = \`http://localhost:\${newPort}/api\`;
return axios(config); // 重试请求
}
}
// 正常错误处理
if (error.response) {
const { status, data } = error.response;
if (status === 401) {
localStorage.removeItem('token');
localStorage.removeItem('userInfo');
window.location.href = '/login';
}
return Promise.reject(data.error || '服务器错误');
}
return Promise.reject('网络错误,请检查服务器连接');
}
);
return api;
}
// 创建API实例
let apiInstance = createApiInstance(currentPort);
// 主动尝试多个端口的函数
async function findAvailablePort() {
// 尝试的端口列表(按照优先级排序)
const portsToTry = [3000, 3001, 3002, 3003];
for (const port of portsToTry) {
try {
if (DEBUG) {
console.log(\`[API调试] 尝试连接端口: \${port}\`);
}
// 简化健康检查
const response = await fetch(\`http://localhost:\${port}/api\`, {
method: 'GET',
signal: AbortSignal.timeout(2000)
});
if (response) {
if (DEBUG) {
console.log(\`[API调试] 找到可用端口: \${port}\`);
}
currentPort = port;
apiInstance = createApiInstance(port);
return port;
}
} catch (error) {
if (DEBUG) {
console.log(\`[API调试] 端口 \${port} 不可用:\`, error.message || error);
}
}
}
// 如果都不可用尝试从Electron获取端口
if (window.electronAPI && window.electronAPI.getServerPort) {
try {
if (DEBUG) {
console.log('[API调试] 尝试从Electron API获取端口');
}
const port = await window.electronAPI.getServerPort();
if (port) {
if (DEBUG) {
console.log(\`[API调试] 从Electron API获取到端口: \${port}\`);
}
currentPort = port;
apiInstance = createApiInstance(port);
return port;
}
} catch (error) {
if (DEBUG) {
console.warn('[API调试] 无法从Electron API获取端口:', error);
}
}
}
return null; // 没有找到可用端口
}
// 立即执行端口查找
findAvailablePort().then(port => {
if (port) {
if (DEBUG) {
console.log(\`[API调试] 服务器端口确定为: \${port}\`);
}
} else {
if (DEBUG) {
console.error('[API调试] 未找到可用的服务器端口');
}
}
}).catch(error => {
if (DEBUG) {
console.error('[API调试] 端口查找失败:', error);
}
});
// 添加到window对象方便调试和手动控制
window.__apiPort = currentPort;
window.__findAvailablePort = findAvailablePort;
// 添加手动切换端口的方法,方便调试
window.__switchApiPort = function(port) {
if (DEBUG) {
console.log(\`[API调试] 手动切换到端口: \${port}\`);
}
currentPort = port;
apiInstance = createApiInstance(port);
window.__apiPort = port;
return port;
}
// 简单的API方法包装器
const apiMethods = {
get: async (url, config) => {
try {
return await apiInstance.get(url, config);
} catch (error) {
// 请求失败时尝试重新检测端口
console.log('请求失败,尝试重新检测端口');
await findAvailablePort();
// 即使端口检测失败,也尝试用新配置的端口重试一次
return apiInstance.get(url, config);
}
},
post: async (url, data, config) => {
try {
return await apiInstance.post(url, data, config);
} catch (error) {
console.log('请求失败,尝试重新检测端口');
await findAvailablePort();
return apiInstance.post(url, data, config);
}
},
put: async (url, data, config) => {
try {
return await apiInstance.put(url, data, config);
} catch (error) {
console.log('请求失败,尝试重新检测端口');
await findAvailablePort();
return apiInstance.put(url, data, config);
}
},
delete: async (url, config) => {
try {
return await apiInstance.delete(url, config);
} catch (error) {
console.log('请求失败,尝试重新检测端口');
await findAvailablePort();
return apiInstance.delete(url, config);
}
},
getCurrentPort: () => currentPort
};
export const api = apiMethods;
`;
// 备份原始文件
const backupPath = apiFilePath + '.backup';
fs.writeFileSync(backupPath, originalContent);
console.log(`✅ 已备份原始文件到: ${backupPath}`);
// 写入修复后的文件
fs.writeFileSync(apiFilePath, fixedContent);
console.log('✅ api.js文件修复完成');
} catch (error) {
console.error('❌ 修复api.js文件失败:', error.message);
return false;
}
return true;
}
// 创建一个简化的启动脚本来测试修复
function createTestStartScript() {
try {
const testScriptPath = path.join(__dirname, 'test-fix.js');
const testScriptContent = `
console.log('=== 测试API修复结果 ===');
console.log('\n请运行以下命令启动应用进行测试:');
console.log('npm start');
console.log('\n如果仍然有问题请检查控制台错误信息。');
console.log('如果需要恢复原始文件,可以运行:');
console.log('node restore-api.js');
`;
fs.writeFileSync(testScriptPath, testScriptContent);
console.log(`✅ 已创建测试脚本: ${testScriptPath}`);
} catch (error) {
console.error('❌ 创建测试脚本失败:', error.message);
}
}
// 执行修复
async function runFix() {
const apiFixed = await fixApiFile();
if (apiFixed) {
createTestStartScript();
console.log('\n=== 修复完成 ===');
console.log('修复内容:');
console.log('1. 确保findAvailablePort函数正确实现和调用');
console.log('2. 优化了错误处理逻辑');
console.log('3. 增强了端口检测的稳定性');
console.log('\n请重新启动应用测试修复效果。');
} else {
console.log('\n=== 修复失败 ===');
console.log('请手动检查api.js文件。');
}
}
runFix();

View File

@ -1,326 +0,0 @@
const fs = require('fs');
const path = require('path');
console.log('=== Electron路由修复工具 ===');
// 修复Layout.js中的菜单点击处理函数
function fixLayoutMenuClick() {
console.log('\n1. 修复Layout.js菜单点击处理函数...');
try {
const layoutPath = path.join(__dirname, 'src/renderer/components/Layout.js');
if (fs.existsSync(layoutPath)) {
let content = fs.readFileSync(layoutPath, 'utf8');
// 查找现有的handleMenuClick函数
const oldFunction = `function handleMenuClick({ key }) {
console.log('菜单点击:', key);
try {
navigate(key);
console.log('导航成功到:', key);
} catch (error) {
console.error('导航失败:', error);
// 直接使用window.location作为备选方案
window.location.href = key;
}
}`;
// 新的增强版函数
const newFunction = `function handleMenuClick({ key }) {
console.log('菜单点击:', key);
// 在Electron环境中使用更可靠的导航方式
try {
// 首先尝试使用React Router导航
navigate(key);
console.log('React Router导航成功到:', key);
} catch (routerError) {
console.error('React Router导航失败:', routerError);
// 如果React Router失败尝试使用window.location
try {
// 对于相对路径确保在Electron环境中正确处理
if (key.startsWith('/')) {
// 在Electron中我们需要使用hash模式或者特殊处理
const baseUrl = window.location.origin + window.location.pathname;
const newUrl = baseUrl.replace(/\\/[^\\/]*$/, '') + '#' + key;
window.location.hash = key;
} else {
window.location.href = key;
}
console.log('备用导航方式成功');
} catch (locationError) {
console.error('备用导航方式也失败了:', locationError);
// 最后的备选方案
window.location.reload();
}
}
}`;
// 替换函数
if (content.includes(oldFunction)) {
content = content.replace(oldFunction, newFunction);
fs.writeFileSync(layoutPath, content, 'utf8');
console.log(' ✅ Layout.js菜单点击处理函数已修复');
} else {
console.log(' ⚠️ 未找到预期的handleMenuClick函数可能已被修改');
}
} else {
console.log(' ❌ Layout.js文件不存在');
}
} catch (error) {
console.error(' ❌ 修复Layout.js失败:', error);
}
}
// 修复App.js中的路由配置
function fixAppRouting() {
console.log('\n2. 修复App.js路由配置...');
try {
const appPath = path.join(__dirname, 'src/renderer/App.js');
if (fs.existsSync(appPath)) {
let content = fs.readFileSync(appPath, 'utf8');
// 检查是否已经使用HashRouter
if (!content.includes('HashRouter')) {
// 替换BrowserRouter为HashRouter
content = content.replace(/BrowserRouter/g, 'HashRouter');
// 更新导入语句
if (content.includes("import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';")) {
content = content.replace(
"import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';",
"import { HashRouter as Router, Routes, Route } from 'react-router-dom';"
);
} else if (content.includes("import { BrowserRouter, Routes, Route } from 'react-router-dom';")) {
content = content.replace(
"import { BrowserRouter, Routes, Route } from 'react-router-dom';",
"import { HashRouter, Routes, Route } from 'react-router-dom';"
);
}
fs.writeFileSync(appPath, content, 'utf8');
console.log(' ✅ App.js路由配置已修复使用HashRouter');
} else {
console.log(' ✅ App.js已使用HashRouter无需修改');
}
} else {
console.log(' ❌ App.js文件不存在');
}
} catch (error) {
console.error(' ❌ 修复App.js路由配置失败:', error);
}
}
// 创建Electron环境下的路由助手
function createRouteHelper() {
console.log('\n3. 创建Electron路由助手...');
try {
const helperPath = path.join(__dirname, 'src/renderer/utils/electron-router.js');
// 确保utils目录存在
const utilsDir = path.dirname(helperPath);
if (!fs.existsSync(utilsDir)) {
fs.mkdirSync(utilsDir, { recursive: true });
}
const helperContent = `/**
* Electron环境路由助手
* 提供在Electron桌面应用中更可靠的路由导航功能
*/
/**
* 在Electron环境中安全地导航到指定路径
* @param {string} path - 目标路径
* @param {object} navigate - React Router的navigate函数
*/
export function electronNavigate(path, navigate) {
console.log('Electron导航到:', path);
try {
// 首先尝试使用React Router
if (navigate && typeof navigate === 'function') {
navigate(path);
console.log('React Router导航成功');
return;
}
} catch (routerError) {
console.warn('React Router导航失败:', routerError);
}
// 备用方案使用window.location
try {
if (path.startsWith('/')) {
// 对于绝对路径在Electron中使用hash模式
window.location.hash = path;
} else {
// 相对路径直接跳转
window.location.href = path;
}
console.log('备用导航方式成功');
} catch (locationError) {
console.error('备用导航方式失败:', locationError);
// 最后方案:刷新页面
window.location.reload();
}
}
/**
* 获取当前路由路径
* @returns {string} 当前路径
*/
export function getCurrentRoute() {
// 在Electron中我们可能需要从hash中获取路径
if (window.location.hash) {
return window.location.hash.substring(1); // 移除开头的#
}
return window.location.pathname;
}
/**
* 初始化Electron路由环境
*/
export function initElectronRouter() {
console.log('初始化Electron路由环境');
// 确保基础href设置正确
let base = document.querySelector('base');
if (!base) {
base = document.createElement('base');
base.href = './';
document.head.appendChild(base);
}
// 监听路由变化
window.addEventListener('hashchange', () => {
console.log('路由变化到:', window.location.hash);
});
}
export default {
electronNavigate,
getCurrentRoute,
initElectronRouter
};`;
fs.writeFileSync(helperPath, helperContent, 'utf8');
console.log(' ✅ Electron路由助手已创建');
} catch (error) {
console.error(' ❌ 创建Electron路由助手失败:', error);
}
}
// 更新Layout.js使用新的路由助手
function updateLayoutWithHelper() {
console.log('\n4. 更新Layout.js使用路由助手...');
try {
const layoutPath = path.join(__dirname, 'src/renderer/components/Layout.js');
if (fs.existsSync(layoutPath)) {
let content = fs.readFileSync(layoutPath, 'utf8');
// 添加导入语句
const importStatement = "import { electronNavigate } from '../utils/electron-router';";
if (!content.includes(importStatement)) {
// 在其他导入语句之后添加
content = content.replace(
"import { useNavigate, useLocation } from 'react-router-dom';",
"import { useNavigate, useLocation } from 'react-router-dom';\nimport { electronNavigate } from '../utils/electron-router';"
);
}
// 更新handleMenuClick函数
const oldFunction = `function handleMenuClick({ key }) {
console.log('菜单点击:', key);
// 在Electron环境中使用更可靠的导航方式
try {
// 首先尝试使用React Router导航
navigate(key);
console.log('React Router导航成功到:', key);
} catch (routerError) {
console.error('React Router导航失败:', routerError);
// 如果React Router失败尝试使用window.location
try {
// 对于相对路径确保在Electron环境中正确处理
if (key.startsWith('/')) {
// 在Electron中我们需要使用hash模式或者特殊处理
const baseUrl = window.location.origin + window.location.pathname;
const newUrl = baseUrl.replace(/\\/[^\\/]*$/, '') + '#' + key;
window.location.hash = key;
} else {
window.location.href = key;
}
console.log('备用导航方式成功');
} catch (locationError) {
console.error('备用导航方式也失败了:', locationError);
// 最后的备选方案
window.location.reload();
}
}
}`;
const newFunction = `function handleMenuClick({ key }) {
console.log('菜单点击:', key);
// 使用Electron路由助手进行导航
electronNavigate(key, navigate);
}`;
if (content.includes(oldFunction)) {
content = content.replace(oldFunction, newFunction);
fs.writeFileSync(layoutPath, content, 'utf8');
console.log(' ✅ Layout.js已更新使用路由助手');
} else {
console.log(' ⚠️ 未找到预期的handleMenuClick函数可能已被修改');
}
} else {
console.log(' ❌ Layout.js文件不存在');
}
} catch (error) {
console.error(' ❌ 更新Layout.js失败:', error);
}
}
// 更新package.json添加构建脚本
function updatePackageJson() {
console.log('\n5. 更新package.json...');
try {
const packagePath = path.join(__dirname, 'package.json');
if (fs.existsSync(packagePath)) {
const packageData = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
// 添加Electron专用的构建脚本
if (!packageData.scripts['build-electron']) {
packageData.scripts['build-electron'] = 'npm run build-renderer && npm run build-main';
}
// 确保homepage设置正确
packageData.homepage = './';
fs.writeFileSync(packagePath, JSON.stringify(packageData, null, 2), 'utf8');
console.log(' ✅ package.json已更新');
} else {
console.log(' ❌ package.json文件不存在');
}
} catch (error) {
console.error(' ❌ 更新package.json失败:', error);
}
}
// 主函数
function main() {
console.log('开始修复Electron环境中的路由问题...\n');
fixLayoutMenuClick();
fixAppRouting();
createRouteHelper();
updateLayoutWithHelper();
updatePackageJson();
console.log('\n=== 路由修复完成 ===');
console.log('请重新构建并启动应用以使更改生效:');
console.log(' 1. npm run build');
console.log(' 2. npm start');
}
// 执行修复
main();

View File

@ -1,184 +0,0 @@
/**
* 菜单功能修复脚本
* 修复"启动收银台.bat"启动后菜单功能无法正常使用的问题
*/
const fs = require('fs');
const path = require('path');
console.log('=== 菜单功能修复工具 ===');
// 修复Layout.js中的菜单点击处理函数
function fixLayoutMenuClick() {
console.log('\n1. 修复Layout.js菜单点击处理函数...');
try {
const layoutPath = path.join(__dirname, 'src', 'renderer', 'components', 'Layout.js');
if (fs.existsSync(layoutPath)) {
let content = fs.readFileSync(layoutPath, 'utf8');
// 备份原文件
fs.writeFileSync(layoutPath + '.backup', content);
console.log(' ✅ 已备份原文件');
// 查找现有的handleMenuClick函数
const oldFunction = `const handleMenuClick = ({ key }) => {
try {
// 使用electronNavigate处理导航
electronNavigate(key, navigate);
} catch (error) {
console.error('菜单导航失败:', error);
}
};`;
// 新的增强版函数
const newFunction = `const handleMenuClick = ({ key }) => {
console.log('菜单点击:', key);
try {
// 首先尝试使用electronNavigate处理导航
if (typeof electronNavigate === 'function') {
electronNavigate(key, navigate);
console.log('通过electronNavigate导航成功');
return;
}
} catch (error) {
console.warn('electronNavigate导航失败:', error);
}
// 备用方案1: 直接使用React Router的navigate
try {
if (navigate && typeof navigate === 'function') {
navigate(key);
console.log('通过React Router导航成功');
return;
}
} catch (error) {
console.warn('React Router导航失败:', error);
}
// 备用方案2: 使用window.location.hash
try {
if (key.startsWith('/')) {
window.location.hash = key;
console.log('通过window.location.hash导航成功');
return;
}
} catch (error) {
console.warn('window.location.hash导航失败:', error);
}
// 最后的备选方案: 刷新页面
console.error('所有导航方案都失败了,将刷新页面');
window.location.reload();
};`;
// 替换函数
if (content.includes(oldFunction)) {
content = content.replace(oldFunction, newFunction);
fs.writeFileSync(layoutPath, content, 'utf8');
console.log(' ✅ Layout.js菜单点击处理函数已修复');
} else {
console.log(' ⚠️ 未找到预期的handleMenuClick函数可能已被修改');
// 尝试更通用的替换
const genericPattern = /const handleMenuClick = \({ key }\) => {[\s\S]*?};/;
if (genericPattern.test(content)) {
content = content.replace(genericPattern, newFunction);
fs.writeFileSync(layoutPath, content, 'utf8');
console.log(' ✅ Layout.js菜单点击处理函数已通过通用模式修复');
} else {
console.log(' ❌ 无法定位handleMenuClick函数进行修复');
}
}
} else {
console.log(' ❌ Layout.js文件不存在');
}
} catch (error) {
console.error(' ❌ 修复Layout.js失败:', error);
}
}
// 修复App.js中的路由配置
function fixAppRouting() {
console.log('\n2. 检查App.js路由配置...');
try {
const appPath = path.join(__dirname, 'src', 'renderer', 'App.js');
if (fs.existsSync(appPath)) {
let content = fs.readFileSync(appPath, 'utf8');
// 检查是否使用HashRouter
if (content.includes('HashRouter as Router')) {
console.log(' ✅ 已正确使用HashRouter');
} else if (content.includes('BrowserRouter as Router')) {
console.log(' ⚠️ 使用了BrowserRouter可能需要改为HashRouter');
} else {
console.log(' ⚠️ 未明确使用HashRouter或BrowserRouter');
}
// 检查路由配置
const routePattern = /<Route path="\/cashier" element={<Cashier \/>} \/>/;
if (routePattern.test(content)) {
console.log(' ✅ 收银台路由配置正确');
} else {
console.log(' ⚠️ 收银台路由配置可能存在问题');
}
} else {
console.log(' ❌ App.js文件不存在');
}
} catch (error) {
console.error(' ❌ 检查App.js失败:', error);
}
}
// 更新HTML模板确保基础路径正确
function updateHtmlTemplate() {
console.log('\n3. 检查HTML模板...');
try {
const htmlPath = path.join(__dirname, 'src', 'renderer', 'index.html');
if (fs.existsSync(htmlPath)) {
let content = fs.readFileSync(htmlPath, 'utf8');
// 检查base标签
if (content.includes('<base href="./">')) {
console.log(' ✅ base标签已正确设置');
} else {
// 添加base标签
const headEndIndex = content.indexOf('</head>');
if (headEndIndex > 0) {
const baseTag = '\n <base href="./">\n';
content = content.slice(0, headEndIndex) + baseTag + content.slice(headEndIndex);
fs.writeFileSync(htmlPath, content, 'utf8');
console.log(' ✅ 已添加base标签');
} else {
console.log(' ⚠️ 无法定位head标签未添加base标签');
}
}
} else {
console.log(' ❌ index.html文件不存在');
}
} catch (error) {
console.error(' ❌ 检查HTML模板失败:', error);
}
}
// 主函数
function main() {
console.log('开始修复菜单功能问题...\n');
fixLayoutMenuClick();
fixAppRouting();
updateHtmlTemplate();
console.log('\n=== 菜单功能修复完成 ===');
console.log('修复内容:');
console.log('1. 增强了Layout.js中的菜单点击处理函数添加多重备选导航方案');
console.log('2. 确保使用HashRouter以兼容Electron环境');
console.log('3. 在HTML模板中添加了正确的base标签');
console.log('\n请重新构建并启动应用以使更改生效:');
console.log(' 1. npm run build');
console.log(' 2. npm start');
console.log('\n或者直接运行"启动收银台.bat"');
}
// 执行修复
main();

View File

@ -1,125 +0,0 @@
console.log('=== 导航菜单修复工具 ===');
const fs = require('fs');
const path = require('path');
// 修复Layout.js中的菜单导航问题
function fixLayoutNavigation() {
console.log('\n1. 修复Layout.js菜单导航...');
const layoutPath = path.join(__dirname, 'src', 'renderer', 'components', 'Layout.js');
try {
// 读取Layout.js内容
let content = fs.readFileSync(layoutPath, 'utf8');
// 确保handleMenuClick函数正确实现
const oldHandleMenuClick = /function handleMenuClick\(\{ key \}\) \{[\s\S]*?navigate\(key\);[\s\S]*?\}/;
if (oldHandleMenuClick.test(content)) {
const newHandleMenuClick = `function handleMenuClick({ key }) {
console.log('菜单点击:', key);
try {
navigate(key);
console.log('导航成功到:', key);
} catch (error) {
console.error('导航失败:', error);
// 直接使用window.location作为备选方案
window.location.href = key;
}
}`;
content = content.replace(oldHandleMenuClick, newHandleMenuClick);
console.log('✅ 已增强菜单点击处理函数');
}
// 确保Menu组件配置正确
const oldMenuConfig = /<Menu[\s\S]*?selectedKeys={\[location\.pathname\]}/;
if (oldMenuConfig.test(content)) {
const newMenuConfig = '<Menu\n mode="inline"\n selectedKeys={[location.pathname]}\n items={menuItems}\n onClick={handleMenuClick}\n style={{ height: \'100%\', borderRight: 0 }}\n forceRender={true}\n autoOpenKeys={[]}';
content = content.replace(/<Menu[\s\S]*?style={{ height: '100%', borderRight: 0 }}>/, newMenuConfig);
console.log('✅ 已增强Menu组件配置');
}
// 备份并写入修复后的内容
fs.writeFileSync(layoutPath + '.backup', fs.readFileSync(layoutPath, 'utf8'));
fs.writeFileSync(layoutPath, content);
console.log('✅ Layout.js修复完成');
} catch (error) {
console.error('❌ 修复Layout.js失败:', error.message);
}
}
// 修复App.js中的路由配置
function fixAppRoutes() {
console.log('\n2. 修复App.js路由配置...');
const appPath = path.join(__dirname, 'src', 'renderer', 'App.js');
try {
// 确保路由配置正确
let content = fs.readFileSync(appPath, 'utf8');
// 增强错误处理和调试
const oldRenderTryBlock = /try \{[\s\S]*?return \([\s\S]*?<Routes>/;
if (oldRenderTryBlock.test(content)) {
const debugInfo = `// 添加路由调试
console.log('渲染路由配置');
window.__routesDebug = {
user: mockUser,
location: window.location.pathname,
timestamp: new Date().toISOString()
};`;
// 在return前添加调试信息
content = content.replace('return (\n <ConfigProvider locale={zhCN}>', `${debugInfo}\n return (\n <ConfigProvider locale={zhCN}>`);
console.log('✅ 已添加路由调试信息');
}
fs.writeFileSync(appPath + '.fix-backup', fs.readFileSync(appPath, 'utf8'));
fs.writeFileSync(appPath, content);
console.log('✅ App.js修复完成');
} catch (error) {
console.error('❌ 修复App.js失败:', error.message);
}
}
// 创建一个简单的启动脚本来测试修复
function createTestScript() {
try {
const testScriptContent = `
@echo off
chcp 65001
cd %~dp0
echo 测试导航菜单修复...
echo 请按F12打开开发者工具查看控制台输出
npm start
pause
`;
fs.writeFileSync(path.join(__dirname, 'test-navigation.bat'), testScriptContent);
console.log('✅ 已创建测试脚本: test-navigation.bat');
} catch (error) {
console.error('❌ 创建测试脚本失败:', error.message);
}
}
// 主函数
function runFix() {
console.log('开始修复导航菜单问题...');
fixLayoutNavigation();
fixAppRoutes();
createTestScript();
console.log('\n=== 修复完成 ===');
console.log('1. 已增强菜单点击处理函数,添加错误处理和备选导航方案');
console.log('2. 已优化Menu组件配置');
console.log('3. 已添加详细的调试日志');
console.log('\n请运行 test-navigation.bat 测试修复效果');
console.log('修复思路: 增强导航错误处理,添加备选方案,便于调试');
}
runFix();

View File

@ -1,174 +0,0 @@
import { productService } from '../src/renderer/services';
import { orderService } from '../src/renderer/services';
import { authService } from '../src/renderer/services';
// 模拟测试环境
const setupTestEnvironment = () => {
// 模拟localStorage
const localStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
clear: jest.fn(),
};
global.localStorage = localStorageMock;
// 模拟fetch
global.fetch = jest.fn();
// 设置默认用户信息
const mockUser = {
id: 1,
username: 'admin',
name: '管理员',
role: 'admin'
};
localStorageMock.getItem.mockImplementation((key) => {
if (key === 'userInfo') {
return JSON.stringify(mockUser);
}
return null;
});
};
// 测试API连接
const testApiConnection = async () => {
console.log('测试API连接...');
try {
const commonPorts = [3000, 3001, 3002, 9876, 8080];
for (const port of commonPorts) {
try {
const response = await fetch(`http://localhost:${port}/api/health`, {
method: 'GET',
signal: AbortSignal.timeout(1000)
});
if (response.ok) {
console.log(`✅ API连接成功端口: ${port}`);
return port;
}
} catch (error) {
// 端口不可用,继续尝试下一个
}
}
console.error('❌ 无法连接到API服务器');
return null;
} catch (error) {
console.error('❌ API连接测试失败:', error);
return null;
}
};
// 测试认证功能
const testAuth = async () => {
console.log('测试认证功能...');
try {
// 测试登录
const loginData = await authService.login('admin', 'admin123');
if (loginData && loginData.id) {
console.log('✅ 用户登录功能正常');
} else {
console.error('❌ 用户登录功能异常');
}
// 测试获取当前用户
const currentUser = authService.getCurrentUser();
if (currentUser) {
console.log('✅ 获取当前用户功能正常');
} else {
console.error('❌ 获取当前用户功能异常');
}
} catch (error) {
console.error('❌ 认证测试失败:', error);
}
};
// 测试商品功能
const testProducts = async () => {
console.log('测试商品功能...');
try {
// 测试获取商品列表
const products = await productService.getProducts();
if (products && Array.isArray(products.data)) {
console.log(`✅ 商品列表获取成功,共 ${products.data.length} 个商品`);
// 测试获取商品分类
const categories = await productService.getCategories();
if (categories && Array.isArray(categories)) {
console.log(`✅ 商品分类获取成功,共 ${categories.length} 个分类`);
} else {
console.error('❌ 商品分类获取失败');
}
} else {
console.error('❌ 商品列表获取失败');
}
} catch (error) {
console.error('❌ 商品功能测试失败:', error);
}
};
// 测试订单功能
const testOrders = async () => {
console.log('测试订单功能...');
try {
// 测试获取订单列表
const orders = await orderService.getOrders();
if (orders && Array.isArray(orders.data)) {
console.log(`✅ 订单列表获取成功,共 ${orders.data.length} 个订单`);
} else {
console.error('❌ 订单列表获取失败');
}
// 测试获取挂单列表
const heldOrders = await orderService.getHeldOrders();
if (heldOrders && Array.isArray(heldOrders)) {
console.log(`✅ 挂单列表获取成功,共 ${heldOrders.length} 个挂单`);
} else {
console.error('❌ 挂单列表获取失败');
}
} catch (error) {
console.error('❌ 订单功能测试失败:', error);
}
};
// 运行所有测试
const runTests = async () => {
console.log('开始迷你收银台系统功能测试...\n');
setupTestEnvironment();
// 测试API连接
const apiPort = await testApiConnection();
if (!apiPort) {
console.error('\n❌ API连接失败无法继续测试');
return;
}
console.log('\n');
// 测试各项功能
await testAuth();
await testProducts();
await testOrders();
console.log('\n测试完成');
};
// 如果直接运行此文件,执行测试
if (require.main === module) {
runTests().catch(error => {
console.error('测试执行失败:', error);
process.exit(1);
});
}
module.exports = {
setupTestEnvironment,
testApiConnection,
testAuth,
testProducts,
testOrders,
runTests
};

View File

@ -1,59 +0,0 @@
const http = require('http');
// 测试获取所有设置
console.log('Testing GET /api/settings');
const getSettingsReq = http.get('http://localhost:3002/api/settings', (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('GET /api/settings response:', data);
});
});
getSettingsReq.on('error', (err) => {
console.error('GET /api/settings error:', err.message);
});
// 测试创建订单
console.log('Testing POST /api/orders');
const orderData = JSON.stringify({
"items": [
{
"product_id": 1,
"quantity": 2,
"unit_price": 10.00
}
],
"total_amount": 20.00,
"paid_amount": 20.00,
"payment_method": "cash",
"cashier_id": 1
});
const postOrderReq = http.request({
hostname: 'localhost',
port: 3002,
path: '/api/orders',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(orderData)
}
}, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('POST /api/orders response:', data);
});
});
postOrderReq.on('error', (err) => {
console.error('POST /api/orders error:', err.message);
});
postOrderReq.write(orderData);
postOrderReq.end();

View File

@ -1,61 +0,0 @@
const axios = require('axios');
// 使用服务器实际运行的端口根据netstat检查结果服务器在3000端口运行
const API_BASE_URL = 'http://localhost:3000/api';
// 模拟现金支付的订单数据
const orderData = {
items: [
{
product_id: 1,
quantity: 2,
price: 10.00
}
],
total_amount: 20.00,
payment_method: 'cash',
received_amount: 25.00,
change: 5.00
};
async function testCashPayment() {
console.log('开始测试现金支付流程...');
try {
// 发送创建订单请求
console.log('正在发送现金支付订单请求...');
console.log('请求URL:', `${API_BASE_URL}/orders`);
console.log('请求数据:', JSON.stringify(orderData, null, 2));
const response = await axios.post(`${API_BASE_URL}/orders`, orderData);
console.log('订单创建成功:');
console.log('订单ID:', response.data.id);
console.log('订单总额:', response.data.total_amount);
console.log('支付方式:', response.data.payment_method);
console.log('\n请检查钱箱是否已打开。如果钱箱正常打开说明功能工作正常。');
console.log('如果钱箱未打开,请检查以下几点:');
console.log('1. 钱箱是否正确连接到电脑');
console.log('2. 钱箱连接的串口是否正确配置');
console.log('3. 钱箱是否处于可用状态');
} catch (error) {
console.error('测试过程中出现错误:');
console.error('错误类型:', error.constructor.name);
if (error.response) {
console.error('状态码:', error.response.status);
console.error('响应头:', JSON.stringify(error.response.headers, null, 2));
console.error('响应数据:', JSON.stringify(error.response.data, null, 2));
} else if (error.request) {
console.error('请求信息:', error.request);
console.error('请检查服务器是否正在运行');
} else {
console.error('错误信息:', error.message);
}
}
}
// 运行测试
testCashPayment();

View File

@ -1,56 +0,0 @@
const axios = require('axios');
// API基础URL - 使用正确的端口
const API_BASE_URL = 'http://localhost:3001/api';
console.log('开始测试现金支付流程...');
// 测试数据
const orderData = {
items: [
{
product_id: 1,
quantity: 2
}
],
payment_method: 'cash',
paid_amount: 10,
cashier_id: 1
};
console.log('准备创建订单:', orderData);
// 创建订单
axios.post(`${API_BASE_URL}/orders`, orderData)
.then(response => {
console.log('订单创建成功:', response.data);
// 检查响应数据
if (response.data && response.data.id) {
console.log(`订单ID: ${response.data.id}`);
console.log(`订单总额: ${response.data.total_amount}`);
console.log(`支付方式: ${response.data.payment_method}`);
console.log('现金支付测试完成');
} else {
console.error('订单创建响应格式不正确:', response.data);
}
})
.catch(error => {
console.error('订单创建失败:');
if (error.response) {
// 服务器响应了错误状态码
console.error('错误状态:', error.response.status);
console.error('错误数据:', error.response.data);
console.error('响应头:', error.response.headers);
} else if (error.request) {
// 请求已发出但没有收到响应
console.error('无响应:', error.request);
} else {
// 其他错误
console.error('错误信息:', error.message);
}
console.error('错误配置:', error.config);
});

View File

@ -1,31 +0,0 @@
const axios = require('axios');
async function testCreateOrder() {
try {
const response = await axios.post('http://localhost:3001/api/orders', {
items: [
{
product_id: 1,
quantity: 2
}
],
total_amount: 7.00,
paid_amount: 7.00,
payment_method: 'cash'
}, {
headers: {
'Content-Type': 'application/json'
}
});
console.log('Order created successfully:', response.data);
} catch (error) {
if (error.response) {
console.error('Error creating order:', error.response.status, error.response.data);
} else {
console.error('Error creating order:', error.message);
}
}
}
testCreateOrder();

View File

@ -1,9 +0,0 @@
console.log('=== 测试API修复结果 ===');
console.log('
请运行以下命令启动应用进行测试:');
console.log('npm start');
console.log('
如果仍然有问题请检查控制台错误信息');
console.log('如果需要恢复原始文件,可以运行:');
console.log('node restore-api.js');

View File

@ -1,71 +0,0 @@
const axios = require('axios');
const fs = require('fs');
const path = require('path');
// 读取服务器端口
function getServerPort() {
try {
const portFile = path.join(__dirname, 'data', 'server-port.json');
if (fs.existsSync(portFile)) {
const portData = JSON.parse(fs.readFileSync(portFile, 'utf8'));
return portData.port || 3001;
}
} catch (error) {
console.log('读取端口文件失败:', error.message);
}
return 3001;
}
async function testFullCheckout() {
const port = getServerPort();
const baseURL = `http://localhost:${port}/api`;
console.log('开始测试完整结账流程...');
console.log('服务器地址:', baseURL);
try {
// 1. 登录获取用户信息
console.log('\n1. 尝试登录...');
const loginResponse = await axios.post(`${baseURL}/cashiers/login`, {
username: 'cashier1',
password: 'cashier123'
});
console.log('登录成功:', loginResponse.data);
const userInfo = loginResponse.data;
// 2. 创建订单
console.log('\n2. 创建订单...');
const orderData = {
items: [
{
product_id: 1,
quantity: 2
}
],
payment_method: 'cash',
cashier_id: userInfo.id,
paid_amount: 20,
discount_amount: 0
};
console.log('订单数据:', JSON.stringify(orderData, null, 2));
const orderResponse = await axios.post(`${baseURL}/orders`, orderData);
console.log('订单创建成功:', orderResponse.data);
console.log('\n测试完成');
} catch (error) {
console.error('\n测试失败:');
if (error.response) {
console.error('响应状态:', error.response.status);
console.error('响应数据:', error.response.data);
} else if (error.request) {
console.error('请求失败:', error.message);
} else {
console.error('错误:', error.message);
}
}
}
testFullCheckout();

View File

@ -1,19 +0,0 @@
// 测试服务器进程向主进程发送消息的功能
console.log('测试服务器进程向主进程发送消息...');
// 检查是否有process.send方法
if (process.send) {
console.log('process.send方法存在尝试发送测试消息...');
// 发送测试消息
process.send({ type: 'TEST_MESSAGE', data: '这是一条测试消息' });
console.log('测试消息已发送');
// 发送OPEN_CASH_DRAWER消息
process.send({ type: 'OPEN_CASH_DRAWER' });
console.log('OPEN_CASH_DRAWER消息已发送');
} else {
console.log('process.send方法不存在当前环境不支持向父进程发送消息');
}
console.log('测试脚本执行完毕');

View File

@ -1,17 +0,0 @@
const logger = require('./src/server/utils/logger');
console.log('开始测试新的日志系统...\n');
// 测试不同级别的日志
logger.debug('这是一个调试信息', { timestamp: new Date().toISOString() });
logger.info('这是一个普通信息', { user: 'test', action: 'login' });
logger.warn('这是一个警告信息', { issue: '低库存', product: '可口可乐' });
logger.error('这是一个错误信息', { error: '数据库连接失败', code: 500 });
console.log('\n日志测试完成');
console.log('请检查 data/logs 目录下的日志文件:');
console.log('- error-YYYY-MM-DD.log (错误日志)');
console.log('- warn-YYYY-MM-DD.log (警告日志)');
console.log('- info-YYYY-MM-DD.log (信息日志)');
console.log('- debug-YYYY-MM-DD.log (调试日志)');
console.log('- combined-YYYY-MM-DD.log (综合日志)');

View File

@ -1,97 +0,0 @@
/**
* 菜单功能修复验证脚本
*/
const fs = require('fs');
const path = require('path');
console.log('=== 菜单功能修复验证 ===');
// 检查Layout.js修复结果
function checkLayoutFix() {
console.log('\n1. 检查Layout.js修复结果...');
try {
const layoutPath = path.join(__dirname, 'src', 'renderer', 'components', 'Layout.js');
if (fs.existsSync(layoutPath)) {
const content = fs.readFileSync(layoutPath, 'utf8');
// 检查是否包含增强的错误处理
if (content.includes('console.log(\'菜单点击:\', key)')) {
console.log(' ✅ 菜单点击处理函数已增强');
} else {
console.log(' ⚠️ 菜单点击处理函数可能未正确修复');
}
// 检查多重备选方案
if (content.includes('window.location.hash = key')) {
console.log(' ✅ 已添加window.location.hash备选方案');
} else {
console.log(' ⚠️ 未找到window.location.hash备选方案');
}
// 检查是否包含最后的备选方案
if (content.includes('window.location.reload()')) {
console.log(' ✅ 已添加页面刷新备选方案');
} else {
console.log(' ⚠️ 未找到页面刷新备选方案');
}
} else {
console.log(' ❌ Layout.js文件不存在');
}
} catch (error) {
console.error(' ❌ 检查Layout.js失败:', error);
}
}
// 检查HTML模板
function checkHtmlTemplate() {
console.log('\n2. 检查HTML模板...');
try {
const htmlPath = path.join(__dirname, 'src', 'renderer', 'index.html');
if (fs.existsSync(htmlPath)) {
const content = fs.readFileSync(htmlPath, 'utf8');
if (content.includes('<base href="./">')) {
console.log(' ✅ base标签已正确设置');
} else {
console.log(' ⚠️ base标签可能未正确设置');
}
} else {
console.log(' ❌ index.html文件不存在');
}
} catch (error) {
console.error(' ❌ 检查HTML模板失败:', error);
}
}
// 检查备份文件
function checkBackup() {
console.log('\n3. 检查备份文件...');
try {
const backupPath = path.join(__dirname, 'src', 'renderer', 'components', 'Layout.js.backup');
if (fs.existsSync(backupPath)) {
console.log(' ✅ 已创建备份文件');
} else {
console.log(' ⚠️ 未找到备份文件');
}
} catch (error) {
console.error(' ❌ 检查备份文件失败:', error);
}
}
// 主函数
function main() {
console.log('开始验证菜单功能修复...\n');
checkLayoutFix();
checkHtmlTemplate();
checkBackup();
console.log('\n=== 验证完成 ===');
console.log('如果所有检查都通过✅,菜单功能应该可以正常工作。');
console.log('如果有任何⚠️,建议重新运行修复脚本。');
console.log('\n接下来请运行"启动收银台.bat"测试菜单功能。');
}
// 执行验证
main();

View File

@ -1,115 +0,0 @@
/**
* 菜单导航功能测试脚本
* 用于验证Electron环境中菜单点击是否正常工作
*/
console.log('=== 菜单导航功能测试 ===');
// 测试函数
function runTests() {
console.log('\n开始测试...');
// 测试1: 检查必要的函数和对象是否存在
console.log('\n1. 检查必要组件:');
if (typeof window.electronNavigate === 'function') {
console.log(' ✅ electronNavigate函数存在');
} else {
console.log(' ❌ electronNavigate函数不存在');
}
if (window.location) {
console.log(' ✅ window.location对象存在');
} else {
console.log(' ❌ window.location对象不存在');
}
// 测试2: 模拟菜单点击
console.log('\n2. 模拟菜单点击测试:');
try {
// 模拟点击收银台菜单
console.log(' 模拟点击收银台菜单...');
const cashierEvent = { key: '/cashier' };
// 如果electronNavigate存在使用它进行测试
if (typeof window.electronNavigate === 'function') {
console.log(' 使用electronNavigate进行导航测试...');
// 这里只是测试函数调用,不实际导航
console.log(' ✅ electronNavigate可以被调用');
} else {
console.log(' ⚠️ electronNavigate不存在跳过测试');
}
} catch (error) {
console.error(' ❌ 模拟菜单点击测试失败:', error);
}
// 测试3: 检查当前路由
console.log('\n3. 检查当前路由:');
try {
const currentPath = window.location.hash || window.location.pathname;
console.log(` 当前路径: ${currentPath}`);
if (currentPath.includes('cashier')) {
console.log(' ✅ 当前在收银台页面');
} else {
console.log(' 当前不在收银台页面');
}
} catch (error) {
console.error(' ❌ 检查当前路由失败:', error);
}
// 测试4: 检查菜单组件
console.log('\n4. 检查菜单组件:');
try {
// 查找菜单元素
const menuElements = document.querySelectorAll('.ant-menu-item');
console.log(` 找到 ${menuElements.length} 个菜单项`);
if (menuElements.length > 0) {
console.log(' ✅ 菜单组件已正确渲染');
// 检查第一个菜单项
const firstMenuItem = menuElements[0];
console.log(` 第一个菜单项文本: ${firstMenuItem.textContent.trim()}`);
// 检查是否有点击事件监听器
const clickListeners = getEventListeners(firstMenuItem, 'click');
if (clickListeners && clickListeners.length > 0) {
console.log(' ✅ 菜单项有点击事件监听器');
} else {
console.log(' ⚠️ 菜单项可能没有点击事件监听器');
}
} else {
console.log(' ❌ 未找到菜单组件');
}
} catch (error) {
console.error(' ❌ 检查菜单组件失败:', error);
}
console.log('\n=== 测试完成 ===');
console.log('如果所有测试都通过✅,菜单导航应该正常工作。');
console.log('如果有任何⚠️或❌,可能需要进一步检查。');
}
// 辅助函数获取元素的事件监听器仅在Chrome DevTools中可用
function getEventListeners(element, eventType) {
// 这个函数在普通JavaScript环境中不可用仅在开发者工具中可用
// 我们提供一个模拟实现
if (typeof window.getEventListeners !== 'undefined') {
try {
return window.getEventListeners(element)[eventType] || [];
} catch (error) {
return [];
}
}
return []; // 在普通环境中返回空数组
}
// 延迟执行测试,确保页面完全加载
setTimeout(() => {
runTests();
}, 2000);
// 导出函数供其他地方使用
if (typeof module !== 'undefined') {
module.exports = { runTests };
}

View File

@ -1,43 +0,0 @@
/**
* 菜单功能测试脚本
*/
console.log('=== 菜单功能测试 ===');
// 检查必要的组件和函数
console.log('\n1. 检查必要组件:');
console.log('window.location:', window.location);
console.log('window.electronNavigate:', typeof window.electronNavigate);
// 检查React Router相关函数
console.log('\n2. 检查React Router:');
console.log('useNavigate hook:', typeof useNavigate);
console.log('useLocation hook:', typeof useLocation);
// 检查菜单项
console.log('\n3. 检查菜单项:');
const menuItems = [
{ key: '/cashier', label: '收银台' },
{ key: '/products', label: '商品管理' },
{ key: '/orders', label: '订单管理' },
{ key: '/reports', label: '报表统计' },
{ key: '/settings', label: '系统设置' }
];
console.log('菜单项配置:', menuItems);
// 模拟菜单点击测试
console.log('\n4. 模拟菜单点击测试:');
try {
menuItems.forEach(item => {
console.log(` 测试导航到: ${item.key} (${item.label})`);
// 这里只是模拟,不实际执行导航
console.log(` 可以通过以下方式手动测试:`);
console.log(` - window.location.hash = '${item.key}'`);
console.log(` - navigate('${item.key}') (如果在React组件中)`);
});
} catch (error) {
console.error(' 测试失败:', error);
}
console.log('\n=== 测试完成 ===');
console.log('请在浏览器控制台中运行此脚本以查看详细信息。');

View File

@ -1,24 +0,0 @@
@echo off
chcp 65001 > nul
echo === 迷你收银台导航测试 ===
echo.
echo 正在启动收银台应用...
echo 请在应用启动后执行以下测试:
echo 1. 点击左侧菜单项(收银台、商品管理等)
echo 2. 检查是否能正常切换页面
echo 3. 查看开发者工具控制台是否有错误信息
echo.
echo 按任意键启动应用...
pause > nul
echo 启动应用...
npm start
echo.
echo 测试完成。如果仍有问题,请检查以下内容:
echo 1. 开发者工具控制台是否有错误信息 (Ctrl+Shift+I 打开)
echo 2. 确保所有依赖包已正确安装 (npm install)
echo 3. 检查端口是否被占用

View File

@ -1,67 +0,0 @@
// 测试导航功能的脚本
const fs = require('fs');
const path = require('path');
console.log('=== 导航功能测试 ===');
// 检查关键文件是否存在
const filesToCheck = [
'./src/renderer/utils/electron-router.js',
'./src/renderer/components/Layout.js',
'./src/renderer/App.js'
];
console.log('\n1. 检查关键文件:');
filesToCheck.forEach(file => {
const fullPath = path.join(__dirname, file);
if (fs.existsSync(fullPath)) {
console.log(`${file} 存在`);
} else {
console.log(`${file} 不存在`);
}
});
// 检查App.js是否正确导入和初始化electron-router
console.log('\n2. 检查App.js中的electron-router初始化:');
try {
const appJsPath = path.join(__dirname, 'src', 'renderer', 'App.js');
const appJsContent = fs.readFileSync(appJsPath, 'utf8');
if (appJsContent.includes('import { initElectronRouter }')) {
console.log('✅ 正确导入了initElectronRouter');
} else {
console.log('❌ 未找到initElectronRouter导入');
}
if (appJsContent.includes('initElectronRouter()')) {
console.log('✅ 正确调用了initElectronRouter()');
} else {
console.log('❌ 未找到initElectronRouter()调用');
}
} catch (error) {
console.log('❌ 检查App.js时出错:', error.message);
}
// 检查Layout.js中的导航处理
console.log('\n3. 检查Layout.js中的导航处理:');
try {
const layoutJsPath = path.join(__dirname, 'src', 'renderer', 'components', 'Layout.js');
const layoutJsContent = fs.readFileSync(layoutJsPath, 'utf8');
if (layoutJsContent.includes('electronNavigate')) {
console.log('✅ Layout.js中使用了electronNavigate');
} else {
console.log('❌ Layout.js中未使用electronNavigate');
}
if (layoutJsContent.includes('handleMenuClick')) {
console.log('✅ 找到handleMenuClick函数');
} else {
console.log('❌ 未找到handleMenuClick函数');
}
} catch (error) {
console.log('❌ 检查Layout.js时出错:', error.message);
}
console.log('\n=== 测试完成 ===');
console.log('如果所有检查都显示✅,则导航功能应该正常工作。');

View File

@ -1,57 +0,0 @@
// 简易启动脚本 - 直接启动主进程避免依赖concurrently
const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');
console.log('迷你收银台系统 - 简易启动脚本');
console.log('==============================\n');
// 确保数据目录存在
const dataDir = path.join(__dirname, 'data');
if (!fs.existsSync(dataDir)) {
console.log('创建数据目录...');
fs.mkdirSync(dataDir, { recursive: true });
}
// 启动应用
console.log('正在启动收银台应用...');
// 添加更详细的调试信息
console.log('\n======== 启动信息汇总 ========');
console.log('- 启动时间:', new Date().toLocaleString());
console.log('- 应用状态: 正在加载...');
console.log('\n如需查看前端详细日志请检查Electron开发者工具控制台');
console.log('可以按Ctrl+Shift+I在应用窗口中打开开发者工具');
console.log('===============================\n');
// 直接运行electron
const electronProcess = spawn(
path.join(__dirname, 'node_modules', '.bin', 'electron.cmd'),
['.'],
{
cwd: __dirname,
stdio: 'inherit',
shell: true,
env: { ...process.env, DEBUG: 'electron:*' }
}
);
// 定期检查应用状态
const statusInterval = setInterval(() => {
console.log(`[${new Date().toLocaleTimeString()}] 应用正常运行中...`);
}, 30000); // 每30秒输出一次状态
electronProcess.on('close', (code) => {
console.log(`\n应用进程退出,代码: ${code}`);
if (code !== 0) {
console.log('\n应用启动失败请尝试以下解决方案:');
console.log('1. 删除node_modules文件夹并重新安装依赖');
console.log('2. 以管理员身份运行启动脚本');
console.log('3. 检查Node.js版本是否满足要求(14+)');
}
});
electronProcess.on('error', (error) => {
console.error('\n启动失败:', error.message);
console.log('\n请检查electron是否正确安装尝试运行: npm install electron');
});

View File

@ -1,123 +0,0 @@
/**
* 菜单功能验证脚本
* 用于验证菜单功能是否正常工作
*/
const fs = require('fs');
const path = require('path');
console.log('=== 菜单功能验证 ===\n');
// 验证Layout.js文件
function verifyLayoutJS() {
console.log('1. 验证 Layout.js 文件...');
try {
const layoutPath = path.join(__dirname, 'src', 'renderer', 'components', 'Layout.js');
const content = fs.readFileSync(layoutPath, 'utf8');
// 检查关键功能点
const hasConsoleLog = content.includes("console.log('菜单点击:', key)");
const hasElectronNavigate = content.includes('electronNavigate(key, navigate)');
const hasWindowLocationHash = content.includes('window.location.hash = key');
const hasWindowReload = content.includes('window.location.reload()');
console.log(` 日志输出: ${hasConsoleLog ? '✅' : '❌'}`);
console.log(` Electron导航: ${hasElectronNavigate ? '✅' : '❌'}`);
console.log(` Hash导航: ${hasWindowLocationHash ? '✅' : '❌'}`);
console.log(` 页面刷新: ${hasWindowReload ? '✅' : '❌'}`);
if (hasConsoleLog && hasElectronNavigate && hasWindowLocationHash && hasWindowReload) {
console.log(' 结论: Layout.js 菜单处理函数已正确修复\n');
return true;
} else {
console.log(' 结论: Layout.js 菜单处理函数存在问题\n');
return false;
}
} catch (error) {
console.error(' 错误:', error.message);
console.log(' 结论: 无法验证 Layout.js 文件\n');
return false;
}
}
// 验证启动脚本
function verifyStartupScript() {
console.log('2. 验证 启动脚本...');
try {
const batPath = path.join(__dirname, '启动收银台.bat');
const content = fs.readFileSync(batPath, 'utf8');
// 检查关键环境变量
const hasLogging = content.includes('ELECTRON_ENABLE_LOGGING=1');
const hasMenuBar = content.includes('ELECTRON_FORCE_WINDOW_MENU_BAR=1');
const hasNodeEnv = content.includes('NODE_ENV=production');
const hasAutoBuild = content.includes('if not exist "build\\index.html"');
console.log(` 日志设置: ${hasLogging ? '✅' : '❌'}`);
console.log(` 菜单栏设置: ${hasMenuBar ? '✅' : '❌'}`);
console.log(` 环境变量: ${hasNodeEnv ? '✅' : '❌'}`);
console.log(` 自动构建: ${hasAutoBuild ? '✅' : '❌'}`);
if (hasLogging && hasMenuBar && hasNodeEnv && hasAutoBuild) {
console.log(' 结论: 启动脚本已正确增强\n');
return true;
} else {
console.log(' 结论: 启动脚本存在问题\n');
return false;
}
} catch (error) {
console.error(' 错误:', error.message);
console.log(' 结论: 无法验证启动脚本\n');
return false;
}
}
// 验证构建文件
function verifyBuildFiles() {
console.log('3. 验证 构建文件...');
const indexPath = path.join(__dirname, 'build', 'index.html');
const mainJsPath = path.join(__dirname, 'build', 'js', 'main.js');
const hasIndexHtml = fs.existsSync(indexPath);
const hasMainJs = fs.existsSync(mainJsPath);
console.log(` index.html: ${hasIndexHtml ? '✅' : '❌'}`);
console.log(` main.js: ${hasMainJs ? '✅' : '❌'}`);
if (hasIndexHtml && hasMainJs) {
console.log(' 结论: 构建文件存在\n');
return true;
} else {
console.log(' 结论: 构建文件缺失\n');
return false;
}
}
// 主函数
function main() {
console.log('开始验证菜单功能修复...\n');
const layoutOk = verifyLayoutJS();
const startupOk = verifyStartupScript();
const buildOk = verifyBuildFiles();
console.log('=== 验证结果 ===');
if (layoutOk && startupOk && buildOk) {
console.log('🎉 所有验证通过!菜单功能应该可以正常使用。');
console.log('\n现在您可以:');
console.log('1. 双击 "启动收银台.bat" 启动应用');
console.log('2. 点击左侧菜单项测试功能');
console.log('3. 如仍有问题,请查看 "最终使用说明.md" 获取更多帮助');
} else {
console.log('❌ 部分验证失败,请检查上述标记为❌的项目。');
console.log('\n建议操作:');
console.log('1. 运行 "npm run build" 重新构建项目');
console.log('2. 查看 "MENU_FIX_README.md" 获取技术细节');
console.log('3. 查看 "最终使用说明.md" 获取使用指导');
}
}
main();