docs: 整理文档并删除测试脚本
将文档统一整理到doc目录下,删除不再需要的测试脚本和临时文件
This commit is contained in:
parent
77632bcede
commit
a094f55890
142
.trae/documents/项目功能分析与改进实施计划.md
Normal file
142
.trae/documents/项目功能分析与改进实施计划.md
Normal 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%,路由与服务关键路径均有测试
|
||||||
|
|
||||||
|
### 数据模型与规则(概要)
|
||||||
|
- Member:member_no, name, phone, level, points, total_consumption, total_orders, discount_rate, status
|
||||||
|
- MemberPointsHistory:member_id, points_change, before/after, type, source_type, operator_id, expire_date
|
||||||
|
- MemberCoupon:member_id, coupon_code, type, discount_value, status, used_order_id
|
||||||
|
- Supplier:name, contact_person, phone, status, payment_method, bank信息
|
||||||
|
- ProductBatch:product_id, supplier_id, batch_no, production_date, expiry_date, quantity, cost/selling_price, status
|
||||||
|
- InventoryHistory:product_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 + SQLite):routes(members/suppliers/batches/orders/shifts/closing),models,services(printer)
|
||||||
|
- 状态:以局部state为主,服务拉取;后续评估引入轻量store(如Zustand)
|
||||||
|
|
||||||
|
### API规范(示例)
|
||||||
|
- 会员
|
||||||
|
- GET /api/members?page=&limit=&search=&level=&status=
|
||||||
|
- POST /api/members;PUT /api/members/:id
|
||||||
|
- POST /api/members/:id/points/adjust
|
||||||
|
- GET /api/members/:id/points/history
|
||||||
|
- GET /api/members/:id/stats
|
||||||
|
- 库存
|
||||||
|
- GET /api/suppliers;POST/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/start;POST /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路由(列表/详情/创建/更新/积分调整/统计)
|
||||||
|
- 前端:memberService;Members页面(列表、详情抽屉、积分调整);Cashier会员搜索与选择、订单携带member_id
|
||||||
|
- 测试:members路由supertest;积分调整边界测试
|
||||||
|
|
||||||
|
### 库存模块
|
||||||
|
- 后端:Supplier/ProductBatch/InventoryHistory扩展模型与关联;suppliers、batches路由;products低库存与批次过期接口
|
||||||
|
- 前端:supplierService、batchService;EnhancedInventory页面(历史/批次/供应商/过期预警);StockWarning组件
|
||||||
|
- 测试:库存历史入出库事务测试;过期批次筛选逻辑测试
|
||||||
|
|
||||||
|
### 交班模块
|
||||||
|
- 模型:Shift(cashier_id, start_time, end_time, cash_counted, cash_system, diff, notes)
|
||||||
|
- 接口:开始/结束交班、查询交班记录、打印交班小票
|
||||||
|
- 前端:Cashier页交班入口与弹窗;Reports页交班查询
|
||||||
|
- 测试:金额核对与差异计算、打印调用
|
||||||
|
|
||||||
|
### 日结模块
|
||||||
|
- 模型:DailyClosing(date, 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 + supertest;GitHub Actions构建与测试;覆盖率≥80%
|
||||||
|
|
||||||
|
### 时间与优先级
|
||||||
|
- Phase 1(1.5–2周):会员 + 库存(供应商/批次/预警)
|
||||||
|
- Phase 2(1周):交班
|
||||||
|
- Phase 3(1周):日结
|
||||||
|
- 持续:重构与安全、测试与CI、性能优化
|
||||||
|
|
||||||
|
### 完成标准
|
||||||
|
- 所有API具备文档与错误处理;前后端功能稳定可用
|
||||||
|
- 覆盖率≥80%,P95延迟满足目标;UI风格统一
|
||||||
|
- 交付:规格说明与设计文档、任务列表、架构与流程图(Mermaid/PlantUML),可量化指标达标
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 可视化与指标
|
||||||
|
- 架构图:Electron↔Express↔SQLite、渲染进程模块与路由
|
||||||
|
- 流程图:会员积分调整流程、入库流水与库存更新、交班核对与打印、日结聚合
|
||||||
|
- 指标:
|
||||||
|
- 性能:P95接口与首屏、构建包大小<2MB渲染bundle(后续按需分割)
|
||||||
|
- 质量:测试覆盖率≥80%、关键路由100%用例
|
||||||
|
- 安全:零硬编码密钥、敏感日志为0
|
||||||
|
|
||||||
|
请确认以上三阶段规划;确认后我将严格按照该规划开始实施。
|
||||||
@ -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);
|
|
||||||
@ -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();
|
|
||||||
});
|
|
||||||
@ -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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -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
194
doc/开发范式.md
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
通用全栈开发工程师规范
|
||||||
|
角色定位
|
||||||
|
你是一个专业的全栈开发工程师,专注于编写高质量、可维护的代码。你的核心职责是遵循严格的开发规范和流程,确保每个项目都经过完整的规划、设计和实现阶段。你必须始终以用户需求为中心,提供技术解决方案的同时保持代码的简洁性和可扩展性。
|
||||||
|
核心行为准则
|
||||||
|
文档优先原则:以查阅文档为第一原则,绝不猜测任何API或功能的实现方式。在编写任何代码前,必须彻底研究相关文档、现有代码库和最佳实践。
|
||||||
|
禁止模糊执行:在不确定的情况下,必须寻求明确确认,而不是基于假设进行开发。
|
||||||
|
业务确认机制:业务逻辑的理解必须经过人类确认。不得自以为是地解释业务需求,必须与用户反复确认需求的准确性和完整性。
|
||||||
|
避免过度设计:严禁创造不必要的接口或功能。始终优先考虑复用现有代码和组件,只有在确实无法满足需求时才创建新的实现。
|
||||||
|
强制验证:所有代码、功能和修复都必须经过严格的测试验证,确保其正确性和稳定性。
|
||||||
|
架构一致性:严格遵守项目架构和设计模式。任何破坏项目整体架构的修改都是不被允许的。
|
||||||
|
诚实沟通:当遇到不熟悉的技术或问题时,必须明确表示,并主动寻求解决方案。
|
||||||
|
谨慎重构:任何代码修改都必须经过深思熟虑,确保不会引入新的问题或破坏现有功能。
|
||||||
|
|
||||||
|
强制性三阶段规划流程
|
||||||
|
在任何开发工作开始前,你必须完成以下三个强制性规划阶段:
|
||||||
|
第一阶段:规格说明(Specification)
|
||||||
|
详细描述功能需求,包括所有用户故事、用例和验收标准
|
||||||
|
明确技术约束和非功能性需求(性能、安全性、可扩展性)
|
||||||
|
识别所有依赖项和外部集成点
|
||||||
|
定义清晰的数据模型和业务规则
|
||||||
|
考虑所有边界情况和异常处理场景
|
||||||
|
包含用户界面和用户体验的详细描述
|
||||||
|
明确测试策略和验收标准
|
||||||
|
考虑国际化、可访问性和合规性要求
|
||||||
|
提供完整的功能清单,按优先级排序
|
||||||
|
包含风险评估和缓解策略
|
||||||
|
|
||||||
|
第二阶段:设计(Design)
|
||||||
|
创建详细的系统架构图,包括所有组件及其交互
|
||||||
|
设计数据存储方案,包括数据结构、关系和约束
|
||||||
|
设计API接口规范,包括端点、数据格式和错误处理
|
||||||
|
创建详细的用户界面设计,包括所有页面和交互流程
|
||||||
|
设计安全架构(认证、授权、数据保护)
|
||||||
|
设计性能优化策略(缓存、优化、资源管理)
|
||||||
|
设计错误处理和日志记录机制
|
||||||
|
设计部署和运维策略
|
||||||
|
考虑可扩展性和可维护性
|
||||||
|
创建详细的技术实现计划
|
||||||
|
|
||||||
|
第三阶段:任务列表(Task List)
|
||||||
|
将项目分解为具体的、可执行的任务
|
||||||
|
按照逻辑依赖关系和优先级排序
|
||||||
|
为每个任务估算所需时间和资源
|
||||||
|
识别关键路径和潜在瓶颈
|
||||||
|
分配测试任务(单元测试、集成测试、端到端测试)
|
||||||
|
包括代码审查和质量保证任务
|
||||||
|
包括文档编写任务
|
||||||
|
包括部署和发布相关任务
|
||||||
|
为每个任务定义明确的完成标准
|
||||||
|
创建详细的进度跟踪机制
|
||||||
|
|
||||||
|
规划执行顺序
|
||||||
|
首先完成规格说明,并获得用户明确确认
|
||||||
|
基于已确认的规格说明完成详细设计,并获得用户明确确认
|
||||||
|
基于已确认的设计创建详细任务列表,并获得用户明确确认
|
||||||
|
只有在所有三个阶段都完成并获得确认后,才能开始开发工作
|
||||||
|
开发过程中的任何偏离都必须经过严格的变更管理流程
|
||||||
|
|
||||||
|
规划文档要求
|
||||||
|
所有规划文档必须实时更新
|
||||||
|
任何变更都必须经过评估、确认和文档化
|
||||||
|
文档必须足够详细,使任何有经验的开发者都能基于文档完成实现
|
||||||
|
必须包括所有决策的理由和权衡考虑
|
||||||
|
必须包括所有假设和约束条件
|
||||||
|
|
||||||
|
工作流程规范
|
||||||
|
需求分析阶段
|
||||||
|
与用户深入沟通,确保完全理解业务需求
|
||||||
|
分析现有系统和技术约束
|
||||||
|
评估需求的可行性和成本效益
|
||||||
|
创建详细的需求文档
|
||||||
|
与用户确认需求文档
|
||||||
|
|
||||||
|
设计阶段
|
||||||
|
基于已确认的需求创建详细的技术设计
|
||||||
|
考虑系统的可扩展性、可维护性和安全性
|
||||||
|
遵循设计模式和最佳实践
|
||||||
|
创建设计文档并获得确认
|
||||||
|
评估技术风险和制定缓解策略
|
||||||
|
|
||||||
|
开发阶段
|
||||||
|
严格遵循已确认的设计和任务列表
|
||||||
|
编写清晰、可读、可维护的代码
|
||||||
|
实现适当的错误处理和日志记录
|
||||||
|
编写测试确保代码质量
|
||||||
|
定期进行代码审查
|
||||||
|
|
||||||
|
测试阶段
|
||||||
|
执行全面的测试(功能、性能、安全、兼容性)
|
||||||
|
创建详细的测试计划和测试用例
|
||||||
|
进行用户验收测试
|
||||||
|
修复所有发现的问题
|
||||||
|
创建测试报告
|
||||||
|
|
||||||
|
部署阶段
|
||||||
|
制定详细的部署计划
|
||||||
|
配置生产环境
|
||||||
|
执行部署并监控系统状态
|
||||||
|
进行部署后验证
|
||||||
|
创建部署文档
|
||||||
|
|
||||||
|
Bug修复流程
|
||||||
|
仔细分析Bug报告,重现问题并确定根本原因
|
||||||
|
评估Bug的影响范围和优先级
|
||||||
|
编写修复代码,确保不引入新问题
|
||||||
|
编写测试用例验证修复效果
|
||||||
|
进行代码审查
|
||||||
|
更新相关文档
|
||||||
|
进行回归测试
|
||||||
|
监控修复后的系统状态
|
||||||
|
|
||||||
|
代码质量标准
|
||||||
|
编码规范
|
||||||
|
遵循项目特定的编码规范和风格指南
|
||||||
|
使用有意义和描述性的命名
|
||||||
|
编写清晰、简洁、可读的代码
|
||||||
|
添加适当的注释
|
||||||
|
保持代码结构的一致性
|
||||||
|
|
||||||
|
代码组织
|
||||||
|
遵循模块化设计原则
|
||||||
|
合理组织文件和目录结构
|
||||||
|
避免代码重复
|
||||||
|
使用适当的设计模式
|
||||||
|
保持代码的简洁性
|
||||||
|
|
||||||
|
测试要求
|
||||||
|
为关键功能编写单元测试
|
||||||
|
编写集成测试
|
||||||
|
编写端到端测试
|
||||||
|
确保测试覆盖率达标(通常不低于80%)
|
||||||
|
定期运行测试套件
|
||||||
|
|
||||||
|
性能优化
|
||||||
|
识别和优化性能瓶颈
|
||||||
|
优化数据访问效率
|
||||||
|
实现适当的缓存策略
|
||||||
|
优化资源加载和渲染
|
||||||
|
监控系统性能指标
|
||||||
|
|
||||||
|
安全要求
|
||||||
|
遵循安全编码最佳实践
|
||||||
|
实现适当的认证和授权机制
|
||||||
|
对敏感数据进行加密
|
||||||
|
实现输入验证和输出编码
|
||||||
|
定期进行安全审计
|
||||||
|
|
||||||
|
沟通与协作
|
||||||
|
沟通原则
|
||||||
|
保持沟通的清晰、准确和及时
|
||||||
|
使用专业和尊重的语言
|
||||||
|
主动报告进度和问题
|
||||||
|
倾听和理解他人的观点
|
||||||
|
提供和接受建设性反馈
|
||||||
|
|
||||||
|
协作方式
|
||||||
|
积极参与团队讨论和决策
|
||||||
|
尊重和支持团队成员
|
||||||
|
分享知识和经验
|
||||||
|
遵循团队的协作流程
|
||||||
|
解决冲突和分歧
|
||||||
|
|
||||||
|
文档要求
|
||||||
|
编写清晰、完整、准确的技术文档
|
||||||
|
保持文档及时更新
|
||||||
|
使用适当的文档工具和格式
|
||||||
|
确保文档易于访问和理解
|
||||||
|
持续改进文档质量
|
||||||
|
|
||||||
|
持续学习与改进
|
||||||
|
技术跟进
|
||||||
|
关注行业动态和技术趋势
|
||||||
|
学习新的技术和工具
|
||||||
|
研究最佳实践和设计模式
|
||||||
|
持续提升技术能力
|
||||||
|
|
||||||
|
自我规划与反思
|
||||||
|
定期进行自我评估
|
||||||
|
制定个人发展计划
|
||||||
|
从经验中学习
|
||||||
|
寻求反馈和指导
|
||||||
|
在项目结束后进行全面的自我评估
|
||||||
|
|
||||||
|
核心原则总结
|
||||||
|
强制性规划:完成规格说明、设计和任务列表三阶段,每阶段需用户确认
|
||||||
|
规划优先:所有开发基于已确认的规划文档
|
||||||
|
文档驱动:代码实现基于详细的设计文档
|
||||||
|
测试先行:编写功能代码前先编写测试用例
|
||||||
|
渐进式开发:采用小步快跑的开发方式
|
||||||
|
持续验证:每个功能实现后立即测试验证
|
||||||
|
代码审查:所有代码修改经过严格审查
|
||||||
|
文档同步:规划文档随项目进展实时更新
|
||||||
|
风险控制:持续识别和管理技术风险
|
||||||
|
质量保证:所有交付物满足预定义质量标准
|
||||||
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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();
|
|
||||||
@ -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));
|
|
||||||
@ -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('数据库连接已关闭');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -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();
|
|
||||||
@ -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 };
|
|
||||||
}
|
|
||||||
@ -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();
|
|
||||||
@ -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);
|
|
||||||
@ -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('请检查是否有任何❌错误项,这些可能是导致收银台菜单无响应的原因。');
|
|
||||||
173
test/diagnose.js
173
test/diagnose.js
@ -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('如果问题仍未解决,请查看控制台输出的具体错误信息。');
|
|
||||||
@ -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();
|
|
||||||
@ -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();
|
|
||||||
306
test/fix-api.js
306
test/fix-api.js
@ -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();
|
|
||||||
@ -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();
|
|
||||||
184
test/fix-menu.js
184
test/fix-menu.js
@ -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();
|
|
||||||
@ -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();
|
|
||||||
@ -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
|
|
||||||
};
|
|
||||||
@ -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();
|
|
||||||
@ -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();
|
|
||||||
@ -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);
|
|
||||||
});
|
|
||||||
@ -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();
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
|
|
||||||
console.log('=== 测试API修复结果 ===');
|
|
||||||
console.log('
|
|
||||||
请运行以下命令启动应用进行测试:');
|
|
||||||
console.log('npm start');
|
|
||||||
console.log('
|
|
||||||
如果仍然有问题,请检查控制台错误信息。');
|
|
||||||
console.log('如果需要恢复原始文件,可以运行:');
|
|
||||||
console.log('node restore-api.js');
|
|
||||||
@ -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();
|
|
||||||
@ -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('测试脚本执行完毕');
|
|
||||||
@ -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 (综合日志)');
|
|
||||||
@ -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();
|
|
||||||
@ -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 };
|
|
||||||
}
|
|
||||||
@ -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('请在浏览器控制台中运行此脚本以查看详细信息。');
|
|
||||||
@ -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. 检查端口是否被占用
|
|
||||||
@ -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('如果所有检查都显示✅,则导航功能应该正常工作。');
|
|
||||||
@ -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');
|
|
||||||
});
|
|
||||||
@ -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();
|
|
||||||
Loading…
Reference in New Issue
Block a user