Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d29874c07 | ||
|
|
b0115059e5 | ||
|
|
275ad1a720 | ||
|
|
b5420880f7 | ||
|
|
fe60d656b1 | ||
|
|
6c33eeb3bf | ||
|
|
252c2b882d | ||
|
|
d10c51edca | ||
|
|
e3ab0502be | ||
|
|
23734541d1 | ||
|
|
5669bdc8a7 | ||
|
|
53cd8f60b5 | ||
|
|
7d7fbcaa0e | ||
|
|
61fd6cd78b | ||
|
|
0878b05033 | ||
|
|
d40083b096 | ||
|
|
2e5a31e904 | ||
|
|
69c8626888 | ||
|
|
5d2f0099ac | ||
|
|
23823d08f8 | ||
|
|
268a1cc16b | ||
|
|
95ba01e152 | ||
|
|
b61f2cc70b | ||
|
|
cbdc25545e | ||
|
|
0d216d3067 | ||
|
|
e75761bc39 | ||
|
|
382062239f | ||
|
|
1568d4c5ee | ||
|
|
5be57a052b | ||
|
|
bc2ca6919e | ||
|
|
3246e8981f | ||
|
|
d81765e7d2 | ||
|
|
fa36263c61 | ||
|
|
7dbf1c1efc | ||
|
|
fe1df2e3e2 | ||
|
|
f4b11085aa | ||
|
|
dc92fb6eab | ||
|
|
516d7cb99b | ||
|
|
dcc778ede6 | ||
|
|
67374c5513 | ||
|
|
668700353f | ||
|
|
e36d5475dd | ||
|
|
aa532ef4ec | ||
|
|
387fc11796 | ||
|
|
66344011f4 | ||
|
|
976080683f | ||
|
|
ef77566b1d | ||
|
|
b05dc327af | ||
|
|
ba40f97ab6 | ||
|
|
c40698c58b | ||
|
|
a626aafbf3 | ||
|
|
e6c7c3a46e | ||
|
|
083cc3f675 | ||
|
|
744bc4eb2f | ||
|
|
8b8df295f1 | ||
|
|
df842cd83c | ||
|
|
63233f1de6 | ||
|
|
470c58ace0 | ||
|
|
1af12e3cf2 | ||
|
|
63edb27353 | ||
|
|
9fa3ed66f7 | ||
|
|
83a3426219 |
195
CLAUDE.md
Normal file
195
CLAUDE.md
Normal file
@ -0,0 +1,195 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
**腾智微丝产线包装系统** is a PyQt6-based industrial automation control system for managing and monitoring the packing process of fine wire production lines. It integrates camera monitoring, weighing, barcode scanning, PLC communication, and multiple serial device support.
|
||||
|
||||
**Key Operating Modes:**
|
||||
- **standalone**: Complete independent system
|
||||
- **api**: Acts as an interface component for other systems
|
||||
|
||||
## Building and Running
|
||||
|
||||
### Setup Development Environment
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Run the application
|
||||
python main.py
|
||||
```
|
||||
|
||||
### Key Configuration
|
||||
- Main config file: `config/app_config.json`
|
||||
- Database: SQLite (default at `db/jtDB.db`), also supports PostgreSQL and MySQL
|
||||
- Logs: `logs/app_YYYY-MM-DD.log` (auto-rotating daily, kept for 30 days)
|
||||
|
||||
### Database Initialization
|
||||
If the database doesn't exist, it's automatically created on first run. The database schema is initialized via `utils/init_db.py`.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
This project follows an **MVC architecture** with clear separation of concerns:
|
||||
|
||||
### Layer Structure
|
||||
|
||||
1. **Model (DAO Layer)** - `dao/` directory
|
||||
- Data Access Objects for database operations
|
||||
- Supports SQLite, PostgreSQL, MySQL via `utils/sql_utils.py`
|
||||
- Key DAOs: `inspection_dao.py`, `user_dao.py`, `electricity_dao.py`, `pallet_type_dao.py`
|
||||
|
||||
2. **View Layer** - `ui/` and `widgets/` directories
|
||||
- **UI Definitions** (`ui/*.py`): Generated or hand-written UI class definitions
|
||||
- **Controllers** (`widgets/*.py`): Business logic and signal/slot handling for UI components
|
||||
- Built with **PySide6** (Qt for Python)
|
||||
- Key components:
|
||||
- `widgets/main_window.py`: Core application window
|
||||
- `widgets/login_widget.py`: User authentication
|
||||
- `widgets/camera_widget.py`: Camera management
|
||||
- `widgets/inspection_settings_widget.py`: Inspection configuration
|
||||
|
||||
3. **Utilities Layer** - `utils/` directory
|
||||
- **Configuration**: `config_loader.py` (singleton pattern, loads from JSON)
|
||||
- **Serial Communication**: `serial_manager.py` (manages multiple serial ports)
|
||||
- **Modbus Protocol**: `modbus_utils.py`, `modbus_monitor.py` (PLC communication)
|
||||
- **Hardware**:
|
||||
- `electricity_monitor.py` (power consumption tracking, singleton)
|
||||
- `keyboard_listener.py` (hardware input)
|
||||
- **Image Processing**: `local_image_player.py` (offline camera simulation)
|
||||
- **Database**: `sql_utils.py` (connection pooling, multi-database support)
|
||||
|
||||
4. **Hardware Integration** - `camera/` directory
|
||||
- Hikvision camera SDK integration
|
||||
- Supports both real camera devices and local image sequence playback
|
||||
|
||||
5. **API Layer** - `apis/` directory
|
||||
- RESTful API endpoints for api mode: `tary_api.py`, `gc_api.py`
|
||||
|
||||
## Key Design Patterns and Conventions
|
||||
|
||||
### Singleton Pattern
|
||||
Used for global resources and services:
|
||||
- `ConfigLoader`: Configuration management
|
||||
- `ElectricityMonitor`: Power monitoring
|
||||
- `SerialManager`: Serial port management (when instantiated)
|
||||
|
||||
**Access pattern:**
|
||||
```python
|
||||
from utils.config_loader import ConfigLoader
|
||||
config = ConfigLoader.get_instance()
|
||||
```
|
||||
|
||||
### Qt Signal/Slot Communication
|
||||
- Preferred method for inter-component communication
|
||||
- Asynchronous event handling
|
||||
- Avoid direct method calls between widgets
|
||||
|
||||
### Configuration Management
|
||||
- JSON-based configuration (`config/app_config.json`)
|
||||
- Accessed via `ConfigLoader.get_value(key_path, default_value)`
|
||||
- Supports nested keys with dot notation: `'database.sources.sqlite.path'`
|
||||
|
||||
### Database Access
|
||||
- Use `SQLUtils` for connection management
|
||||
- All DAOs inherit pattern from existing implementations
|
||||
- Connection pooling is automatically managed
|
||||
- Always call `SQLUtils.close_all_connections()` on application exit
|
||||
|
||||
### Error Handling
|
||||
- Global exception handler in `main.py` catches unhandled exceptions
|
||||
- Log all errors with context (function name, line number)
|
||||
- Use try/except in main threads, avoid silent failures
|
||||
|
||||
## Important Implementation Details
|
||||
|
||||
### Serial Port Configuration
|
||||
- Defined in `config/app_config.json` under `serial.*`
|
||||
- Each device (cz=weighing, xj=scanner, mdz=moisture sensor) has own config
|
||||
- `serial_manager.py` handles connections and data parsing
|
||||
- Note: Weighing device (cz) now retrieves data via Modbus register D11, not serial
|
||||
|
||||
### Modbus Communication
|
||||
- TCP protocol to PLC at `modbus.host:modbus.port` (default: localhost:5020)
|
||||
- `modbus_monitor.py` provides continuous monitoring of specific registers
|
||||
- Test/development: Use `modbus_server.py` to simulate PLC
|
||||
|
||||
### Application Mode Detection
|
||||
- Check `app.mode` from config to determine standalone vs api mode
|
||||
- API mode disables certain UI elements and uses REST endpoints instead
|
||||
|
||||
### Threading Considerations
|
||||
- Use `QThread` for long-running operations (serial reading, Modbus polling)
|
||||
- Always use signals to communicate between threads
|
||||
- Avoid blocking the GUI thread
|
||||
|
||||
## File Organization
|
||||
|
||||
```
|
||||
.
|
||||
├── main.py # Application entry point
|
||||
├── config/app_config.json # Main configuration file
|
||||
├── requirements.txt # Python dependencies
|
||||
├── widgets/ # View controllers (UI logic)
|
||||
├── ui/ # View definitions (UI layouts)
|
||||
├── dao/ # Data access objects
|
||||
├── utils/ # Utility modules
|
||||
│ ├── config_loader.py # Configuration singleton
|
||||
│ ├── serial_manager.py # Serial port handling
|
||||
│ ├── modbus_utils.py # Modbus communication
|
||||
│ ├── sql_utils.py # Database utilities
|
||||
│ └── ... (other utilities)
|
||||
├── camera/ # Camera SDK integration
|
||||
├── apis/ # API endpoints (api mode)
|
||||
├── db/ # Database files
|
||||
├── logs/ # Application logs
|
||||
└── inspection/ # Inspection business logic
|
||||
```
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### Adding a New Widget/Dialog
|
||||
1. Create UI definition in `ui/` with suffix `_ui.py`
|
||||
2. Create controller in `widgets/` with corresponding name
|
||||
3. Use signals for communication with parent widgets
|
||||
4. Register in config if it needs configuration options
|
||||
|
||||
### Modifying Serial Device Handling
|
||||
1. Edit device config in `config/app_config.json`
|
||||
2. Update parser logic in `serial_manager.py` if data format changes
|
||||
3. Test with physical device or mock serial data
|
||||
|
||||
### Adding Database Tables
|
||||
1. Modify schema in `utils/init_db.py`
|
||||
2. Create new DAO class in `dao/` following existing patterns
|
||||
3. Add migration if needed for existing databases
|
||||
|
||||
### Debugging Modbus Communication
|
||||
```bash
|
||||
# Start Modbus test server
|
||||
python modbus_server.py
|
||||
|
||||
# Check register values and communication
|
||||
# Monitor output from modbus_monitor.py in logs
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
See `requirements.txt` for complete list. Key dependencies:
|
||||
- **PySide6**: GUI framework
|
||||
- **pymodbus**: Modbus TCP communication
|
||||
- **pyserial**: Serial port communication
|
||||
- **pandas**: Data processing
|
||||
- **opencv-python**: Image processing (for local image playback)
|
||||
- **pillow**: Image handling
|
||||
- **pynput**: Keyboard monitoring
|
||||
|
||||
## Notes for Future Development
|
||||
|
||||
1. **Database Migrations**: When modifying schemas, provide backwards compatibility or migration scripts
|
||||
2. **Configuration Changes**: Maintain JSON schema compatibility with existing configs
|
||||
3. **Testing**: Tests directory exists at `tests/` but currently minimal
|
||||
4. **Logging**: Application logs are verbose by design - check logs when debugging issues
|
||||
5. **Resource Cleanup**: Always stop services (ElectricityMonitor, SerialManager) on application exit
|
||||
374
apis/gc_api.py
374
apis/gc_api.py
@ -1,12 +1,21 @@
|
||||
from utils.api_utils import ApiUtils
|
||||
import logging
|
||||
import requests
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
from utils.config_loader import ConfigLoader
|
||||
from utils.api_utils import ApiUtils
|
||||
|
||||
class GcApi:
|
||||
"""工程API接口类"""
|
||||
|
||||
def __init__(self):
|
||||
"""初始化API接口"""
|
||||
self.api_utils = ApiUtils()
|
||||
|
||||
# 添加缓存相关属性
|
||||
self._tray_stats_cache = {} # 缓存数据
|
||||
self._tray_stats_cache_time = {} # 缓存时间
|
||||
self._cache_ttl = 5 # 缓存有效期(秒)
|
||||
|
||||
|
||||
def ismt_option(self, params):
|
||||
@ -100,6 +109,28 @@ class GcApi:
|
||||
except Exception as e:
|
||||
logging.error(f"获取订单信息失败: {str(e)}")
|
||||
return None
|
||||
def get_spakc_info(self, order_code,corp_id,tray_id):
|
||||
"""
|
||||
获取订单信息
|
||||
"""
|
||||
try:
|
||||
# API 配置中的键名
|
||||
api_key = "get_spack_info"
|
||||
# 构建 form-data 格式的数据
|
||||
order_dict = {"orderId":order_code,"data_corp":corp_id,"trayId":tray_id}
|
||||
data = {
|
||||
"parms": json.dumps(order_dict), # 必须将数据序列化为JSON字符串
|
||||
"pageIndex": 0,
|
||||
"pageSize": 10,
|
||||
"sortField": "",
|
||||
"sortOrder": ""
|
||||
}
|
||||
# 将工程号作为参数传递,使用 data 参数传递 form-data 格式数据
|
||||
response = self.api_utils.post(api_key, data=data)
|
||||
return response
|
||||
except Exception as e:
|
||||
logging.error(f"获取订单信息失败: {str(e)}")
|
||||
return None
|
||||
def add_order_info(self, info):
|
||||
"""
|
||||
添加订单信息
|
||||
@ -122,6 +153,34 @@ class GcApi:
|
||||
logging.error(f"添加订单信息失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def remove_order_info(self, xpack, ddmo, sgc):
|
||||
"""
|
||||
删除订单信息
|
||||
|
||||
Args:
|
||||
xpack: 托盘号
|
||||
ddmo: 订单号
|
||||
sgc: 工程号
|
||||
|
||||
Returns:
|
||||
dict: 接口响应结果
|
||||
"""
|
||||
try:
|
||||
# API 配置中的键名
|
||||
api_key = "remove_order_info"
|
||||
# 构建 form-data 格式的数据
|
||||
data = {
|
||||
"xpack": xpack,
|
||||
"ddmo": ddmo,
|
||||
"gch": sgc
|
||||
}
|
||||
# 调用接口
|
||||
response = self.api_utils.post(api_key, data=data)
|
||||
return response
|
||||
except Exception as e:
|
||||
logging.error(f"删除订单信息失败: {str(e)}")
|
||||
return {"status": False, "message": f"删除订单信息失败: {str(e)}"}
|
||||
|
||||
def get_xpack(self, order_id,corp_id):
|
||||
"""
|
||||
获取包装号
|
||||
@ -139,4 +198,313 @@ class GcApi:
|
||||
return response
|
||||
except Exception as e:
|
||||
logging.error(f"获取包装号失败: {str(e)}")
|
||||
return None
|
||||
return None
|
||||
|
||||
def get_order_list(self, params):
|
||||
"""
|
||||
获取订单列表
|
||||
|
||||
Args:
|
||||
params: 查询参数字典
|
||||
|
||||
Returns:
|
||||
dict: 订单列表
|
||||
"""
|
||||
try:
|
||||
# API 配置中的键名
|
||||
api_key = "get_order_info"
|
||||
|
||||
# 构建查询参数
|
||||
order_dict = {
|
||||
"srch_mo": params.get("srch_mo", ""),
|
||||
"srch_note": params.get("srch_note", ""),
|
||||
"srch_rq1": params.get("srch_rq1", ""),
|
||||
"srch_rq2": params.get("srch_rq2", ""),
|
||||
"srch_cz": params.get("srch_cz", ""),
|
||||
"srch_size": params.get("srch_size", ""),
|
||||
"data_corp": params.get("corp_id", "")
|
||||
}
|
||||
|
||||
# 构建 form-data 格式的数据
|
||||
data = {
|
||||
"parms": json.dumps(order_dict), # 必须将数据序列化为JSON字符串
|
||||
"pageIndex": 0,
|
||||
"pageSize": 50, # 获取更多数据
|
||||
"sortField": "create_time",
|
||||
"sortOrder": "desc"
|
||||
}
|
||||
|
||||
# 调用API
|
||||
response = self.api_utils.post(api_key, data=data)
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"获取订单列表失败: {str(e)}")
|
||||
return {"status": False, "message": f"获取订单列表失败: {str(e)}"}
|
||||
|
||||
def get_params(self, stype, main, corp_id):
|
||||
"""
|
||||
获取指定参数信息
|
||||
|
||||
Args:
|
||||
stype: 参数类型,如"库房档案"
|
||||
main: 主参数,如"XC"
|
||||
corp_id: 公司ID
|
||||
|
||||
Returns:
|
||||
dict: 参数信息列表
|
||||
"""
|
||||
try:
|
||||
# API 配置中的键名
|
||||
api_key = "get_params"
|
||||
|
||||
# 构建GET请求参数
|
||||
params = {
|
||||
"stype": stype,
|
||||
"main": main,
|
||||
"corp_id": corp_id
|
||||
}
|
||||
|
||||
# 发送GET请求
|
||||
response = self.api_utils.get(api_key, params=params)
|
||||
|
||||
# 检查响应状态
|
||||
if response.get("success", False):
|
||||
return {
|
||||
"status": True,
|
||||
"data": response.get("data", []),
|
||||
"message": "获取参数信息成功"
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"status": False,
|
||||
"data": [],
|
||||
"message": response.get("message", "获取参数信息失败")
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"获取参数信息失败: {str(e)}")
|
||||
return {
|
||||
"status": False,
|
||||
"data": [],
|
||||
"message": f"获取参数信息失败: {str(e)}"
|
||||
}
|
||||
|
||||
def get_wire_type_params(self, corp_id):
|
||||
"""
|
||||
获取线材类型参数信息
|
||||
|
||||
Args:
|
||||
corp_id: 公司ID
|
||||
|
||||
Returns:
|
||||
dict: 线材类型信息列表
|
||||
"""
|
||||
try:
|
||||
# 使用get_params方法,传入线材类型参数
|
||||
return self.get_params("线材类型", "", corp_id)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"获取线材类型参数失败: {str(e)}")
|
||||
return {
|
||||
"status": False,
|
||||
"data": [],
|
||||
"message": f"获取线材类型参数失败: {str(e)}"
|
||||
}
|
||||
|
||||
def get_luno_list(self, params):
|
||||
"""
|
||||
获取炉号列表
|
||||
|
||||
Args:
|
||||
params: 查询参数字典,包含:
|
||||
- srch_rq1: 开始日期
|
||||
- srch_rq2: 结束日期
|
||||
- luono: 炉号
|
||||
- cz: 材质
|
||||
- gg: 规格
|
||||
- gc: 钢厂
|
||||
- corp_id: 公司ID
|
||||
|
||||
Returns:
|
||||
dict: 炉号列表
|
||||
"""
|
||||
try:
|
||||
# API 配置中的键名
|
||||
api_key = "get_luno"
|
||||
|
||||
# 构建查询参数
|
||||
luno_dict = {
|
||||
"srch_rq1": params.get("srch_rq1", ""),
|
||||
"srch_rq2": params.get("srch_rq2", ""),
|
||||
"luono": params.get("luono", ""),
|
||||
"cz": params.get("cz", ""),
|
||||
"gg": params.get("gg", ""),
|
||||
"gc": params.get("gc", ""),
|
||||
"data_corp": params.get("corp_id", "")
|
||||
}
|
||||
|
||||
# 构建 form-data 格式的数据
|
||||
data = {
|
||||
"parms": json.dumps(luno_dict), # 必须将数据序列化为JSON字符串
|
||||
"pageIndex": 0,
|
||||
"pageSize": 50, # 获取更多数据
|
||||
"sortField": "create_time",
|
||||
"sortOrder": "desc"
|
||||
}
|
||||
|
||||
# 调用API
|
||||
response = self.api_utils.post(api_key, data=data)
|
||||
return response
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"获取炉号列表失败: {str(e)}")
|
||||
return {"status": False, "message": f"获取炉号列表失败: {str(e)}"}
|
||||
|
||||
def get_order_info_by_xpack(self, xpack, corp_id):
|
||||
"""
|
||||
通过托盘号获取订单信息
|
||||
"""
|
||||
try:
|
||||
# API 配置中的键名
|
||||
api_key = "get_order_info_by_xpack"
|
||||
order_dict = {
|
||||
"srch_spack": xpack,
|
||||
"data_corp": corp_id
|
||||
}
|
||||
data = {
|
||||
"parms": json.dumps(order_dict),
|
||||
"pageIndex": 0,
|
||||
"pageSize": 10,
|
||||
"sortField": "",
|
||||
"sortOrder": ""
|
||||
}
|
||||
response = self.api_utils.post(api_key, data=data)
|
||||
return response
|
||||
except Exception as e:
|
||||
logging.error(f"通过托盘号获取订单信息失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def get_package_statistics(self, order_id, corp_id):
|
||||
"""
|
||||
获取订单包装统计数据
|
||||
"""
|
||||
try:
|
||||
# API 配置中的键名
|
||||
api_key = "get_package_statistics"
|
||||
# 构建URL参数
|
||||
params = {
|
||||
"orderId": order_id,
|
||||
"data_corp": corp_id
|
||||
}
|
||||
response = self.api_utils.get(api_key, params=params)
|
||||
return response
|
||||
except Exception as e:
|
||||
logging.error(f"获取订单包装统计数据失败: {str(e)}")
|
||||
return {"status": False, "message": str(e)}
|
||||
|
||||
def get_tray_package_statistics(self, order_id, tray_id, corp_id):
|
||||
"""获取托盘包装统计信息
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
tray_id: 托盘号
|
||||
corp_id: 公司ID
|
||||
|
||||
Returns:
|
||||
dict: 托盘包装统计信息
|
||||
"""
|
||||
# 生成缓存键
|
||||
cache_key = f"{order_id}_{tray_id}_{corp_id}"
|
||||
|
||||
# 检查缓存是否有效
|
||||
current_time = time.time()
|
||||
if (cache_key in self._tray_stats_cache and
|
||||
current_time - self._tray_stats_cache_time.get(cache_key, 0) < self._cache_ttl):
|
||||
logging.info(f"使用缓存数据: getBzNumByTrayWszb.do, 参数={order_id}, {tray_id}")
|
||||
return self._tray_stats_cache[cache_key]
|
||||
|
||||
# 原有API调用逻辑
|
||||
logging.info(f"调用API: getBzNumByTrayWszb.do, 参数={order_id}, {tray_id}")
|
||||
try:
|
||||
# API 配置中的键名
|
||||
api_key = "get_tray_package_statistics"
|
||||
# 构建URL参数
|
||||
params = {
|
||||
"orderId": order_id,
|
||||
"trayId": tray_id,
|
||||
"data_corp": corp_id
|
||||
}
|
||||
response = self.api_utils.get(api_key, params=params)
|
||||
|
||||
if response and response.get('status', False):
|
||||
# 更新缓存
|
||||
self._tray_stats_cache[cache_key] = response
|
||||
self._tray_stats_cache_time[cache_key] = current_time
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
logging.error(f"获取托盘包装统计数据失败: {str(e)}")
|
||||
return {"status": False, "message": str(e)}
|
||||
|
||||
def get_spack_info(self, order_id, tray_id, corp_id):
|
||||
"""
|
||||
获取spack信息
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
tray_id: 托盘号
|
||||
corp_id: 公司ID
|
||||
|
||||
Returns:
|
||||
dict: 包含spack信息的字典
|
||||
"""
|
||||
try:
|
||||
# API 配置中的键名
|
||||
api_key = "get_spack_info"
|
||||
# 构建URL参数
|
||||
params = {
|
||||
"orderId": order_id,
|
||||
"trayId": tray_id,
|
||||
"data_corp": corp_id
|
||||
}
|
||||
response = self.api_utils.get(api_key, params=params)
|
||||
return response
|
||||
except Exception as e:
|
||||
logging.error(f"获取spack信息失败: {str(e)}")
|
||||
return {"status": False, "message": str(e)}
|
||||
|
||||
def clear_tray_stats_cache(self, order_id=None, tray_id=None, corp_id=None):
|
||||
"""清除托盘统计数据缓存
|
||||
|
||||
Args:
|
||||
order_id: 订单号,如果为None则清除所有缓存
|
||||
tray_id: 托盘号,如果为None则清除该订单的所有缓存
|
||||
corp_id: 公司ID,如果为None则清除该订单和托盘的所有缓存
|
||||
"""
|
||||
if order_id is None:
|
||||
# 清除所有缓存
|
||||
self._tray_stats_cache = {}
|
||||
self._tray_stats_cache_time = {}
|
||||
logging.info("已清除所有托盘统计数据缓存")
|
||||
return
|
||||
|
||||
# 清除特定缓存
|
||||
keys_to_remove = []
|
||||
prefix = f"{order_id}_"
|
||||
if tray_id:
|
||||
prefix += f"{tray_id}_"
|
||||
if corp_id:
|
||||
prefix += f"{corp_id}"
|
||||
|
||||
for key in list(self._tray_stats_cache.keys()):
|
||||
if key.startswith(prefix):
|
||||
keys_to_remove.append(key)
|
||||
|
||||
for key in keys_to_remove:
|
||||
if key in self._tray_stats_cache:
|
||||
del self._tray_stats_cache[key]
|
||||
if key in self._tray_stats_cache_time:
|
||||
del self._tray_stats_cache_time[key]
|
||||
|
||||
logging.info(f"已清除托盘统计数据缓存: 前缀={prefix}, 清除数量={len(keys_to_remove)}")
|
||||
@ -7,7 +7,7 @@
|
||||
"enable_keyboard_listener": false,
|
||||
"enable_camera": false
|
||||
},
|
||||
"base_url": "http://localhost:8084",
|
||||
"base_url": "https://jsjtnew.tengzhicn.com",
|
||||
"mode": "api"
|
||||
},
|
||||
"apis": {
|
||||
@ -15,8 +15,15 @@
|
||||
"get_gc_info": "/jsjt/xcsc/tprk/getBZGCInfoToWsbz.do",
|
||||
"get_order_info": "/jsjt/xcsc/tprk/getXsddBzrkGridListToWsbz.do",
|
||||
"add_order_info": "/jsjt/xcsc/tprk/bzrkAdd01.do",
|
||||
"remove_order_info": "/jsjt/xcsc/tprk/removeByOrder.do",
|
||||
"get_xpack": "/jsjt/xcsc/tprk/getXpackToWsbz.do",
|
||||
"ismt_option": "/jsjt/xcsc/tprk/ismtOptioTonWsbz.do"
|
||||
"ismt_option": "/jsjt/xcsc/tprk/ismtOptioTonWsbz.do",
|
||||
"get_params": "/select/getcombcodeWsbz.do",
|
||||
"get_luno": "/common/luno/getLunoListWsbz.do",
|
||||
"get_order_info_by_xpack": "/jsjt/xcsc/tprk/getXsddBzrkGridListByXpackToWsbz.do",
|
||||
"get_package_statistics": "/jsjt/xcsc/tprk/getBzNumByOrderidWszb.do",
|
||||
"get_tray_package_statistics": "/jsjt/xcsc/tprk/getBzNumByTrayWszb.do",
|
||||
"get_spack_info": "/jsjt/xcsc/tprk/getSpackWszb.do"
|
||||
},
|
||||
"database": {
|
||||
"default": "sqlite",
|
||||
@ -87,10 +94,11 @@
|
||||
"data_bits": 8,
|
||||
"parity": "N",
|
||||
"port": "9600",
|
||||
"ser": "COM2",
|
||||
"ser": "",
|
||||
"stable_threshold": 10,
|
||||
"stop_bits": 1,
|
||||
"timeout": 1
|
||||
"timeout": 1,
|
||||
"comment": "称重通过寄存器D11获取,无需串口"
|
||||
},
|
||||
"xj": {
|
||||
"bit": 10,
|
||||
@ -99,7 +107,7 @@
|
||||
"parity": "N",
|
||||
"port": "19200",
|
||||
"query_cmd": "01 41 0d",
|
||||
"query_interval": 5,
|
||||
"query_interval": 1,
|
||||
"auto_query": true,
|
||||
"ser": "COM3",
|
||||
"stop_bits": 1,
|
||||
@ -109,10 +117,10 @@
|
||||
"code": "scanner",
|
||||
"data_bits": 8,
|
||||
"parity": "N",
|
||||
"port": "9600",
|
||||
"ser": "COM3",
|
||||
"port": "115200",
|
||||
"ser": "COM1",
|
||||
"stop_bits": 1,
|
||||
"timeout": 1
|
||||
"timeout": 0.5
|
||||
}
|
||||
},
|
||||
"electricity": {
|
||||
|
||||
@ -180,7 +180,7 @@ class InspectionDAO:
|
||||
|
||||
# 构建SQL
|
||||
sql = f"""
|
||||
UPDATE inspection_config
|
||||
UPDATE wsbz_inspection_config
|
||||
SET {', '.join(update_fields)}
|
||||
WHERE id = ?
|
||||
"""
|
||||
@ -253,7 +253,8 @@ class InspectionDAO:
|
||||
gzl = ?, maxsl = ?, cz = ?, size = ?, cd = ?, luno = ?,
|
||||
qfqd = ?, pono = ?, xj = ?, ysl = ?, dycz = ?,
|
||||
zx_code = ?, edit_id = ?, remarks = ?, zx_name = ?,
|
||||
bccd = ? ,tccd = ?, zzyq = ?, customer = ?,customerexp = ?,bz_bqd = ?,bz_tqd = ?,type_name = ?,remarks_hb=?
|
||||
bccd = ? ,tccd = ?, zzyq = ?, customer = ?,customerexp = ?,
|
||||
bz_bqd = ?,bz_tqd = ?,type_name = ?,remarks_hb=?,khno=?
|
||||
WHERE ddmo = ?
|
||||
"""
|
||||
params = (
|
||||
@ -295,6 +296,7 @@ class InspectionDAO:
|
||||
data.get("bz_tqd", ""),
|
||||
data.get("type_name", ""),
|
||||
data.get("remarks_hb", ""),
|
||||
data.get("khno", ""),
|
||||
data.get("mo", "") # WHERE 条件参数
|
||||
)
|
||||
logging.info(f"更新订单信息: ddmo={data.get('mo', '')}")
|
||||
@ -305,10 +307,12 @@ class InspectionDAO:
|
||||
data_corp, user_id, user_name, gzl_zl, ddmo, xpack,
|
||||
qd, spack_type, mxzs, jt, ddnote, code, type,
|
||||
lable, lib, gzl, maxsl, cz, size, cd, luno, qfqd,
|
||||
pono, xj, ysl, dycz, zx_code, edit_id, remarks,zx_name,bccd,tccd,zzyq,customer,customerexp,bz_bqd,bz_tqd,type_name,remarks_hb
|
||||
pono, xj, ysl, dycz, zx_code, edit_id, remarks,zx_name,
|
||||
bccd,tccd,zzyq,customer,customerexp,bz_bqd,bz_tqd,type_name,
|
||||
remarks_hb,khno
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||
)
|
||||
"""
|
||||
params = (
|
||||
@ -350,7 +354,8 @@ class InspectionDAO:
|
||||
data.get("bz_bqd", ""),
|
||||
data.get("bz_tqd", ""),
|
||||
data.get("type_name", ""),
|
||||
data.get("remarks_hb", "")
|
||||
data.get("remarks_hb", ""),
|
||||
data.get("khno", "")
|
||||
)
|
||||
logging.info(f"插入新订单信息: ddmo={data.get('mo', '')}")
|
||||
|
||||
@ -389,14 +394,14 @@ class InspectionDAO:
|
||||
status = item.get('status', '')
|
||||
remark = item.get('remark', '')
|
||||
tray_id = item.get('tray_id', '')
|
||||
|
||||
package_id = item.get('package_id', '')
|
||||
# 获取新游标执行查询,避免递归使用
|
||||
check_cursor = db.get_new_cursor()
|
||||
check_sql = """
|
||||
SELECT id FROM wsbz_inspection_data
|
||||
WHERE order_id = ? AND gc_note = ? AND position = ? AND tray_id = ?
|
||||
WHERE order_id = ? AND gc_note = ? AND position = ? AND tray_id = ? AND package_id = ?
|
||||
"""
|
||||
check_params = (order_id, gc_note, position, tray_id)
|
||||
check_params = (order_id, gc_note, position, tray_id, package_id)
|
||||
|
||||
check_cursor.execute(check_sql, check_params)
|
||||
existing_record = check_cursor.fetchone()
|
||||
@ -408,12 +413,12 @@ class InspectionDAO:
|
||||
UPDATE wsbz_inspection_data
|
||||
SET config_id = ?, value = ?, status = ?, remark = ?,
|
||||
update_time = ?, update_by = ?
|
||||
WHERE order_id = ? AND gc_note = ? AND position = ? AND tray_id = ?
|
||||
WHERE order_id = ? AND gc_note = ? AND position = ? AND tray_id = ? AND package_id = ?
|
||||
"""
|
||||
update_params = (
|
||||
config_id, value, status, remark,
|
||||
current_time, username,
|
||||
order_id, gc_note, position, tray_id
|
||||
order_id, gc_note, position, tray_id, package_id
|
||||
)
|
||||
db.execute_update(update_sql, update_params)
|
||||
else:
|
||||
@ -421,12 +426,12 @@ class InspectionDAO:
|
||||
insert_sql = """
|
||||
INSERT INTO wsbz_inspection_data (
|
||||
order_id, gc_note, position, config_id, value, status, remark,
|
||||
create_time, create_by, update_time, update_by, tray_id
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
create_time, create_by, update_time, update_by, tray_id, package_id
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
insert_params = (
|
||||
order_id, gc_note, position, config_id, value, status, remark,
|
||||
current_time, username, current_time, username, tray_id
|
||||
current_time, username, current_time, username, tray_id, package_id
|
||||
)
|
||||
db.execute_update(insert_sql, insert_params)
|
||||
|
||||
@ -436,21 +441,36 @@ class InspectionDAO:
|
||||
except Exception as e:
|
||||
logging.error(f"保存检验数据失败: {str(e)}")
|
||||
return False
|
||||
def get_inspection_data_unfinished(self, tray_id):
|
||||
def get_inspection_data_unfinished(self, tray_id, package_id=None):
|
||||
"""获取未完成的检验数据,通过是否贴标来判断
|
||||
|
||||
Args:
|
||||
tray_id: 托盘号
|
||||
package_id: 箱号(spack),如果为None则只使用tray_id查询
|
||||
|
||||
Returns:
|
||||
list: 未完成的检验数据列表
|
||||
"""
|
||||
try:
|
||||
# 先获取所有没有贴标的工程号
|
||||
sql_orders = """
|
||||
SELECT DISTINCT d.gc_note
|
||||
FROM wsbz_inspection_data d
|
||||
WHERE d.is_deleted = FALSE AND d.tray_id = ?
|
||||
AND d.position = 11 AND COALESCE(d.value,'') = ''
|
||||
"""
|
||||
params = (tray_id,)
|
||||
if package_id:
|
||||
# 如果提供了package_id,则同时使用tray_id和package_id查询
|
||||
sql_orders = """
|
||||
SELECT DISTINCT d.gc_note
|
||||
FROM wsbz_inspection_data d
|
||||
WHERE d.is_deleted = FALSE AND d.tray_id = ? AND d.package_id = ?
|
||||
AND status != 'labeled'
|
||||
"""
|
||||
params = (tray_id, package_id)
|
||||
else:
|
||||
# 如果没有提供package_id,则只使用tray_id查询
|
||||
sql_orders = """
|
||||
SELECT DISTINCT d.gc_note
|
||||
FROM wsbz_inspection_data d
|
||||
WHERE d.is_deleted = FALSE AND d.tray_id = ?
|
||||
AND status != 'labeled'
|
||||
"""
|
||||
params = (tray_id,)
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
db.cursor.execute(sql_orders, params)
|
||||
gc_notes = db.cursor.fetchall()
|
||||
@ -463,17 +483,32 @@ class InspectionDAO:
|
||||
placeholders = ','.join(['?' for _ in gc_notes])
|
||||
|
||||
# 获取这些工程号的所有检验数据
|
||||
sql = f"""
|
||||
SELECT d.id, d.gc_note, d.position, d.config_id, d.value, d.status, d.remark,
|
||||
c.name, c.display_name, c.data_type, c.unit
|
||||
FROM wsbz_inspection_data d
|
||||
LEFT JOIN wsbz_inspection_config c ON d.config_id = c.id
|
||||
WHERE d.is_deleted = FALSE AND d.tray_id = ?
|
||||
AND d.gc_note IN ({placeholders})
|
||||
ORDER BY d.create_time
|
||||
"""
|
||||
|
||||
params = [tray_id] + gc_notes
|
||||
if package_id:
|
||||
# 如果提供了package_id,则同时使用tray_id和package_id查询
|
||||
sql = f"""
|
||||
SELECT d.id, d.gc_note, d.position, d.config_id, d.value, d.status, d.remark,
|
||||
c.name, c.display_name, c.data_type, c.unit, d.package_id
|
||||
FROM wsbz_inspection_data d
|
||||
LEFT JOIN wsbz_inspection_config c ON d.config_id = c.id
|
||||
WHERE d.is_deleted = FALSE AND d.tray_id = ? AND d.package_id = ?
|
||||
AND d.gc_note IN ({placeholders})
|
||||
ORDER BY d.create_time
|
||||
"""
|
||||
|
||||
params = [tray_id, package_id] + gc_notes
|
||||
else:
|
||||
# 如果没有提供package_id,则只使用tray_id查询
|
||||
sql = f"""
|
||||
SELECT d.id, d.gc_note, d.position, d.config_id, d.value, d.status, d.remark,
|
||||
c.name, c.display_name, c.data_type, c.unit, d.package_id
|
||||
FROM wsbz_inspection_data d
|
||||
LEFT JOIN wsbz_inspection_config c ON d.config_id = c.id
|
||||
WHERE d.is_deleted = FALSE AND d.tray_id = ?
|
||||
AND d.gc_note IN ({placeholders})
|
||||
ORDER BY d.create_time
|
||||
"""
|
||||
|
||||
params = [tray_id] + gc_notes
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
db.cursor.execute(sql, params)
|
||||
results = db.cursor.fetchall()
|
||||
@ -491,7 +526,8 @@ class InspectionDAO:
|
||||
'name': row[7],
|
||||
'display_name': row[8],
|
||||
'data_type': row[9],
|
||||
'unit': row[10]
|
||||
'unit': row[10],
|
||||
'package_id': row[11] if len(row) > 11 else ''
|
||||
}
|
||||
data_list.append(data)
|
||||
|
||||
@ -499,26 +535,40 @@ class InspectionDAO:
|
||||
except Exception as e:
|
||||
logging.error(f"获取未完成的检验数据失败: {str(e)}")
|
||||
return []
|
||||
def get_inspection_data_by_order(self, order_id,gc_note, tray_id):
|
||||
def get_inspection_data_by_order(self, order_id, gc_note, tray_id, package_id=None):
|
||||
"""根据工程号获取检验数据
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
gc_note: 工程号
|
||||
tray_id: 托盘号
|
||||
package_id: 箱号(spack),如果为None则只使用tray_id查询
|
||||
Returns:
|
||||
list: 检验数据列表
|
||||
"""
|
||||
try:
|
||||
sql = """
|
||||
SELECT d.id, d.position, d.config_id, d.value, d.status, d.remark,
|
||||
c.name, c.display_name, c.data_type, c.unit
|
||||
FROM wsbz_inspection_data d
|
||||
LEFT JOIN wsbz_inspection_config c ON d.config_id = c.id
|
||||
WHERE d.order_id = ? AND d.gc_note = ? AND d.is_deleted = FALSE AND d.tray_id = ?
|
||||
ORDER BY d.create_time, d.order_id, d.position
|
||||
"""
|
||||
params = (order_id, gc_note, tray_id)
|
||||
if package_id:
|
||||
# 如果提供了package_id,则同时使用tray_id和package_id查询
|
||||
sql = """
|
||||
SELECT d.id, d.position, d.config_id, d.value, d.status, d.remark,
|
||||
c.name, c.display_name, c.data_type, c.unit, d.package_id
|
||||
FROM wsbz_inspection_data d
|
||||
LEFT JOIN wsbz_inspection_config c ON d.config_id = c.id
|
||||
WHERE d.order_id = ? AND d.gc_note = ? AND d.is_deleted = FALSE AND d.tray_id = ? AND d.package_id = ?
|
||||
ORDER BY d.create_time, d.order_id, d.position
|
||||
"""
|
||||
params = (order_id, gc_note, tray_id, package_id)
|
||||
else:
|
||||
# 如果没有提供package_id,则只使用tray_id查询
|
||||
sql = """
|
||||
SELECT d.id, d.position, d.config_id, d.value, d.status, d.remark,
|
||||
c.name, c.display_name, c.data_type, c.unit, d.package_id
|
||||
FROM wsbz_inspection_data d
|
||||
LEFT JOIN wsbz_inspection_config c ON d.config_id = c.id
|
||||
WHERE d.order_id = ? AND d.gc_note = ? AND d.is_deleted = FALSE AND d.tray_id = ?
|
||||
ORDER BY d.create_time, d.order_id, d.position
|
||||
"""
|
||||
params = (order_id, gc_note, tray_id)
|
||||
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
db.cursor.execute(sql, params)
|
||||
@ -536,7 +586,8 @@ class InspectionDAO:
|
||||
'name': row[6],
|
||||
'display_name': row[7],
|
||||
'data_type': row[8],
|
||||
'unit': row[9]
|
||||
'unit': row[9],
|
||||
'package_id': row[10] if len(row) > 10 else ''
|
||||
}
|
||||
data_list.append(data)
|
||||
|
||||
@ -545,11 +596,12 @@ class InspectionDAO:
|
||||
logging.error(f"获取检验数据失败: {str(e)}")
|
||||
return []
|
||||
|
||||
def get_package_record(self, tray_id):
|
||||
def get_package_record(self, tray_id, package_id=None):
|
||||
"""根据托盘号获取包装记录
|
||||
|
||||
Args:
|
||||
tray_id: 托盘号
|
||||
package_id: 箱号(spack),如果为None则只使用tray_id查询
|
||||
Returns:
|
||||
list: 包装记录列表
|
||||
"""
|
||||
@ -578,23 +630,33 @@ class InspectionDAO:
|
||||
except Exception as e:
|
||||
logging.error(f"获取包装记录失败: {str(e)}")
|
||||
return []
|
||||
def save_package_record(self, order_id, tray_id, label_value, weight_value,net_weight_value, finish_time,gc_note):
|
||||
def save_package_record(self, order_id, tray_id, label_value, weight_value, net_weight_value, finish_time, gc_note=None, package_id=None):
|
||||
"""保存包装记录
|
||||
|
||||
Args:
|
||||
order_id: 工程号
|
||||
order_id: 订单号
|
||||
tray_id: 托盘号
|
||||
label_value: 标签值
|
||||
weight_value: 重量值
|
||||
label_value: 贴标值
|
||||
weight_value: 称重值
|
||||
net_weight_value: 净重值
|
||||
finish_time: 完成时间
|
||||
gc_note: 工程号
|
||||
package_id: 箱号(spack),如果为None则使用tray_id
|
||||
|
||||
Returns:
|
||||
bool: 保存是否成功
|
||||
"""
|
||||
# TODO:调用接口,获取到工程号对应的其他信息,比如材质,规格,后续完成
|
||||
try:
|
||||
# 如果没有提供package_id,则使用tray_id
|
||||
if package_id is None:
|
||||
package_id = tray_id
|
||||
|
||||
sql = """
|
||||
INSERT INTO wsbz_inspection_pack_data (order_id, tray_id, axis_package_id, weight, net_weight, pack_time, create_time, create_by, update_time, update_by, is_deleted,gc_note)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)
|
||||
INSERT INTO wsbz_inspection_pack_data (order_id, tray_id, axis_package_id, weight, net_weight, pack_time, create_time, create_by, update_time, update_by, is_deleted, gc_note, package_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
params = (order_id, tray_id, label_value, weight_value, net_weight_value, finish_time, datetime.now(), 'system', datetime.now(), 'system', False,gc_note)
|
||||
params = (order_id, tray_id, label_value, weight_value, net_weight_value, finish_time, datetime.now(), 'system', datetime.now(), 'system', False, gc_note, package_id)
|
||||
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
db.begin_transaction()
|
||||
@ -605,21 +667,136 @@ class InspectionDAO:
|
||||
except Exception as e:
|
||||
logging.error(f"保存包装记录失败: {str(e)}")
|
||||
return False
|
||||
def delete_inspection_data(self, order_id, gc_note, tray_id):
|
||||
|
||||
def get_product_status(self, order_id, gc_note, tray_id):
|
||||
"""获取产品的当前状态
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
gc_note: 工程号
|
||||
tray_id: 托盘号
|
||||
|
||||
Returns:
|
||||
str: 产品状态,如果没有找到则返回'init'
|
||||
"""
|
||||
try:
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
sql = """
|
||||
SELECT status FROM wsbz_inspection_data
|
||||
WHERE order_id = ? AND gc_note = ? AND tray_id = ?
|
||||
ORDER BY id ASC LIMIT 1
|
||||
"""
|
||||
params = (order_id, gc_note, tray_id)
|
||||
db.cursor.execute(sql, params)
|
||||
result = db.cursor.fetchone()
|
||||
return result[0] if result and result[0] else 'init' # 默认为init状态
|
||||
except Exception as e:
|
||||
logging.error(f"获取产品状态失败: {str(e)}")
|
||||
return 'init' # 出错时返回默认状态
|
||||
|
||||
def check_package_record_exists(self, order_id, gc_note, tray_id, package_id=None):
|
||||
"""检查指定工程号和托盘号的包装记录是否已存在
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
gc_note: 工程号
|
||||
tray_id: 托盘号
|
||||
package_id: 箱号(spack),如果为None则只使用tray_id查询
|
||||
|
||||
Returns:
|
||||
bool: 记录是否存在
|
||||
"""
|
||||
try:
|
||||
if package_id:
|
||||
# 如果提供了package_id,则同时使用tray_id和package_id查询
|
||||
sql = """
|
||||
SELECT COUNT(*) FROM wsbz_inspection_pack_data
|
||||
WHERE order_id = ? AND gc_note = ? AND tray_id = ? AND package_id = ? AND is_deleted = FALSE
|
||||
"""
|
||||
params = (order_id, gc_note, tray_id, package_id)
|
||||
else:
|
||||
# 如果没有提供package_id,则只使用tray_id查询
|
||||
sql = """
|
||||
SELECT COUNT(*) FROM wsbz_inspection_pack_data
|
||||
WHERE order_id = ? AND gc_note = ? AND tray_id = ? AND is_deleted = FALSE
|
||||
"""
|
||||
params = (order_id, gc_note, tray_id)
|
||||
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
db.cursor.execute(sql, params)
|
||||
result = db.cursor.fetchone()
|
||||
|
||||
return result[0] > 0 if result else False
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"检查包装记录是否存在失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def update_product_status(self, order_id, gc_note, tray_id, new_status, package_id=None):
|
||||
"""更新产品的状态
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
gc_note: 工程号
|
||||
tray_id: 托盘号
|
||||
new_status: 新状态
|
||||
package_id: 箱号(spack),如果为None则只使用tray_id查询
|
||||
|
||||
Returns:
|
||||
bool: 更新是否成功
|
||||
"""
|
||||
try:
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
# 更新该产品所有记录的状态字段
|
||||
if package_id:
|
||||
# 如果提供了package_id,则同时使用tray_id和package_id查询
|
||||
update_sql = """
|
||||
UPDATE wsbz_inspection_data SET status = ?, update_time = ?
|
||||
WHERE order_id = ? AND gc_note = ? AND tray_id = ? AND package_id = ?
|
||||
"""
|
||||
update_params = (new_status, datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
order_id, gc_note, tray_id, package_id)
|
||||
else:
|
||||
# 如果没有提供package_id,则只使用tray_id查询
|
||||
update_sql = """
|
||||
UPDATE wsbz_inspection_data SET status = ?, update_time = ?
|
||||
WHERE order_id = ? AND gc_note = ? AND tray_id = ?
|
||||
"""
|
||||
update_params = (new_status, datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
order_id, gc_note, tray_id)
|
||||
db.execute_update(update_sql, update_params)
|
||||
if package_id:
|
||||
logging.info(f"已更新产品状态: 订单号={order_id}, 工程号={gc_note}, 托盘号={tray_id}, 箱号={package_id}, 新状态={new_status}")
|
||||
else:
|
||||
logging.info(f"已更新产品状态: 订单号={order_id}, 工程号={gc_note}, 托盘号={tray_id}, 新状态={new_status}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error(f"更新产品状态失败: {str(e)}")
|
||||
return False
|
||||
def delete_inspection_data(self, order_id, gc_note, tray_id, package_id=None):
|
||||
"""删除检验数据
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
gc_note: 工程号
|
||||
tray_id: 托盘号
|
||||
package_id: 箱号(spack),如果为None则只使用tray_id查询
|
||||
"""
|
||||
try:
|
||||
sql = """
|
||||
UPDATE wsbz_inspection_data
|
||||
SET is_deleted = TRUE
|
||||
WHERE order_id = ? AND gc_note = ? AND tray_id = ?
|
||||
"""
|
||||
params = (order_id, gc_note, tray_id)
|
||||
if package_id:
|
||||
# 如果提供了package_id,则同时使用tray_id和package_id查询
|
||||
sql = """
|
||||
DELETE FROM wsbz_inspection_data
|
||||
WHERE gc_note = ? AND tray_id = ? AND package_id = ?
|
||||
"""
|
||||
params = (gc_note, tray_id, package_id)
|
||||
else:
|
||||
# 如果没有提供package_id,则只使用tray_id查询
|
||||
sql = """
|
||||
DELETE FROM wsbz_inspection_data
|
||||
WHERE gc_note = ? AND tray_id = ?
|
||||
"""
|
||||
params = (gc_note, tray_id)
|
||||
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
db.begin_transaction()
|
||||
@ -684,7 +861,7 @@ class InspectionDAO:
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
db.cursor.execute(sql, params)
|
||||
result = db.cursor.fetchone()
|
||||
return result[0],result[1] if result else None,None
|
||||
return (result[0],result[1]) if result else (None,None)
|
||||
except Exception as e:
|
||||
logging.error(f"获取线径范围失败: {str(e)}")
|
||||
return None,None
|
||||
@ -764,7 +941,7 @@ class InspectionDAO:
|
||||
try:
|
||||
sql = """
|
||||
SELECT DISTINCT data_corp, user_id, user_name, gzl_zl, mzl, ddmo, xpack, qd, spack_type, mxzs, jt, ddnote, code, type, lable, lib, gzl, maxsl, cz, size, cd, luno,
|
||||
qfqd, pono, xj, ysl, dycz, zx_code, edit_id, remarks, zx_name, bccd, tccd, zzyq, customer, customerexp, bz_bqd as bqd, bz_tqd as tqd, type_name, remarks_hb
|
||||
coalesce(qfqd,'') as qfqd, pono, xj, ysl, dycz, zx_code, edit_id, remarks, zx_name, bccd, tccd, zzyq, customer, customerexp, bz_bqd as bqd, bz_tqd as tqd, type_name, remarks_hb
|
||||
FROM wsbz_order_info WHERE ddmo = ?
|
||||
"""
|
||||
params = (order_id,)
|
||||
@ -800,7 +977,7 @@ class InspectionDAO:
|
||||
"""
|
||||
try:
|
||||
sql = """
|
||||
SELECT t1.order_id, CASE WHEN t1.position = 12 THEN 'mzl' ELSE name END AS name, value
|
||||
SELECT t1.order_id, CASE t1.position WHEN 12 THEN 'mzl' when 2 then 'xj' ELSE name END AS name, value
|
||||
FROM wsbz_inspection_data t1
|
||||
LEFT JOIN main.wsbz_inspection_config wic ON t1.config_id = wic.id
|
||||
WHERE gc_note = ? AND t1.order_id = ?
|
||||
@ -906,4 +1083,343 @@ class InspectionDAO:
|
||||
'order_num_month': 0,
|
||||
'order_num_year': 0,
|
||||
'order_num_all': 0
|
||||
}
|
||||
}
|
||||
|
||||
def save_luno_info(self, luno_code, luno_data):
|
||||
"""保存炉号信息到数据库
|
||||
|
||||
Args:
|
||||
luno_code: 炉号编码
|
||||
luno_data: 炉号数据字典
|
||||
|
||||
Returns:
|
||||
bool: 保存是否成功
|
||||
"""
|
||||
try:
|
||||
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
# 检查是否已存在该炉号
|
||||
check_sql = "SELECT id FROM wsbz_luno_info WHERE luono = ?"
|
||||
with SQLUtils('sqlite') as db:
|
||||
db.cursor.execute(check_sql, (luno_code,))
|
||||
existing = db.cursor.fetchone()
|
||||
|
||||
if existing:
|
||||
# 更新现有记录
|
||||
update_sql = """
|
||||
UPDATE wsbz_luno_info SET
|
||||
cz = ?, gg = ?, gc = ?, bz = ?, data_corp = ?, data_corp_name = ?,
|
||||
c = ?, si = ?, mn = ?, p = ?, s = ?, ni = ?, cr = ?, ti = ?, mo = ?, cu = ?,
|
||||
others = ?, klqd = ?, ysl = ?, rq = ?, update_time = ?, update_by = ?
|
||||
WHERE luono = ?
|
||||
"""
|
||||
params = (
|
||||
luno_data.get('cz', ''),
|
||||
luno_data.get('gg', ''),
|
||||
luno_data.get('gc', ''),
|
||||
luno_data.get('bz', ''),
|
||||
luno_data.get('data_corp', ''),
|
||||
luno_data.get('data_corp_name', ''),
|
||||
luno_data.get('c', ''),
|
||||
luno_data.get('si', ''),
|
||||
luno_data.get('mn', ''),
|
||||
luno_data.get('p', ''),
|
||||
luno_data.get('s', ''),
|
||||
luno_data.get('ni', ''),
|
||||
luno_data.get('cr', ''),
|
||||
luno_data.get('ti', ''),
|
||||
luno_data.get('mo', ''),
|
||||
luno_data.get('cu', ''),
|
||||
luno_data.get('others', ''),
|
||||
luno_data.get('klqd', ''),
|
||||
luno_data.get('ysl', ''),
|
||||
luno_data.get('rq', ''),
|
||||
current_time,
|
||||
luno_data.get('user_name', 'system'),
|
||||
luno_code
|
||||
)
|
||||
db.cursor.execute(update_sql, params)
|
||||
else:
|
||||
# 插入新记录
|
||||
insert_sql = """
|
||||
INSERT INTO wsbz_luno_info (
|
||||
luono, cz, gg, gc, bz, data_corp, data_corp_name,
|
||||
c, si, mn, p, s, ni, cr, ti, mo, cu, others, klqd, ysl, rq,
|
||||
create_time, create_by, update_time, update_by
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"""
|
||||
params = (
|
||||
luno_code,
|
||||
luno_data.get('cz', ''),
|
||||
luno_data.get('gg', ''),
|
||||
luno_data.get('gc', ''),
|
||||
luno_data.get('bz', ''),
|
||||
luno_data.get('data_corp', ''),
|
||||
luno_data.get('data_corp_name', ''),
|
||||
luno_data.get('c', ''),
|
||||
luno_data.get('si', ''),
|
||||
luno_data.get('mn', ''),
|
||||
luno_data.get('p', ''),
|
||||
luno_data.get('s', ''),
|
||||
luno_data.get('ni', ''),
|
||||
luno_data.get('cr', ''),
|
||||
luno_data.get('ti', ''),
|
||||
luno_data.get('mo', ''),
|
||||
luno_data.get('cu', ''),
|
||||
luno_data.get('others', ''),
|
||||
luno_data.get('klqd', ''),
|
||||
luno_data.get('ysl', ''),
|
||||
luno_data.get('rq', ''),
|
||||
current_time,
|
||||
luno_data.get('user_name', 'system'),
|
||||
current_time,
|
||||
luno_data.get('user_name', 'system')
|
||||
)
|
||||
db.cursor.execute(insert_sql, params)
|
||||
|
||||
db.connection.commit()
|
||||
|
||||
logging.info(f"成功保存炉号信息: {luno_code}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"保存炉号信息失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def get_luno_info(self, luno_code):
|
||||
"""根据炉号获取炉号信息
|
||||
|
||||
Args:
|
||||
luno_code: 炉号编码
|
||||
|
||||
Returns:
|
||||
dict: 炉号信息字典,未找到返回None
|
||||
"""
|
||||
try:
|
||||
sql = """
|
||||
SELECT luono, cz, gg, gc, bz, data_corp, data_corp_name,
|
||||
c, si, mn, p, s, ni, cr, ti, mo, cu, others, klqd, ysl, rq,
|
||||
create_time, create_by, update_time, update_by
|
||||
FROM wsbz_luno_info
|
||||
WHERE luono = ? AND is_deleted = FALSE
|
||||
"""
|
||||
|
||||
with SQLUtils('sqlite') as db:
|
||||
db.cursor.execute(sql, (luno_code,))
|
||||
row = db.cursor.fetchone()
|
||||
|
||||
if row:
|
||||
# 获取列名
|
||||
column_names = [desc[0] for desc in db.cursor.description]
|
||||
|
||||
# 转换为字典
|
||||
luno_info = {}
|
||||
for i, value in enumerate(row):
|
||||
if i < len(column_names):
|
||||
luno_info[column_names[i]] = value
|
||||
|
||||
return luno_info
|
||||
else:
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"获取炉号信息失败: {str(e)}")
|
||||
return None
|
||||
def delete_package_record(self, order_id, gc_note, tray_id, package_id=None):
|
||||
"""删除包装记录
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
gc_note: 工程号
|
||||
tray_id: 托盘号
|
||||
package_id: 箱号(spack),如果为None则只使用tray_id查询
|
||||
|
||||
Returns:
|
||||
bool: 删除是否成功
|
||||
"""
|
||||
try:
|
||||
if package_id:
|
||||
# 如果提供了package_id,则同时使用tray_id和package_id查询
|
||||
sql = """
|
||||
DELETE FROM wsbz_inspection_pack_data
|
||||
WHERE gc_note = ? AND tray_id = ? AND package_id = ?
|
||||
"""
|
||||
params = (gc_note, tray_id, package_id)
|
||||
else:
|
||||
# 如果没有提供package_id,则只使用tray_id查询
|
||||
sql = """
|
||||
DELETE FROM wsbz_inspection_pack_data
|
||||
WHERE gc_note = ? AND tray_id = ?
|
||||
"""
|
||||
params = (gc_note, tray_id)
|
||||
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
db.begin_transaction()
|
||||
db.execute_update(sql, params)
|
||||
db.commit_transaction()
|
||||
|
||||
if package_id:
|
||||
logging.info(f"已删除包装记录: 订单号={order_id}, 工程号={gc_note}, 托盘号={tray_id}, 箱号={package_id}")
|
||||
else:
|
||||
logging.info(f"已删除包装记录: 订单号={order_id}, 工程号={gc_note}, 托盘号={tray_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error(f"删除包装记录失败: {str(e)}")
|
||||
return False
|
||||
def get_inspection_data_by_config(self, order_id, gc_note, tray_id, position, config_id, package_id=None):
|
||||
"""根据工程号、托盘号、位置和配置ID查询检验数据
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
gc_note: 工程号
|
||||
tray_id: 托盘号
|
||||
position: 位置序号
|
||||
config_id: 配置ID
|
||||
package_id: 箱号(spack),如果为None则只使用tray_id查询
|
||||
|
||||
Returns:
|
||||
dict: 检验数据记录,如果不存在则返回None
|
||||
"""
|
||||
try:
|
||||
# 使用SQLUtils获取数据库连接
|
||||
if package_id:
|
||||
# 如果提供了package_id,则同时使用tray_id和package_id查询
|
||||
sql = """
|
||||
SELECT id, order_id, gc_note, position, config_id, value, status, remark, tray_id, package_id, create_time, update_time
|
||||
FROM wsbz_inspection_data
|
||||
WHERE order_id = ? AND gc_note = ? AND tray_id = ? AND position = ? AND config_id = ? AND package_id = ?
|
||||
ORDER BY update_time DESC
|
||||
LIMIT 1
|
||||
"""
|
||||
params = (order_id, gc_note, tray_id, position, config_id, package_id)
|
||||
else:
|
||||
# 如果没有提供package_id,则只使用tray_id查询
|
||||
sql = """
|
||||
SELECT id, order_id, gc_note, position, config_id, value, status, remark, tray_id, package_id, create_time, update_time
|
||||
FROM wsbz_inspection_data
|
||||
WHERE order_id = ? AND gc_note = ? AND tray_id = ? AND position = ? AND config_id = ?
|
||||
ORDER BY update_time DESC
|
||||
LIMIT 1
|
||||
"""
|
||||
params = (order_id, gc_note, tray_id, position, config_id)
|
||||
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
# 执行查询
|
||||
db.cursor.execute(sql, params)
|
||||
|
||||
# 获取结果
|
||||
row = db.cursor.fetchone()
|
||||
|
||||
# 如果有结果,转换为字典
|
||||
if row:
|
||||
return {
|
||||
'id': row[0],
|
||||
'order_id': row[1],
|
||||
'gc_note': row[2],
|
||||
'position': row[3],
|
||||
'config_id': row[4],
|
||||
'value': row[5],
|
||||
'status': row[6],
|
||||
'remark': row[7],
|
||||
'tray_id': row[8],
|
||||
'package_id': row[9],
|
||||
'create_time': row[10],
|
||||
'update_time': row[11]
|
||||
}
|
||||
else:
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"查询检验数据失败: {str(e)}")
|
||||
return None
|
||||
# def get_package_statistics(self, order_id=None):
|
||||
# """获取包装记录的统计数据
|
||||
|
||||
# Args:
|
||||
# order_id: 订单号,如果为None则获取所有订单的统计
|
||||
|
||||
# Returns:
|
||||
# dict: 包含当前订单和所有订单的统计数据
|
||||
# {
|
||||
# 'count': 当前订单的记录数量,
|
||||
# 'weight': 当前订单的总重量,
|
||||
# 'count_all': 所有订单的记录数量,
|
||||
# 'weight_all': 所有订单的总重量
|
||||
# }
|
||||
# """
|
||||
# try:
|
||||
# # 构建SQL查询
|
||||
# sql = """
|
||||
# SELECT SUM(weight) weight,
|
||||
# SUM(count) count,
|
||||
# SUM(count_all) count_all,
|
||||
# SUM(weight_all) weight_all
|
||||
# FROM (
|
||||
# SELECT COUNT(gc_note) AS count,
|
||||
# SUM(weight) AS weight,
|
||||
# '' AS count_all,
|
||||
# '' AS weight_all
|
||||
# FROM wsbz_inspection_pack_data
|
||||
# WHERE order_id = ?
|
||||
# UNION ALL
|
||||
# SELECT '', '',
|
||||
# COUNT(gc_note) AS count_all,
|
||||
# SUM(weight) AS weight_all
|
||||
# FROM wsbz_inspection_pack_data
|
||||
# ) a
|
||||
# """
|
||||
|
||||
# with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
# # 执行查询
|
||||
# db.cursor.execute(sql, (order_id or '',))
|
||||
|
||||
# # 获取结果
|
||||
# row = db.cursor.fetchone()
|
||||
|
||||
# # 如果有结果,转换为字典
|
||||
# if row:
|
||||
# return {
|
||||
# 'weight': float(row[0] or 0),
|
||||
# 'count': int(row[1] or 0),
|
||||
# 'count_all': int(row[2] or 0),
|
||||
# 'weight_all': float(row[3] or 0)
|
||||
# }
|
||||
# else:
|
||||
# return {
|
||||
# 'weight': 0,
|
||||
# 'count': 0,
|
||||
# 'count_all': 0,
|
||||
# 'weight_all': 0
|
||||
# }
|
||||
|
||||
# except Exception as e:
|
||||
# logging.error(f"获取包装记录统计数据失败: {str(e)}")
|
||||
# return {
|
||||
# 'weight': 0,
|
||||
# 'count': 0,
|
||||
# 'count_all': 0,
|
||||
# 'weight_all': 0
|
||||
# }
|
||||
def get_spack_by_order_id(self, order_id,tray_id):
|
||||
"""根据订单号和托盘号获取箱号(spack)
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
tray_id: 托盘号
|
||||
|
||||
Returns:
|
||||
str: 箱号(spack),如果不存在则返回空字符串
|
||||
"""
|
||||
|
||||
try:
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
db.cursor.execute("SELECT package_id FROM wsbz_inspection_data WHERE order_id = ? AND tray_id = ? LIMIT 1", (order_id, tray_id))
|
||||
row = db.cursor.fetchone()
|
||||
if row:
|
||||
return row[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"获取箱号(spack)失败: {str(e)}")
|
||||
return None
|
||||
@ -367,8 +367,8 @@ class PalletTypeDAO:
|
||||
update_time, update_by, enabled, is_deleted
|
||||
) VALUES (?, ?, ?, ?, ?, ?, TRUE, FALSE)
|
||||
"""
|
||||
params = (pallet_code, tier, current_time, user_id,
|
||||
current_time, user_id)
|
||||
params = (pallet_code, tier, current_time, user_id[0],
|
||||
current_time, user_id[0])
|
||||
self.db.execute_update(insert_sql, params)
|
||||
logging.info(f"创建新托盘档案记录:包装号={pallet_code}, 层数={tier}")
|
||||
|
||||
|
||||
170
dao/report_dao.py
Normal file
170
dao/report_dao.py
Normal file
@ -0,0 +1,170 @@
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from utils.sql_utils import SQLUtils
|
||||
|
||||
class ReportDAO:
|
||||
"""报表数据访问对象,处理报表相关的数据库操作"""
|
||||
|
||||
def __init__(self):
|
||||
"""初始化数据访问对象"""
|
||||
# 不再在初始化时创建数据库连接,而是在需要时创建
|
||||
pass
|
||||
|
||||
def get_production_report(self, start_date, end_date, customer=None, material=None, spec=None):
|
||||
"""获取生产报表数据
|
||||
|
||||
Args:
|
||||
start_date: 开始日期,格式为 'YYYY-MM-DD'
|
||||
end_date: 结束日期,格式为 'YYYY-MM-DD'
|
||||
customer: 客户名称,用于模糊查询,可选
|
||||
material: 材质,用于模糊查询,可选
|
||||
spec: 规格,用于模糊查询,可选
|
||||
|
||||
Returns:
|
||||
list: 包含报表数据的字典列表
|
||||
"""
|
||||
try:
|
||||
# 构建SQL查询
|
||||
sql = """
|
||||
SELECT DATE(pack_time) AS 日期
|
||||
, customerexp AS 客户
|
||||
, t1.order_id AS 订单号
|
||||
, COUNT(DISTINCT t1.gc_note) AS '轴数'
|
||||
, t2.cz AS '材质'
|
||||
, t2.size AS '规格'
|
||||
, ROUND(SUM(t1.net_weight), 2) AS '净重'
|
||||
FROM wsbz_inspection_pack_data t1
|
||||
LEFT JOIN wsbz_order_info t2 ON t1.order_id = t2.ddmo
|
||||
WHERE pack_time BETWEEN ? AND ?
|
||||
"""
|
||||
|
||||
params = [f"{start_date} 00:00:00", f"{end_date} 23:59:59"]
|
||||
|
||||
# 添加可选的筛选条件
|
||||
if customer:
|
||||
sql += " AND customerexp LIKE ?"
|
||||
params.append(f"%{customer}%")
|
||||
|
||||
if material:
|
||||
sql += " AND t2.cz LIKE ?"
|
||||
params.append(f"%{material}%")
|
||||
|
||||
if spec:
|
||||
sql += " AND t2.size LIKE ?"
|
||||
params.append(f"%{spec}%")
|
||||
|
||||
# 添加分组条件
|
||||
sql += " GROUP BY DATE(pack_time), t2.cz, t2.size, customerexp"
|
||||
|
||||
# 执行查询
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
db.cursor.execute(sql, params)
|
||||
results = db.cursor.fetchall()
|
||||
|
||||
# 获取列名
|
||||
columns = [desc[0] for desc in db.cursor.description]
|
||||
|
||||
# 转换为字典列表
|
||||
data = []
|
||||
for row in results:
|
||||
row_dict = {}
|
||||
for i, col in enumerate(columns):
|
||||
row_dict[col] = row[i]
|
||||
data.append(row_dict)
|
||||
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"获取生产报表数据失败: {str(e)}")
|
||||
raise e
|
||||
|
||||
def get_daily_report(self, date):
|
||||
"""获取指定日期的日报表
|
||||
|
||||
Args:
|
||||
date: 日期,格式为 'YYYY-MM-DD'
|
||||
|
||||
Returns:
|
||||
list: 包含报表数据的字典列表
|
||||
"""
|
||||
return self.get_production_report(date, date)
|
||||
|
||||
def get_monthly_report(self, year, month):
|
||||
"""获取指定月份的月报表
|
||||
|
||||
Args:
|
||||
year: 年份,如 2024
|
||||
month: 月份,如 8
|
||||
|
||||
Returns:
|
||||
list: 包含报表数据的字典列表
|
||||
"""
|
||||
# 计算月初和月末
|
||||
start_date = f"{year}-{month:02d}-01"
|
||||
|
||||
# 计算月末日期
|
||||
if month == 12:
|
||||
next_year = year + 1
|
||||
next_month = 1
|
||||
else:
|
||||
next_year = year
|
||||
next_month = month + 1
|
||||
|
||||
end_date = f"{next_year}-{next_month:02d}-01"
|
||||
|
||||
# 使用日期范围查询
|
||||
return self.get_production_report(start_date, end_date)
|
||||
|
||||
def get_yearly_report(self, year):
|
||||
"""获取指定年份的年报表
|
||||
|
||||
Args:
|
||||
year: 年份,如 2024
|
||||
|
||||
Returns:
|
||||
list: 包含报表数据的字典列表
|
||||
"""
|
||||
start_date = f"{year}-01-01"
|
||||
end_date = f"{year+1}-01-01"
|
||||
|
||||
# 使用日期范围查询
|
||||
return self.get_production_report(start_date, end_date)
|
||||
|
||||
def get_customer_report(self, start_date, end_date, customer):
|
||||
"""获取指定客户的报表
|
||||
|
||||
Args:
|
||||
start_date: 开始日期,格式为 'YYYY-MM-DD'
|
||||
end_date: 结束日期,格式为 'YYYY-MM-DD'
|
||||
customer: 客户名称
|
||||
|
||||
Returns:
|
||||
list: 包含报表数据的字典列表
|
||||
"""
|
||||
return self.get_production_report(start_date, end_date, customer=customer)
|
||||
|
||||
def get_material_report(self, start_date, end_date, material):
|
||||
"""获取指定材质的报表
|
||||
|
||||
Args:
|
||||
start_date: 开始日期,格式为 'YYYY-MM-DD'
|
||||
end_date: 结束日期,格式为 'YYYY-MM-DD'
|
||||
material: 材质
|
||||
|
||||
Returns:
|
||||
list: 包含报表数据的字典列表
|
||||
"""
|
||||
return self.get_production_report(start_date, end_date, material=material)
|
||||
|
||||
def get_spec_report(self, start_date, end_date, spec):
|
||||
"""获取指定规格的报表
|
||||
|
||||
Args:
|
||||
start_date: 开始日期,格式为 'YYYY-MM-DD'
|
||||
end_date: 结束日期,格式为 'YYYY-MM-DD'
|
||||
spec: 规格
|
||||
|
||||
Returns:
|
||||
list: 包含报表数据的字典列表
|
||||
"""
|
||||
return self.get_production_report(start_date, end_date, spec=spec)
|
||||
BIN
data/inspection.db
Normal file
BIN
data/inspection.db
Normal file
Binary file not shown.
BIN
db/jtDB.db
BIN
db/jtDB.db
Binary file not shown.
@ -161,4 +161,36 @@ CREATE TABLE IF NOT EXISTS packaging_record (
|
||||
create_by VARCHAR(50) NOT NULL,
|
||||
update_time TIMESTAMP,
|
||||
update_by VARCHAR(50)
|
||||
);
|
||||
|
||||
-- 创建炉号信息表
|
||||
CREATE TABLE IF NOT EXISTS wsbz_luno_info (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
luono VARCHAR(50) NOT NULL, -- 炉号
|
||||
cz VARCHAR(50), -- 材质
|
||||
gg VARCHAR(50), -- 规格
|
||||
gc VARCHAR(50), -- 钢厂
|
||||
bz VARCHAR(50), -- 标准
|
||||
data_corp VARCHAR(50), -- 公司账套
|
||||
data_corp_name VARCHAR(100), -- 公司账套名称
|
||||
c VARCHAR(20), -- C含量
|
||||
si VARCHAR(20), -- Si含量
|
||||
mn VARCHAR(20), -- Mn含量
|
||||
p VARCHAR(20), -- P含量
|
||||
s VARCHAR(20), -- S含量
|
||||
ni VARCHAR(20), -- Ni含量
|
||||
cr VARCHAR(20), -- Cr含量
|
||||
ti VARCHAR(20), -- Ti含量
|
||||
mo VARCHAR(20), -- Mo含量
|
||||
cu VARCHAR(20), -- Cu含量
|
||||
others VARCHAR(100), -- 其他元素
|
||||
klqd VARCHAR(50), -- 抗拉强度
|
||||
ysl VARCHAR(50), -- 延伸率
|
||||
rq VARCHAR(50), -- 日期
|
||||
create_time TIMESTAMP NOT NULL,
|
||||
create_by VARCHAR(50) NOT NULL,
|
||||
update_time TIMESTAMP,
|
||||
update_by VARCHAR(50),
|
||||
is_deleted BOOLEAN DEFAULT FALSE,
|
||||
UNIQUE(luono, is_deleted) -- 确保炉号唯一
|
||||
);
|
||||
367
fix_status_management.py
Normal file
367
fix_status_management.py
Normal file
@ -0,0 +1,367 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
修复状态管理功能的脚本
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
# 1. 在InspectionDAO类中添加状态管理方法
|
||||
DAO_METHODS = """
|
||||
def get_product_status(self, order_id, gc_note, tray_id):
|
||||
\"\"\"获取产品的当前状态
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
gc_note: 工程号
|
||||
tray_id: 托盘号
|
||||
|
||||
Returns:
|
||||
str: 产品状态,如果没有找到则返回'init'
|
||||
\"\"\"
|
||||
try:
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
sql = \"\"\"
|
||||
SELECT status FROM wsbz_inspection_data
|
||||
WHERE order_id = ? AND gc_note = ? AND tray_id = ?
|
||||
ORDER BY id ASC LIMIT 1
|
||||
\"\"\"
|
||||
params = (order_id, gc_note, tray_id)
|
||||
result = db.query_one(sql, params)
|
||||
return result[0] if result and result[0] else 'init' # 默认为init状态
|
||||
except Exception as e:
|
||||
logging.error(f"获取产品状态失败: {str(e)}")
|
||||
return 'init' # 出错时返回默认状态
|
||||
|
||||
def update_product_status(self, order_id, gc_note, tray_id, new_status):
|
||||
\"\"\"更新产品的状态
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
gc_note: 工程号
|
||||
tray_id: 托盘号
|
||||
new_status: 新状态
|
||||
|
||||
Returns:
|
||||
bool: 更新是否成功
|
||||
\"\"\"
|
||||
try:
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
# 更新该产品所有记录的状态字段
|
||||
update_sql = \"\"\"
|
||||
UPDATE wsbz_inspection_data SET status = ?, update_time = ?
|
||||
WHERE order_id = ? AND gc_note = ? AND tray_id = ?
|
||||
\"\"\"
|
||||
update_params = (new_status, datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
order_id, gc_note, tray_id)
|
||||
db.execute(update_sql, update_params)
|
||||
logging.info(f"已更新产品状态: 订单号={order_id}, 工程号={gc_note}, 托盘号={tray_id}, 新状态={new_status}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error(f"更新产品状态失败: {str(e)}")
|
||||
return False
|
||||
"""
|
||||
|
||||
# 2. 检查检验完成的方法
|
||||
CHECK_INSPECTION_METHOD = """
|
||||
def check_inspection_completed(self, row):
|
||||
\"\"\"检查行是否有至少一个检验项已完成,如果是则更新状态为inspected
|
||||
|
||||
Args:
|
||||
row: 行索引
|
||||
|
||||
Returns:
|
||||
bool: 是否有至少一个检验项已完成
|
||||
\"\"\"
|
||||
try:
|
||||
# 获取工程号
|
||||
gc_note_item = self.process_table.item(row, 1)
|
||||
if not gc_note_item:
|
||||
return False
|
||||
|
||||
gc_note = gc_note_item.text().strip()
|
||||
tray_id = self.tray_edit.text()
|
||||
|
||||
# 获取启用的检验配置
|
||||
enabled_configs = self.inspection_manager.get_enabled_configs()
|
||||
|
||||
# 检查是否有至少一个检验项有值
|
||||
has_any_value = False
|
||||
for i, config in enumerate(enabled_configs):
|
||||
col_index = 2 + i
|
||||
item = self.process_table.item(row, col_index)
|
||||
if item and item.text().strip():
|
||||
has_any_value = True
|
||||
break
|
||||
|
||||
# 如果有至少一个检验项有值,更新状态为inspected
|
||||
if has_any_value:
|
||||
from dao.inspection_dao import InspectionDAO
|
||||
inspection_dao = InspectionDAO()
|
||||
inspection_dao.update_product_status(self._current_order_code, gc_note, tray_id, 'inspected')
|
||||
logging.info(f"工程号 {gc_note} 的检验已完成,状态更新为inspected")
|
||||
|
||||
return has_any_value
|
||||
except Exception as e:
|
||||
logging.error(f"检查检验完成状态失败: {str(e)}")
|
||||
return False
|
||||
"""
|
||||
|
||||
# 3. 修改的save_inspection_data方法
|
||||
SAVE_INSPECTION_DATA_METHOD = """
|
||||
def save_inspection_data(self, order_id, gc_note, tray_id, position, config_id, value, status):
|
||||
\"\"\"保存检验数据到数据库
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
gc_note: 工程号
|
||||
position: 位置序号
|
||||
config_id: 配置ID
|
||||
value: 检验值
|
||||
status: 状态
|
||||
\"\"\"
|
||||
try:
|
||||
from dao.inspection_dao import InspectionDAO
|
||||
inspection_dao = InspectionDAO()
|
||||
modbus = ModbusUtils()
|
||||
client = modbus.get_client()
|
||||
|
||||
# 获取当前产品状态,优先使用产品状态管理中的状态
|
||||
current_status = inspection_dao.get_product_status(order_id, gc_note, tray_id)
|
||||
|
||||
# 如果当前状态不是初始状态,则使用当前状态而不是传入的status
|
||||
if current_status not in ['', 'init']:
|
||||
status = current_status
|
||||
|
||||
# 记录保存前的详细日志
|
||||
logging.info(f"正在保存检验数据: 工程号={gc_note}, 托盘号={tray_id}, 位置={position}, 配置ID={config_id}, 值={value}, 状态={status}")
|
||||
|
||||
# 构建数据
|
||||
data = [{
|
||||
'position': position,
|
||||
'config_id': config_id,
|
||||
'value': value,
|
||||
'status': status,
|
||||
'remark': '',
|
||||
'tray_id': tray_id
|
||||
}]
|
||||
|
||||
# 保存到数据库
|
||||
inspection_dao.save_inspection_data(order_id, gc_note, data)
|
||||
"""
|
||||
|
||||
# 4. 修改_process_stable_weight方法中的查找行逻辑
|
||||
PROCESS_STABLE_WEIGHT_FIND_ROW = """
|
||||
# 基于状态查找行:优先查找状态为inspected的行
|
||||
data_row = None
|
||||
from dao.inspection_dao import InspectionDAO
|
||||
inspection_dao = InspectionDAO()
|
||||
|
||||
# 首先查找状态为inspected的行
|
||||
for row in range(2, self.process_table.rowCount()):
|
||||
gc_note_item = self.process_table.item(row, 1)
|
||||
if gc_note_item:
|
||||
row_gc_note = gc_note_item.text().strip()
|
||||
tray_id = self.tray_edit.text()
|
||||
status = inspection_dao.get_product_status(self._current_order_code, row_gc_note, tray_id)
|
||||
if status == 'inspected':
|
||||
data_row = row
|
||||
logging.info(f"找到状态为inspected的行: {data_row}, 工程号: {row_gc_note}")
|
||||
break
|
||||
|
||||
# 如果没有找到inspected状态的行,回退到原有逻辑
|
||||
if data_row is None:
|
||||
# 查找第一个没有称重数据的行
|
||||
for row in range(2, self.process_table.rowCount()):
|
||||
weight_item = self.process_table.item(row, weight_col)
|
||||
if not weight_item or not weight_item.text().strip():
|
||||
data_row = row
|
||||
break
|
||||
|
||||
# 如果仍然没有找到,使用当前选中行或第一个数据行
|
||||
if data_row is None:
|
||||
current_row = self.process_table.currentRow()
|
||||
data_row = current_row if current_row >= 2 else 2 # 使用第一个数据行(索引为2)
|
||||
logging.info(f"未找到状态为inspected的行或没有称重数据的行,使用当前选中行或第一个数据行: {data_row}")
|
||||
else:
|
||||
logging.info(f"找到没有称重数据的行: {data_row}")
|
||||
else:
|
||||
logging.info(f"将使用状态为inspected的行: {data_row}")
|
||||
"""
|
||||
|
||||
# 5. 添加称重完成后的状态更新代码
|
||||
PROCESS_STABLE_WEIGHT_UPDATE_STATUS = """
|
||||
# 更新产品状态为weighed
|
||||
inspection_dao.update_product_status(self._current_order_code, gc_note, tray_id, 'weighed')
|
||||
logging.info(f"工程号 {gc_note} 的称重已完成,状态更新为weighed")
|
||||
"""
|
||||
|
||||
# 6. 修改handle_label_signal方法中的查找行逻辑
|
||||
HANDLE_LABEL_SIGNAL_FIND_ROW = """
|
||||
# 基于状态查找行:优先查找状态为weighed的行
|
||||
data_row = None
|
||||
from dao.inspection_dao import InspectionDAO
|
||||
inspection_dao = InspectionDAO()
|
||||
|
||||
# 首先查找状态为weighed的行
|
||||
for row in range(2, self.process_table.rowCount()):
|
||||
gc_note_item = self.process_table.item(row, 1)
|
||||
if gc_note_item:
|
||||
row_gc_note = gc_note_item.text().strip()
|
||||
tray_id = self.tray_edit.text()
|
||||
status = inspection_dao.get_product_status(self._current_order_code, row_gc_note, tray_id)
|
||||
if status == 'weighed':
|
||||
data_row = row
|
||||
logging.info(f"找到状态为weighed的行: {data_row}, 工程号: {row_gc_note}")
|
||||
break
|
||||
|
||||
# 如果没有找到weighed状态的行,回退到原有逻辑
|
||||
if data_row is None:
|
||||
# 使用当前选中的行或第一个数据行
|
||||
current_row = self.process_table.currentRow()
|
||||
data_row = current_row if current_row >= 2 else 2 # 使用第一个数据行(索引为2)
|
||||
logging.info(f"未找到状态为weighed的行,使用当前选中行或第一个数据行: {data_row}")
|
||||
else:
|
||||
logging.info(f"将使用状态为weighed的行: {data_row}")
|
||||
"""
|
||||
|
||||
# 7. 添加贴标完成后的状态更新代码
|
||||
HANDLE_LABEL_SIGNAL_UPDATE_STATUS = """
|
||||
# 更新产品状态为labeled
|
||||
inspection_dao.update_product_status(self._current_order_code, gc_note, tray_id, 'labeled')
|
||||
logging.info(f"工程号 {gc_note} 的贴标已完成,状态更新为labeled")
|
||||
"""
|
||||
|
||||
# 8. 在handle_inspection_cell_changed方法末尾添加调用check_inspection_completed
|
||||
HANDLE_INSPECTION_CELL_CHANGED_CALL = """
|
||||
# 检查是否完成检验并更新状态
|
||||
self.check_inspection_completed(row)
|
||||
"""
|
||||
|
||||
# 9. 在add_new_inspection_row方法末尾添加初始化状态代码
|
||||
ADD_NEW_INSPECTION_ROW_INIT_STATUS = """
|
||||
# 初始化产品状态为init
|
||||
inspection_dao.update_product_status(self._current_order_code, gc_note, tray_id, 'init')
|
||||
logging.info(f"已添加工程号 {gc_note} 的新记录,显示在第{new_seq}条,初始状态为init")
|
||||
"""
|
||||
|
||||
# 10. 移除数据校验逻辑,替换为简单的单元格颜色设置
|
||||
REMOVE_VALIDATION_LOGIC = """
|
||||
# 设置单元格颜色为浅绿色,表示已填写
|
||||
cell_item.setBackground(QBrush(QColor("#c8e6c9")))
|
||||
"""
|
||||
|
||||
# 主函数:应用所有修改
|
||||
def apply_fixes():
|
||||
print("开始应用状态管理功能修复...")
|
||||
|
||||
# 备份文件
|
||||
os.system("cp dao/inspection_dao.py dao/inspection_dao.py.bak")
|
||||
os.system("cp widgets/main_window.py widgets/main_window.py.bak")
|
||||
|
||||
# 1. 添加DAO方法
|
||||
with open("dao/inspection_dao.py", "r") as f:
|
||||
dao_content = f.read()
|
||||
|
||||
# 检查方法是否已存在
|
||||
if "def get_product_status" not in dao_content:
|
||||
# 找到合适的位置插入新方法
|
||||
pattern = r'def save_package_record.*?return False'
|
||||
match = re.search(pattern, dao_content, re.DOTALL)
|
||||
if match:
|
||||
insert_pos = match.end()
|
||||
new_content = dao_content[:insert_pos] + DAO_METHODS + dao_content[insert_pos:]
|
||||
with open("dao/inspection_dao.py", "w") as f:
|
||||
f.write(new_content)
|
||||
print("1. 已成功添加状态管理方法到 dao/inspection_dao.py")
|
||||
else:
|
||||
print("无法找到合适的位置插入DAO方法")
|
||||
else:
|
||||
print("1. 状态管理方法已存在,跳过添加")
|
||||
|
||||
# 读取main_window.py
|
||||
with open("widgets/main_window.py", "r") as f:
|
||||
main_window_content = f.read()
|
||||
|
||||
# 2. 添加check_inspection_completed方法
|
||||
if "def check_inspection_completed" not in main_window_content:
|
||||
# 找到合适的位置插入新方法
|
||||
pattern = r'def validate_inspection_value.*?return False'
|
||||
match = re.search(pattern, main_window_content, re.DOTALL)
|
||||
if match:
|
||||
insert_pos = match.end()
|
||||
new_content = main_window_content[:insert_pos] + "\n" + CHECK_INSPECTION_METHOD + main_window_content[insert_pos:]
|
||||
main_window_content = new_content
|
||||
print("2. 已成功添加check_inspection_completed方法")
|
||||
else:
|
||||
print("无法找到合适的位置插入check_inspection_completed方法")
|
||||
else:
|
||||
print("2. check_inspection_completed方法已存在,跳过添加")
|
||||
|
||||
# 3. 修改save_inspection_data方法
|
||||
pattern = r'def save_inspection_data.*?inspection_dao\.save_inspection_data\(order_id, gc_note, data\)'
|
||||
replacement = SAVE_INSPECTION_DATA_METHOD
|
||||
main_window_content = re.sub(pattern, replacement, main_window_content, flags=re.DOTALL)
|
||||
print("3. 已成功修改save_inspection_data方法")
|
||||
|
||||
# 4. 修改_process_stable_weight方法中的查找行逻辑
|
||||
pattern = r'# 查找第一个没有称重数据的行\s*data_row = None\s*for row in range.*?if data_row is None:.*?else:\s*logging\.info\(f"找到没有称重数据的行: \{data_row\}"\)'
|
||||
replacement = PROCESS_STABLE_WEIGHT_FIND_ROW
|
||||
main_window_content = re.sub(pattern, replacement, main_window_content, flags=re.DOTALL)
|
||||
print("4. 已成功修改_process_stable_weight方法中的查找行逻辑")
|
||||
|
||||
# 5. 添加称重完成后的状态更新代码
|
||||
pattern = r'(logging\.info\(f"已将稳定的称重数据 \{weight_kg\}kg 写入行 \{data_row\}, 列 \{weight_col\}"\))\s*\n\s*except'
|
||||
replacement = r'\1\n\n' + PROCESS_STABLE_WEIGHT_UPDATE_STATUS + r'\n except'
|
||||
main_window_content = re.sub(pattern, replacement, main_window_content)
|
||||
print("5. 已成功添加称重完成后的状态更新代码")
|
||||
|
||||
# 6. 修改handle_label_signal方法中的查找行逻辑
|
||||
pattern = r'# 获取当前选中的行或第一个数据行\s*current_row = self\.process_table\.currentRow\(\)\s*data_row = current_row if current_row >= 2 else 2'
|
||||
replacement = HANDLE_LABEL_SIGNAL_FIND_ROW
|
||||
main_window_content = re.sub(pattern, replacement, main_window_content)
|
||||
print("6. 已成功修改handle_label_signal方法中的查找行逻辑")
|
||||
|
||||
# 7. 添加贴标完成后的状态更新代码
|
||||
pattern = r'(logging\.info\(f"已将贴标数据 \{axios_num\} 保存到数据库"\))\s*\n\s*# 调用加载到包装记录的方法'
|
||||
replacement = r'\1\n\n' + HANDLE_LABEL_SIGNAL_UPDATE_STATUS + r'\n \n # 调用加载到包装记录的方法'
|
||||
main_window_content = re.sub(pattern, replacement, main_window_content)
|
||||
print("7. 已成功添加贴标完成后的状态更新代码")
|
||||
|
||||
# 8. 在handle_inspection_cell_changed方法末尾添加调用check_inspection_completed
|
||||
pattern = r'(logging\.info\(f"处理单元格变更: 行=\{row\}, 列=\{column\}, 类型=\{data_type\}, 工程号=\{gc_note\}, 值=\{value\}, 状态=\{status\}"\))\s*\n\s*except'
|
||||
replacement = r'\1\n\n' + HANDLE_INSPECTION_CELL_CHANGED_CALL + r'\n except'
|
||||
main_window_content = re.sub(pattern, replacement, main_window_content)
|
||||
print("8. 已成功在handle_inspection_cell_changed方法末尾添加调用check_inspection_completed")
|
||||
|
||||
# 9. 修改add_new_inspection_row方法,设置初始状态
|
||||
# 9.1 修改检验项的status为init
|
||||
pattern = r"'status': '',\s*# 默认设置为通过状态"
|
||||
replacement = "'status': 'init', # 设置初始状态"
|
||||
main_window_content = re.sub(pattern, replacement, main_window_content)
|
||||
|
||||
# 9.2 修改贴标和称重项的status为init
|
||||
pattern = r"'status': 'pass',\s*# 默认设置为通过状态"
|
||||
replacement = "'status': 'init', # 设置初始状态"
|
||||
main_window_content = re.sub(pattern, replacement, main_window_content)
|
||||
|
||||
# 9.3 添加状态初始化代码
|
||||
pattern = r'(logging\.info\(f"已添加工程号 \{gc_note\} 的新记录,显示在第\{new_seq\}条"\))\s*\n\s*except'
|
||||
replacement = ADD_NEW_INSPECTION_ROW_INIT_STATUS + r'\n except'
|
||||
main_window_content = re.sub(pattern, replacement, main_window_content)
|
||||
print("9. 已成功修改add_new_inspection_row方法,设置初始状态")
|
||||
|
||||
# 10. 移除数据校验逻辑
|
||||
pattern = r'# 验证数据有效性\s*if self\.validate_inspection_value\(config, value\):.*?status = \'warning\''
|
||||
replacement = REMOVE_VALIDATION_LOGIC
|
||||
main_window_content = re.sub(pattern, replacement, main_window_content, flags=re.DOTALL)
|
||||
print("10. 已成功移除数据校验逻辑")
|
||||
|
||||
# 保存修改后的main_window.py
|
||||
with open("widgets/main_window.py", "w") as f:
|
||||
f.write(main_window_content)
|
||||
|
||||
print("状态管理功能修复完成!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
apply_fixes()
|
||||
@ -2,19 +2,28 @@ from pymodbus.client import ModbusTcpClient
|
||||
import time
|
||||
client = ModbusTcpClient('localhost', port=5020)
|
||||
client.connect()
|
||||
client.write_registers(address=11, values=[212])
|
||||
client.write_registers(address=11, values=[19200])
|
||||
# client.write_registers(address=3, values=[0])
|
||||
time.sleep(2)
|
||||
client.write_registers(address=0, values=[0])
|
||||
client.write_registers(address=4, values=[0])
|
||||
# time.sleep(2)
|
||||
# client.write_registers(address=0, values=[0])
|
||||
# client.write_registers(address=4, values=[0])
|
||||
|
||||
# client.write_registers(address=30, values=[25])
|
||||
# client.write_registers(address=5, values=[16])
|
||||
# 贴标完成
|
||||
# client.write_registers(address=24, values=[1])
|
||||
client.write_registers(address=13, values=[1])
|
||||
# client.write_registers(address=2, values=[0])
|
||||
client.write_registers(address=13, values=[0])
|
||||
# time.sleep(2)
|
||||
# client.write_registers(address=20, values=[0])
|
||||
# time.sleep(3)
|
||||
# client.write_registers(address=20, values=[1])
|
||||
# time.sleep(3)
|
||||
# client.write_registers(address=20, values=[0])
|
||||
|
||||
result1 = client.read_holding_registers(address=3, count=1)
|
||||
print(result1.registers[0],"123===")
|
||||
|
||||
result = client.read_holding_registers(address=0, count=1)
|
||||
print(result.registers[0],"123===")
|
||||
result2 = client.read_holding_registers(address=2, count=1)
|
||||
print(result2.registers[0],"123===")
|
||||
client.close()
|
||||
378
status_management_patch.py
Executable file
378
status_management_patch.py
Executable file
@ -0,0 +1,378 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
状态管理功能补丁文件
|
||||
用于修复产品状态管理相关的代码
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
# 定义要修改的文件
|
||||
DAO_FILE = "dao/inspection_dao.py"
|
||||
MAIN_WINDOW_FILE = "widgets/main_window.py"
|
||||
|
||||
# 1. 在InspectionDAO类中添加状态管理方法
|
||||
def add_dao_methods():
|
||||
with open(DAO_FILE, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# 检查方法是否已存在
|
||||
if "def get_product_status" in content:
|
||||
print("状态管理方法已存在,跳过添加")
|
||||
return
|
||||
|
||||
# 找到合适的位置插入新方法
|
||||
pattern = r'(def save_package_record.*?\n\s*return False\n)'
|
||||
|
||||
new_methods = r"""\1
|
||||
def get_product_status(self, order_id, gc_note, tray_id):
|
||||
"""获取产品的当前状态
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
gc_note: 工程号
|
||||
tray_id: 托盘号
|
||||
|
||||
Returns:
|
||||
str: 产品状态,如果没有找到则返回'init'
|
||||
"""
|
||||
try:
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
sql = """
|
||||
SELECT status FROM wsbz_inspection_data
|
||||
WHERE order_id = ? AND gc_note = ? AND tray_id = ?
|
||||
ORDER BY id ASC LIMIT 1
|
||||
"""
|
||||
params = (order_id, gc_note, tray_id)
|
||||
result = db.query_one(sql, params)
|
||||
return result[0] if result and result[0] else 'init' # 默认为init状态
|
||||
except Exception as e:
|
||||
logging.error(f"获取产品状态失败: {str(e)}")
|
||||
return 'init' # 出错时返回默认状态
|
||||
|
||||
def update_product_status(self, order_id, gc_note, tray_id, new_status):
|
||||
"""更新产品的状态
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
gc_note: 工程号
|
||||
tray_id: 托盘号
|
||||
new_status: 新状态
|
||||
|
||||
Returns:
|
||||
bool: 更新是否成功
|
||||
"""
|
||||
try:
|
||||
with SQLUtils('sqlite', database='db/jtDB.db') as db:
|
||||
# 更新该产品所有记录的状态字段
|
||||
update_sql = """
|
||||
UPDATE wsbz_inspection_data SET status = ?, update_time = ?
|
||||
WHERE order_id = ? AND gc_note = ? AND tray_id = ?
|
||||
"""
|
||||
update_params = (new_status, datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
order_id, gc_note, tray_id)
|
||||
db.execute(update_sql, update_params)
|
||||
logging.info(f"已更新产品状态: 订单号={order_id}, 工程号={gc_note}, 托盘号={tray_id}, 新状态={new_status}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logging.error(f"更新产品状态失败: {str(e)}")
|
||||
return False"""
|
||||
|
||||
modified_content = re.sub(pattern, new_methods, content, flags=re.DOTALL)
|
||||
|
||||
with open(DAO_FILE, "w") as f:
|
||||
f.write(modified_content)
|
||||
|
||||
print(f"已成功添加状态管理方法到 {DAO_FILE}")
|
||||
|
||||
# 2. 修改add_new_inspection_row方法,设置初始状态
|
||||
def update_add_new_inspection_row():
|
||||
with open(MAIN_WINDOW_FILE, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# 修改检验项的status为init
|
||||
pattern1 = r"'status': '',\s*# 默认设置为通过状态"
|
||||
replacement1 = "'status': 'init', # 设置初始状态"
|
||||
content = re.sub(pattern1, replacement1, content)
|
||||
|
||||
# 修改贴标和称重项的status为init
|
||||
pattern2 = r"'status': 'pass',\s*# 默认设置为通过状态"
|
||||
replacement2 = "'status': 'init', # 设置初始状态"
|
||||
content = re.sub(pattern2, replacement2, content)
|
||||
|
||||
# 添加状态初始化代码
|
||||
pattern3 = r"(logging\.info\(f\"已添加工程号 \{gc_note\} 的新记录,显示在第\{new_seq\}条\"\))"
|
||||
replacement3 = r"""# 初始化产品状态为init
|
||||
inspection_dao.update_product_status(self._current_order_code, gc_note, tray_id, 'init')
|
||||
logging.info(f"已添加工程号 {gc_note} 的新记录,显示在第{new_seq}条,初始状态为init")"""
|
||||
content = re.sub(pattern3, replacement3, content)
|
||||
|
||||
with open(MAIN_WINDOW_FILE, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"已成功更新add_new_inspection_row方法")
|
||||
|
||||
# 3. 添加check_inspection_completed方法
|
||||
def add_check_inspection_completed():
|
||||
with open(MAIN_WINDOW_FILE, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# 检查方法是否已存在
|
||||
if "def check_inspection_completed" in content:
|
||||
print("check_inspection_completed方法已存在,跳过添加")
|
||||
else:
|
||||
# 找到合适的位置插入新方法
|
||||
pattern = r'(def validate_inspection_value.*?)(\n\s*def )'
|
||||
|
||||
new_method = r"""\1
|
||||
|
||||
def check_inspection_completed(self, row):
|
||||
"""检查行是否有至少一个检验项已完成,如果是则更新状态为inspected
|
||||
|
||||
Args:
|
||||
row: 行索引
|
||||
|
||||
Returns:
|
||||
bool: 是否有至少一个检验项已完成
|
||||
"""
|
||||
try:
|
||||
# 获取工程号
|
||||
gc_note_item = self.process_table.item(row, 1)
|
||||
if not gc_note_item:
|
||||
return False
|
||||
|
||||
gc_note = gc_note_item.text().strip()
|
||||
tray_id = self.tray_edit.text()
|
||||
|
||||
# 获取启用的检验配置
|
||||
enabled_configs = self.inspection_manager.get_enabled_configs()
|
||||
|
||||
# 检查是否有至少一个检验项有值
|
||||
has_any_value = False
|
||||
for i, config in enumerate(enabled_configs):
|
||||
col_index = 2 + i
|
||||
item = self.process_table.item(row, col_index)
|
||||
if item and item.text().strip():
|
||||
has_any_value = True
|
||||
break
|
||||
|
||||
# 如果有至少一个检验项有值,更新状态为inspected
|
||||
if has_any_value:
|
||||
from dao.inspection_dao import InspectionDAO
|
||||
inspection_dao = InspectionDAO()
|
||||
inspection_dao.update_product_status(self._current_order_code, gc_note, tray_id, 'inspected')
|
||||
logging.info(f"工程号 {gc_note} 的检验已完成,状态更新为inspected")
|
||||
|
||||
return has_any_value
|
||||
except Exception as e:
|
||||
logging.error(f"检查检验完成状态失败: {str(e)}")
|
||||
return False\2"""
|
||||
|
||||
content = re.sub(pattern, new_method, content, flags=re.DOTALL)
|
||||
|
||||
with open(MAIN_WINDOW_FILE, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"已成功添加check_inspection_completed方法")
|
||||
|
||||
# 在handle_inspection_cell_changed方法末尾添加调用
|
||||
pattern = r'(logging\.info\(f"处理单元格变更: 行=\{row\}, 列=\{column\}, 类型=\{data_type\}, 工程号=\{gc_note\}, 值=\{value\}, 状态=\{status\}"\))\n(\s*except)'
|
||||
replacement = r"""\1
|
||||
|
||||
# 检查是否完成检验并更新状态
|
||||
self.check_inspection_completed(row)
|
||||
\2"""
|
||||
|
||||
content = re.sub(pattern, replacement, content)
|
||||
|
||||
with open(MAIN_WINDOW_FILE, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"已成功在handle_inspection_cell_changed方法末尾添加调用check_inspection_completed")
|
||||
|
||||
# 4. 修改_process_stable_weight方法
|
||||
def update_process_stable_weight():
|
||||
with open(MAIN_WINDOW_FILE, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# 修改查找行的逻辑
|
||||
pattern1 = r'# 查找第一个没有称重数据的行\s*data_row = None\s*for row in range.*?if data_row is None:.*?else:\s*logging\.info\(f"找到没有称重数据的行: \{data_row\}"\)'
|
||||
replacement1 = r"""# 基于状态查找行:优先查找状态为inspected的行
|
||||
data_row = None
|
||||
from dao.inspection_dao import InspectionDAO
|
||||
inspection_dao = InspectionDAO()
|
||||
|
||||
# 首先查找状态为inspected的行
|
||||
for row in range(2, self.process_table.rowCount()):
|
||||
gc_note_item = self.process_table.item(row, 1)
|
||||
if gc_note_item:
|
||||
row_gc_note = gc_note_item.text().strip()
|
||||
tray_id = self.tray_edit.text()
|
||||
status = inspection_dao.get_product_status(self._current_order_code, row_gc_note, tray_id)
|
||||
if status == 'inspected':
|
||||
data_row = row
|
||||
logging.info(f"找到状态为inspected的行: {data_row}, 工程号: {row_gc_note}")
|
||||
break
|
||||
|
||||
# 如果没有找到inspected状态的行,回退到原有逻辑
|
||||
if data_row is None:
|
||||
# 查找第一个没有称重数据的行
|
||||
for row in range(2, self.process_table.rowCount()):
|
||||
weight_item = self.process_table.item(row, weight_col)
|
||||
if not weight_item or not weight_item.text().strip():
|
||||
data_row = row
|
||||
break
|
||||
|
||||
# 如果仍然没有找到,使用当前选中行或第一个数据行
|
||||
if data_row is None:
|
||||
current_row = self.process_table.currentRow()
|
||||
data_row = current_row if current_row >= 2 else 2 # 使用第一个数据行(索引为2)
|
||||
logging.info(f"未找到状态为inspected的行或没有称重数据的行,使用当前选中行或第一个数据行: {data_row}")
|
||||
else:
|
||||
logging.info(f"找到没有称重数据的行: {data_row}")
|
||||
else:
|
||||
logging.info(f"将使用状态为inspected的行: {data_row}")"""
|
||||
|
||||
content = re.sub(pattern1, replacement1, content, flags=re.DOTALL)
|
||||
|
||||
# 添加状态更新代码
|
||||
pattern2 = r'(logging\.info\(f"已将稳定的称重数据 \{weight_kg\}kg 写入行 \{data_row\}, 列 \{weight_col\}"\))\n\s*except'
|
||||
replacement2 = r"""\1
|
||||
|
||||
# 更新产品状态为weighed
|
||||
inspection_dao.update_product_status(self._current_order_code, gc_note, tray_id, 'weighed')
|
||||
logging.info(f"工程号 {gc_note} 的称重已完成,状态更新为weighed")
|
||||
except"""
|
||||
|
||||
content = re.sub(pattern2, replacement2, content)
|
||||
|
||||
with open(MAIN_WINDOW_FILE, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"已成功更新_process_stable_weight方法")
|
||||
|
||||
# 5. 修改handle_label_signal方法
|
||||
def update_handle_label_signal():
|
||||
with open(MAIN_WINDOW_FILE, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# 修改查找行的逻辑
|
||||
pattern1 = r'# 获取当前选中的行或第一个数据行\s*current_row = self\.process_table\.currentRow\(\)\s*data_row = current_row if current_row >= 2 else 2'
|
||||
replacement1 = r"""# 基于状态查找行:优先查找状态为weighed的行
|
||||
data_row = None
|
||||
from dao.inspection_dao import InspectionDAO
|
||||
inspection_dao = InspectionDAO()
|
||||
|
||||
# 首先查找状态为weighed的行
|
||||
for row in range(2, self.process_table.rowCount()):
|
||||
gc_note_item = self.process_table.item(row, 1)
|
||||
if gc_note_item:
|
||||
row_gc_note = gc_note_item.text().strip()
|
||||
tray_id = self.tray_edit.text()
|
||||
status = inspection_dao.get_product_status(self._current_order_code, row_gc_note, tray_id)
|
||||
if status == 'weighed':
|
||||
data_row = row
|
||||
logging.info(f"找到状态为weighed的行: {data_row}, 工程号: {row_gc_note}")
|
||||
break
|
||||
|
||||
# 如果没有找到weighed状态的行,回退到原有逻辑
|
||||
if data_row is None:
|
||||
# 使用当前选中的行或第一个数据行
|
||||
current_row = self.process_table.currentRow()
|
||||
data_row = current_row if current_row >= 2 else 2 # 使用第一个数据行(索引为2)
|
||||
logging.info(f"未找到状态为weighed的行,使用当前选中行或第一个数据行: {data_row}")
|
||||
else:
|
||||
logging.info(f"将使用状态为weighed的行: {data_row}")"""
|
||||
|
||||
content = re.sub(pattern1, replacement1, content)
|
||||
|
||||
# 添加状态更新代码
|
||||
pattern2 = r'(logging\.info\(f"已将贴标数据 \{axios_num\} 保存到数据库"\))\n\s*# 调用加载到包装记录的方法'
|
||||
replacement2 = r"""\1
|
||||
|
||||
# 更新产品状态为labeled
|
||||
inspection_dao.update_product_status(self._current_order_code, gc_note, tray_id, 'labeled')
|
||||
logging.info(f"工程号 {gc_note} 的贴标已完成,状态更新为labeled")
|
||||
|
||||
# 调用加载到包装记录的方法"""
|
||||
|
||||
content = re.sub(pattern2, replacement2, content)
|
||||
|
||||
with open(MAIN_WINDOW_FILE, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"已成功更新handle_label_signal方法")
|
||||
|
||||
# 6. 修改save_inspection_data方法
|
||||
def update_save_inspection_data():
|
||||
with open(MAIN_WINDOW_FILE, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# 查找save_inspection_data方法
|
||||
pattern = r'def save_inspection_data.*?try:.*?inspection_dao = InspectionDAO\(\).*?# 记录保存前的详细日志'
|
||||
|
||||
# 修改方法,添加状态获取逻辑
|
||||
replacement = r"""def save_inspection_data(self, order_id, gc_note, tray_id, position, config_id, value, status):
|
||||
"""保存检验数据到数据库
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
gc_note: 工程号
|
||||
position: 位置序号
|
||||
config_id: 配置ID
|
||||
value: 检验值
|
||||
status: 状态
|
||||
"""
|
||||
try:
|
||||
from dao.inspection_dao import InspectionDAO
|
||||
inspection_dao = InspectionDAO()
|
||||
modbus = ModbusUtils()
|
||||
client = modbus.get_client()
|
||||
|
||||
# 获取当前产品状态,优先使用产品状态管理中的状态
|
||||
current_status = inspection_dao.get_product_status(order_id, gc_note, tray_id)
|
||||
|
||||
# 如果当前状态不是初始状态,则使用当前状态而不是传入的status
|
||||
if current_status not in ['', 'init']:
|
||||
status = current_status
|
||||
|
||||
# 记录保存前的详细日志"""
|
||||
|
||||
content = re.sub(pattern, replacement, content, flags=re.DOTALL)
|
||||
|
||||
with open(MAIN_WINDOW_FILE, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"已成功更新save_inspection_data方法")
|
||||
|
||||
# 7. 移除数据校验逻辑
|
||||
def remove_validation_logic():
|
||||
with open(MAIN_WINDOW_FILE, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# 第一处:移除handle_inspection_cell_changed中的数据校验逻辑
|
||||
pattern1 = r'# 验证数据有效性\s*if self\.validate_inspection_value\(config, value\):.*?status = \'warning\''
|
||||
replacement1 = r"""# 设置单元格颜色为浅绿色,表示已填写
|
||||
cell_item.setBackground(QBrush(QColor("#c8e6c9")))"""
|
||||
|
||||
content = re.sub(pattern1, replacement1, content, flags=re.DOTALL)
|
||||
|
||||
with open(MAIN_WINDOW_FILE, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
print(f"已成功移除数据校验逻辑")
|
||||
|
||||
# 执行所有修改
|
||||
def apply_all_changes():
|
||||
print("开始应用状态管理功能补丁...")
|
||||
add_dao_methods()
|
||||
update_add_new_inspection_row()
|
||||
add_check_inspection_completed()
|
||||
update_process_stable_weight()
|
||||
update_handle_label_signal()
|
||||
update_save_inspection_data()
|
||||
remove_validation_logic()
|
||||
print("状态管理功能补丁应用完成!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
apply_all_changes()
|
||||
7
temp_fix.py
Normal file
7
temp_fix.py
Normal file
@ -0,0 +1,7 @@
|
||||
# 设置单元格颜色为浅绿色,表示已填写
|
||||
cell_item.setBackground(QBrush(QColor("#c8e6c9")))
|
||||
|
||||
# 保持当前状态不变,由状态管理逻辑处理
|
||||
from dao.inspection_dao import InspectionDAO
|
||||
inspection_dao = InspectionDAO()
|
||||
status = inspection_dao.get_product_status(self._current_order_code, gc_note, tray_id)
|
||||
39
temp_save_inspection_data.py
Normal file
39
temp_save_inspection_data.py
Normal file
@ -0,0 +1,39 @@
|
||||
def save_inspection_data(self, order_id, gc_note, tray_id, position, config_id, value, status):
|
||||
"""保存检验数据到数据库
|
||||
|
||||
Args:
|
||||
order_id: 订单号
|
||||
gc_note: 工程号
|
||||
position: 位置序号
|
||||
config_id: 配置ID
|
||||
value: 检验值
|
||||
status: 状态
|
||||
"""
|
||||
try:
|
||||
from dao.inspection_dao import InspectionDAO
|
||||
inspection_dao = InspectionDAO()
|
||||
modbus = ModbusUtils()
|
||||
client = modbus.get_client()
|
||||
|
||||
# 获取当前产品状态,优先使用产品状态管理中的状态
|
||||
current_status = inspection_dao.get_product_status(order_id, gc_note, tray_id)
|
||||
|
||||
# 如果当前状态不是初始状态,则使用当前状态而不是传入的status
|
||||
if current_status not in ['', 'init']:
|
||||
status = current_status
|
||||
|
||||
# 记录保存前的详细日志
|
||||
logging.info(f"正在保存检验数据: 工程号={gc_note}, 托盘号={tray_id}, 位置={position}, 配置ID={config_id}, 值={value}, 状态={status}")
|
||||
|
||||
# 构建数据
|
||||
data = [{
|
||||
'position': position,
|
||||
'config_id': config_id,
|
||||
'value': value,
|
||||
'status': status,
|
||||
'remark': '',
|
||||
'tray_id': tray_id
|
||||
}]
|
||||
|
||||
# 保存到数据库
|
||||
inspection_dao.save_inspection_data(order_id, gc_note, data)
|
||||
1
test_register_monitor.py
Normal file
1
test_register_monitor.py
Normal file
@ -0,0 +1 @@
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,48 +0,0 @@
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
# 添加项目根目录到系统路径,以便导入模块
|
||||
project_root = str(Path(__file__).parent.parent)
|
||||
sys.path.insert(0, project_root)
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(name)s - [%(funcName)s:%(lineno)d] - %(message)s')
|
||||
|
||||
# 导入需要测试的模块
|
||||
from apis.gc_api import GcApi
|
||||
|
||||
def test_gc_api():
|
||||
"""测试 GcApi 的 get_gc_info 方法是否能正确处理 form-data 格式的请求"""
|
||||
print("开始测试 GcApi.get_gc_info 方法...")
|
||||
|
||||
# 创建 GcApi 实例
|
||||
gc_api = GcApi()
|
||||
|
||||
# 测试工程号
|
||||
test_gc_code = "JTPD25060003"
|
||||
|
||||
# 调用方法
|
||||
print(f"使用工程号 {test_gc_code} 调用 get_gc_info...")
|
||||
response = gc_api.get_gc_info(test_gc_code)
|
||||
|
||||
# 打印结果
|
||||
print(f"API 响应: {response}")
|
||||
|
||||
if response:
|
||||
print("测试成功: API 返回了有效响应")
|
||||
else:
|
||||
print("测试失败: API 返回了空响应")
|
||||
|
||||
# 检查响应格式
|
||||
if isinstance(response, dict) and "status" in response:
|
||||
print(f"响应状态: {response.get('status', False)}")
|
||||
print(f"响应消息: {response.get('message', '')}")
|
||||
print(f"响应数据: {response.get('data', None)}")
|
||||
else:
|
||||
print(f"响应格式不符合预期: {response}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_gc_api()
|
||||
@ -92,13 +92,44 @@ class LoadingDialogUI(QDialog):
|
||||
self.order_label.setFixedWidth(100)
|
||||
self.order_label.setFixedHeight(45)
|
||||
|
||||
# 创建一个水平布局,包含输入框和查询按钮
|
||||
order_input_layout = QHBoxLayout()
|
||||
order_input_layout.setSpacing(0)
|
||||
order_input_layout.setContentsMargins(0, 0, 0, 0)
|
||||
|
||||
self.order_input = QLineEdit()
|
||||
self.order_input.setFont(self.normal_font)
|
||||
self.order_input.setPlaceholderText("请扫描订单号")
|
||||
self.order_input.setStyleSheet(input_style)
|
||||
self.order_input.setFixedHeight(45)
|
||||
|
||||
# 添加查询按钮
|
||||
self.order_query_btn = QPushButton("...")
|
||||
self.order_query_btn.setFont(self.normal_font)
|
||||
self.order_query_btn.setFixedSize(45, 45)
|
||||
self.order_query_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
border: none;
|
||||
border-right: 1px solid #e0e0e0;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
background-color: #f5f5f5;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #d0d0d0;
|
||||
}
|
||||
""")
|
||||
|
||||
# 添加到水平布局
|
||||
order_input_layout.addWidget(self.order_input)
|
||||
order_input_layout.addWidget(self.order_query_btn)
|
||||
|
||||
row1.addWidget(self.order_label)
|
||||
row1.addWidget(self.order_input, 1)
|
||||
row1.addLayout(order_input_layout, 1)
|
||||
container_layout.addLayout(row1)
|
||||
|
||||
# 添加水平分隔布局
|
||||
|
||||
347
ui/luno_query_dialog_ui.py
Normal file
347
ui/luno_query_dialog_ui.py
Normal file
@ -0,0 +1,347 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog, QLabel, QLineEdit, QComboBox, QPushButton,
|
||||
QVBoxLayout, QHBoxLayout, QFrame, QTableWidget, QTableWidgetItem,
|
||||
QHeaderView, QDateEdit, QApplication
|
||||
)
|
||||
from PySide6.QtCore import Qt, QDate
|
||||
from PySide6.QtGui import QFont
|
||||
|
||||
class LunoQueryDialogUI(QDialog):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("炉号查询")
|
||||
self.resize(1200, 600) # 调整宽度和高度
|
||||
|
||||
# 设置字体
|
||||
self.normal_font = QFont("微软雅黑", 10)
|
||||
self.title_font = QFont("微软雅黑", 10, QFont.Bold)
|
||||
|
||||
# 初始化UI
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
"""初始化UI"""
|
||||
# 主布局
|
||||
self.main_layout = QVBoxLayout(self)
|
||||
self.main_layout.setContentsMargins(20, 20, 20, 20)
|
||||
self.main_layout.setSpacing(10)
|
||||
|
||||
# 创建查询条件区域
|
||||
self.create_query_frame()
|
||||
|
||||
# 创建结果表格
|
||||
self.create_result_table()
|
||||
|
||||
# 创建按钮
|
||||
self.create_buttons()
|
||||
|
||||
def create_query_frame(self):
|
||||
"""创建查询条件区域"""
|
||||
# 创建一个带边框的容器
|
||||
query_frame = QFrame()
|
||||
query_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
border: 1px solid #e0e0e0;
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
}
|
||||
""")
|
||||
|
||||
# 容器的垂直布局
|
||||
query_layout = QVBoxLayout(query_frame)
|
||||
query_layout.setContentsMargins(15, 15, 15, 15)
|
||||
query_layout.setSpacing(10)
|
||||
|
||||
# 第一行:日期范围和炉号
|
||||
row1 = QHBoxLayout()
|
||||
|
||||
# 日期标签
|
||||
date_label = QLabel("日期:")
|
||||
date_label.setFont(self.normal_font)
|
||||
date_label.setFixedWidth(40)
|
||||
|
||||
# 开始日期
|
||||
self.start_date = QDateEdit()
|
||||
self.start_date.setFont(self.normal_font)
|
||||
self.start_date.setCalendarPopup(True)
|
||||
self.start_date.setDate(QDate.currentDate().addDays(-7))
|
||||
self.start_date.setFixedWidth(120)
|
||||
self.start_date.setStyleSheet("""
|
||||
QDateEdit {
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
""")
|
||||
|
||||
# 至标签
|
||||
to_label = QLabel("至:")
|
||||
to_label.setFont(self.normal_font)
|
||||
to_label.setFixedWidth(20)
|
||||
|
||||
# 结束日期
|
||||
self.end_date = QDateEdit()
|
||||
self.end_date.setFont(self.normal_font)
|
||||
self.end_date.setCalendarPopup(True)
|
||||
self.end_date.setDate(QDate.currentDate())
|
||||
self.end_date.setFixedWidth(120)
|
||||
self.end_date.setStyleSheet("""
|
||||
QDateEdit {
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
""")
|
||||
|
||||
# 炉号标签
|
||||
luno_label = QLabel("炉号:")
|
||||
luno_label.setFont(self.normal_font)
|
||||
luno_label.setFixedWidth(50)
|
||||
|
||||
# 炉号输入框
|
||||
self.luno_input = QLineEdit()
|
||||
self.luno_input.setFont(self.normal_font)
|
||||
self.luno_input.setStyleSheet("""
|
||||
QLineEdit {
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border: 1px solid #66afe9;
|
||||
}
|
||||
""")
|
||||
|
||||
# 材质标签
|
||||
material_label = QLabel("材质")
|
||||
material_label.setFont(self.normal_font)
|
||||
material_label.setFixedWidth(40)
|
||||
|
||||
# 材质下拉框
|
||||
self.material_combo = QComboBox()
|
||||
self.material_combo.setFont(self.normal_font)
|
||||
self.material_combo.addItem("全部")
|
||||
self.material_combo.setStyleSheet("""
|
||||
QComboBox {
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QComboBox::drop-down {
|
||||
subcontrol-origin: padding;
|
||||
subcontrol-position: top right;
|
||||
width: 20px;
|
||||
border-left: 1px solid #e0e0e0;
|
||||
}
|
||||
""")
|
||||
|
||||
# 规格标签
|
||||
spec_label = QLabel("规格")
|
||||
spec_label.setFont(self.normal_font)
|
||||
spec_label.setFixedWidth(40)
|
||||
|
||||
# 规格输入框
|
||||
self.spec_input = QLineEdit()
|
||||
self.spec_input.setFont(self.normal_font)
|
||||
self.spec_input.setStyleSheet("""
|
||||
QLineEdit {
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border: 1px solid #66afe9;
|
||||
}
|
||||
""")
|
||||
|
||||
# 钢厂标签
|
||||
steel_label = QLabel("钢厂")
|
||||
steel_label.setFont(self.normal_font)
|
||||
steel_label.setFixedWidth(40)
|
||||
|
||||
# 钢厂输入框
|
||||
self.steel_input = QLineEdit()
|
||||
self.steel_input.setFont(self.normal_font)
|
||||
self.steel_input.setStyleSheet("""
|
||||
QLineEdit {
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border: 1px solid #66afe9;
|
||||
}
|
||||
""")
|
||||
|
||||
# 查询按钮
|
||||
self.query_button = QPushButton("查询")
|
||||
self.query_button.setFont(self.normal_font)
|
||||
self.query_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #0078d4;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 6px 16px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #106ebe;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
self.query_button.setFixedWidth(80)
|
||||
|
||||
# 添加组件到第一行布局
|
||||
row1.addWidget(date_label)
|
||||
row1.addWidget(self.start_date)
|
||||
row1.addWidget(to_label)
|
||||
row1.addWidget(self.end_date)
|
||||
row1.addSpacing(20)
|
||||
row1.addWidget(luno_label)
|
||||
row1.addWidget(self.luno_input, 1)
|
||||
row1.addSpacing(20)
|
||||
row1.addWidget(material_label)
|
||||
row1.addWidget(self.material_combo, 1)
|
||||
row1.addSpacing(20)
|
||||
row1.addWidget(spec_label)
|
||||
row1.addWidget(self.spec_input, 1)
|
||||
row1.addSpacing(20)
|
||||
row1.addWidget(steel_label)
|
||||
row1.addWidget(self.steel_input, 1)
|
||||
row1.addSpacing(20)
|
||||
row1.addWidget(self.query_button)
|
||||
|
||||
# 添加第一行到查询布局
|
||||
query_layout.addLayout(row1)
|
||||
|
||||
# 将查询框架添加到主布局
|
||||
self.main_layout.addWidget(query_frame)
|
||||
|
||||
def create_result_table(self):
|
||||
"""创建结果表格"""
|
||||
# 创建表格
|
||||
self.result_table = QTableWidget()
|
||||
self.result_table.setFont(self.normal_font)
|
||||
|
||||
# 设置表头样式
|
||||
header = self.result_table.horizontalHeader()
|
||||
header.setStyleSheet("""
|
||||
QHeaderView::section {
|
||||
background-color: #f5f5f5;
|
||||
color: #333333;
|
||||
padding: 5px;
|
||||
border: 1px solid #e0e0e0;
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
|
||||
# 设置表格样式
|
||||
self.result_table.setStyleSheet("""
|
||||
QTableWidget {
|
||||
border: 1px solid #e0e0e0;
|
||||
gridline-color: #e0e0e0;
|
||||
selection-background-color: #0078d4;
|
||||
selection-color: white;
|
||||
}
|
||||
QTableWidget::item {
|
||||
padding: 5px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
""")
|
||||
|
||||
# 启用水平滚动条
|
||||
self.result_table.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||||
|
||||
# 设置表头固定宽度模式,确保所有列都能显示
|
||||
header.setSectionResizeMode(QHeaderView.Fixed)
|
||||
|
||||
# 设置表格最小宽度,确保有足够空间显示滚动条
|
||||
self.result_table.setMinimumWidth(800)
|
||||
|
||||
# 设置默认列宽
|
||||
self.result_table.setColumnWidth(0, 60) # 序号
|
||||
self.result_table.setColumnWidth(1, 100) # 日期
|
||||
self.result_table.setColumnWidth(2, 120) # 炉号
|
||||
self.result_table.setColumnWidth(3, 100) # 材质
|
||||
self.result_table.setColumnWidth(4, 100) # 规格
|
||||
self.result_table.setColumnWidth(5, 100) # 钢厂
|
||||
self.result_table.setColumnWidth(6, 80) # 标准
|
||||
self.result_table.setColumnWidth(7, 100) # 公司账套
|
||||
self.result_table.setColumnWidth(8, 60) # C
|
||||
self.result_table.setColumnWidth(9, 60) # Si
|
||||
self.result_table.setColumnWidth(10, 60) # Mn
|
||||
self.result_table.setColumnWidth(11, 60) # P
|
||||
self.result_table.setColumnWidth(12, 60) # S
|
||||
self.result_table.setColumnWidth(13, 60) # Ni
|
||||
self.result_table.setColumnWidth(14, 60) # Cr
|
||||
self.result_table.setColumnWidth(15, 60) # Ti
|
||||
self.result_table.setColumnWidth(16, 60) # Mo
|
||||
self.result_table.setColumnWidth(17, 60) # Cu
|
||||
self.result_table.setColumnWidth(18, 80) # 其他
|
||||
self.result_table.setColumnWidth(19, 100) # 抗拉强度
|
||||
self.result_table.setColumnWidth(20, 80) # 延伸率
|
||||
|
||||
# 设置表格可以选择整行
|
||||
self.result_table.setSelectionBehavior(QTableWidget.SelectRows)
|
||||
|
||||
# 设置表格只能单选
|
||||
self.result_table.setSelectionMode(QTableWidget.SingleSelection)
|
||||
|
||||
# 添加表格到主布局
|
||||
self.main_layout.addWidget(self.result_table, 1) # 1表示拉伸因子,让表格占据更多空间
|
||||
|
||||
def create_buttons(self):
|
||||
"""创建底部按钮"""
|
||||
button_layout = QHBoxLayout()
|
||||
|
||||
# 确认和取消按钮
|
||||
self.confirm_button = QPushButton("确认")
|
||||
self.confirm_button.setFont(self.normal_font)
|
||||
self.confirm_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #0078d4;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #106ebe;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
self.confirm_button.setFixedSize(100, 35)
|
||||
|
||||
self.cancel_button = QPushButton("取消")
|
||||
self.cancel_button.setFont(self.normal_font)
|
||||
self.cancel_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: white;
|
||||
color: #333333;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
""")
|
||||
self.cancel_button.setFixedSize(100, 35)
|
||||
|
||||
# 添加按钮到布局
|
||||
button_layout.addStretch()
|
||||
button_layout.addWidget(self.confirm_button)
|
||||
button_layout.addSpacing(30) # 添加30px的间距
|
||||
button_layout.addWidget(self.cancel_button)
|
||||
|
||||
# 添加按钮布局到主布局
|
||||
self.main_layout.addLayout(button_layout)
|
||||
@ -1,7 +1,7 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QMainWindow, QWidget, QLabel, QGridLayout, QVBoxLayout, QHBoxLayout,
|
||||
QTableWidget, QTableWidgetItem, QHeaderView, QFrame, QSplitter,
|
||||
QPushButton, QLineEdit, QAbstractItemView, QComboBox, QSizePolicy
|
||||
QPushButton, QLineEdit, QAbstractItemView, QComboBox, QSizePolicy, QTextEdit, QCheckBox
|
||||
)
|
||||
from PySide6.QtGui import QFont, QAction, QBrush, QColor
|
||||
from PySide6.QtCore import Qt, QDateTime, QTimer
|
||||
@ -9,7 +9,10 @@ from PySide6.QtCore import Qt, QDateTime, QTimer
|
||||
class MainWindowUI(QMainWindow):
|
||||
# 定义字段映射为类属性,方便外部引用
|
||||
FIELD_MAPPING = {
|
||||
"库房": "lib",
|
||||
"机台": "jt",
|
||||
"客户": "customerexp",
|
||||
"订单数量": "sl",
|
||||
"规格": "size",
|
||||
"材质": "cz",
|
||||
"种类": "type_name",
|
||||
@ -17,14 +20,15 @@ class MainWindowUI(QMainWindow):
|
||||
"炉号": "luno",
|
||||
"轴型": "zx_name",
|
||||
"标签": "template_name",
|
||||
"打印材质": "cz",
|
||||
"打印材质": "dycz",
|
||||
"底托类型": "spack_type",
|
||||
"强度范围": "qx",
|
||||
"强度": "qd",
|
||||
"延伸要求": "ysl",
|
||||
"线材类型": "jz",
|
||||
"线材类型": "xclx",
|
||||
"包装方式": "bzfs",
|
||||
"轴重要求": "zzyq",
|
||||
"线径公差": "xj",
|
||||
"线径公差": "xjgc",
|
||||
"备注": "remarks_hb"
|
||||
}
|
||||
|
||||
@ -183,6 +187,35 @@ class MainWindowUI(QMainWindow):
|
||||
|
||||
self.order_layout.addWidget(self.order_label)
|
||||
self.order_layout.addWidget(self.order_edit)
|
||||
|
||||
# 添加虚拟工程号按钮
|
||||
self.virtual_order_button = QPushButton("虚拟工程号")
|
||||
self.virtual_order_button.setFixedHeight(30)
|
||||
self.virtual_order_button.setFixedWidth(100)
|
||||
self.virtual_order_button.setFont(QFont("微软雅黑", 10))
|
||||
self.virtual_order_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #e3f2fd;
|
||||
border: 1px solid #2196f3;
|
||||
border-radius: 3px;
|
||||
padding: 2px 5px;
|
||||
color: #1976d2;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #bbdefb;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #90caf9;
|
||||
}
|
||||
""")
|
||||
self.order_layout.addWidget(self.virtual_order_button)
|
||||
|
||||
# 添加圆形标签checkbox
|
||||
self.round_label_checkbox = QCheckBox("圆形标签")
|
||||
self.round_label_checkbox.setFont(QFont("微软雅黑", 10))
|
||||
self.round_label_checkbox.setFixedHeight(30)
|
||||
self.order_layout.addWidget(self.round_label_checkbox)
|
||||
|
||||
self.order_layout.addStretch() # 添加弹性空间,将组件推到左侧
|
||||
self.task_layout.addLayout(self.order_layout)
|
||||
|
||||
@ -196,22 +229,22 @@ class MainWindowUI(QMainWindow):
|
||||
# 设置列宽均等
|
||||
self.task_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
|
||||
# 第一行:订单量和完成量 (一级标题)
|
||||
self.task_table.setSpan(0, 0, 1, 2) # 订单量跨2列
|
||||
self.task_table.setSpan(0, 2, 1, 2) # 完成量跨2列
|
||||
# 第一行:总完成和已完成 (一级标题)
|
||||
self.task_table.setSpan(0, 0, 1, 2) # 总完成跨2列
|
||||
self.task_table.setSpan(0, 2, 1, 2) # 已完成跨2列
|
||||
|
||||
order_item = QTableWidgetItem("订单量")
|
||||
order_item = QTableWidgetItem("订单完成")
|
||||
order_item.setTextAlignment(Qt.AlignCenter)
|
||||
order_item.setFont(self.normal_font)
|
||||
self.task_table.setItem(0, 0, order_item)
|
||||
|
||||
completed_item = QTableWidgetItem("完成量")
|
||||
completed_item = QTableWidgetItem("托盘完成")
|
||||
completed_item.setTextAlignment(Qt.AlignCenter)
|
||||
completed_item.setFont(self.normal_font)
|
||||
self.task_table.setItem(0, 2, completed_item)
|
||||
|
||||
# 第二行:二级标题
|
||||
headers = ["总生产数量", "总生产公斤", "已完成数量", "已完成公斤"]
|
||||
headers = ["订单完成轴数", "订单完成数量", "托盘完成轴数", "托盘完成数量"]
|
||||
for col, header in enumerate(headers):
|
||||
item = QTableWidgetItem(header)
|
||||
item.setTextAlignment(Qt.AlignCenter)
|
||||
@ -251,6 +284,8 @@ class MainWindowUI(QMainWindow):
|
||||
self.material_content_layout = QVBoxLayout(self.material_content)
|
||||
self.material_content_layout.setContentsMargins(10, 10, 10, 10)
|
||||
|
||||
# 移除滚动区域,直接使用表格
|
||||
|
||||
# 创建订单号输入框
|
||||
self.order_info_layout = QHBoxLayout()
|
||||
self.order_no_label = QLabel("订单号:")
|
||||
@ -285,7 +320,9 @@ class MainWindowUI(QMainWindow):
|
||||
|
||||
# 创建信息表格 - 使用QTableWidget实现
|
||||
self.info_table = QTableWidget()
|
||||
self.info_table.setRowCount(9) # 8行常规字段 + 1行备注
|
||||
# 计算需要的行数:21个字段,每行2个字段,最后两行是备注(跨列)
|
||||
# 前20个字段需要10行,备注占用2行,总共12行
|
||||
self.info_table.setRowCount(12) # 10行常规字段 + 2行备注
|
||||
self.info_table.setColumnCount(4) # 4列:标签1, 值1, 标签2, 值2
|
||||
self.info_table.setShowGrid(True) # 显示网格线
|
||||
self.info_table.horizontalHeader().setVisible(False) # 隐藏水平表头
|
||||
@ -320,20 +357,28 @@ class MainWindowUI(QMainWindow):
|
||||
label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
||||
label.setStyleSheet("background-color: #FAFAFA; padding: 5px;")
|
||||
|
||||
# 创建值
|
||||
value = QLabel("")
|
||||
# 创建值(改为QTextEdit)
|
||||
value = QTextEdit("")
|
||||
value.setFont(self.normal_font)
|
||||
value.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
||||
value.setStyleSheet("background-color: white; padding: 5px;")
|
||||
value.setStyleSheet("background-color: white; padding: 5px; border: 1px solid #cccccc;")
|
||||
value.setFixedHeight(70) # 增加高度为两行
|
||||
value.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
value.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
value.setFrameStyle(QFrame.NoFrame)
|
||||
value.setLineWrapMode(QTextEdit.WidgetWidth) # 允许自动换行
|
||||
value.setContentsMargins(0, 0, 0, 0)
|
||||
value.setAcceptRichText(False)
|
||||
value.setTabChangesFocus(True)
|
||||
|
||||
# 保存引用
|
||||
self.info_labels["备注"] = label
|
||||
self.info_values["备注"] = value
|
||||
|
||||
# 添加到表格
|
||||
# 添加到表格,占用两行
|
||||
self.info_table.setCellWidget(row, 0, label)
|
||||
self.info_table.setCellWidget(row, 1, value)
|
||||
self.info_table.setSpan(row, 1, 1, 3) # 值跨越3列
|
||||
self.info_table.setSpan(row, 0, 2, 1) # 标签跨越2行1列
|
||||
self.info_table.setSpan(row, 1, 2, 3) # 值跨越2行3列
|
||||
else:
|
||||
# 创建标签
|
||||
label = QLabel(field_name)
|
||||
@ -341,11 +386,113 @@ class MainWindowUI(QMainWindow):
|
||||
label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
||||
label.setStyleSheet("background-color: #FAFAFA; padding: 5px;")
|
||||
|
||||
# 创建值
|
||||
value = QLabel("")
|
||||
value.setFont(self.normal_font)
|
||||
value.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
||||
value.setStyleSheet("background-color: white; padding: 5px;")
|
||||
# 根据字段类型创建不同的值组件
|
||||
if field_name == "库房":
|
||||
# 库房字段使用QComboBox,完全复刻托盘号组件的配置
|
||||
value = QComboBox()
|
||||
value.setFixedHeight(35)
|
||||
value.setStyleSheet("QComboBox { border: 1px solid #cccccc; padding: 3px; background-color: white; } QComboBox::drop-down { border: none; width: 20px; }")
|
||||
value.setFont(QFont("微软雅黑", 12))
|
||||
value.setEditable(False) # 设置为不可编辑,确保是纯下拉选择
|
||||
value.setInsertPolicy(QComboBox.NoInsert) # 不自动插入用户输入到列表中
|
||||
value.setMaxVisibleItems(10) # 设置下拉框最多显示10个项目
|
||||
# 添加默认选项
|
||||
value.addItem("成品库")
|
||||
value.addItem("退回仓")
|
||||
value.addItem("散装库")
|
||||
value.addItem("不合格库(线材)")
|
||||
value.addItem("废丝库")
|
||||
value.setCurrentIndex(0) # 默认选择第一个
|
||||
elif field_name == "机台":
|
||||
# 机台字段使用QLineEdit,与其他字段保持一致
|
||||
value = QLineEdit("")
|
||||
value.setFont(self.normal_font)
|
||||
value.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
||||
value.setStyleSheet("background-color: white; padding: 5px; border: 1px solid #cccccc;")
|
||||
value.setFrame(False)
|
||||
value.setContentsMargins(0, 0, 0, 0)
|
||||
value.setFixedHeight(35)
|
||||
elif field_name == "订单数量":
|
||||
# 订单数量字段使用QLineEdit,与其他字段保持一致
|
||||
value = QLineEdit("")
|
||||
value.setFont(self.normal_font)
|
||||
value.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
||||
value.setStyleSheet("background-color: white; padding: 5px; border: 1px solid #cccccc;")
|
||||
value.setFrame(False)
|
||||
value.setContentsMargins(0, 0, 0, 0)
|
||||
value.setFixedHeight(35)
|
||||
elif field_name == "线材类型":
|
||||
# 线材类型字段使用QComboBox,与库房组件配置相同
|
||||
value = QComboBox()
|
||||
value.setFixedHeight(35)
|
||||
value.setStyleSheet("QComboBox { border: 1px solid #cccccc; padding: 3px; background-color: white; } QComboBox::drop-down { border: none; width: 20px; }")
|
||||
value.setFont(QFont("微软雅黑", 12))
|
||||
value.setEditable(False) # 设置为不可编辑,确保是纯下拉选择
|
||||
value.setInsertPolicy(QComboBox.NoInsert) # 不自动插入用户输入到列表中
|
||||
value.setMaxVisibleItems(10) # 设置下拉框最多显示10个项目
|
||||
# 添加默认选项(后续会从API动态加载)
|
||||
value.addItem("请选择")
|
||||
value.setCurrentIndex(0) # 默认选择第一个
|
||||
elif field_name == "炉号":
|
||||
# 炉号字段使用QLineEdit + 查询按钮,复刻订单号的逻辑
|
||||
|
||||
# 创建水平布局容器
|
||||
value_container = QWidget()
|
||||
value_layout = QHBoxLayout(value_container)
|
||||
value_layout.setContentsMargins(0, 0, 0, 0)
|
||||
value_layout.setSpacing(0)
|
||||
|
||||
# 创建输入框
|
||||
value = QLineEdit("")
|
||||
value.setFont(self.normal_font)
|
||||
value.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
||||
value.setStyleSheet("background-color: white; padding: 5px; border: 1px solid #cccccc; border-right: none;")
|
||||
value.setFixedHeight(35)
|
||||
|
||||
# 创建查询按钮
|
||||
query_button = QPushButton("...")
|
||||
query_button.setFont(self.normal_font)
|
||||
query_button.setFixedSize(35, 35)
|
||||
query_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
border: 1px solid #cccccc;
|
||||
border-left: none;
|
||||
background-color: #f5f5f5;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #d0d0d0;
|
||||
}
|
||||
""")
|
||||
|
||||
# 添加到布局
|
||||
value_layout.addWidget(value)
|
||||
value_layout.addWidget(query_button)
|
||||
|
||||
# 保存按钮引用,以便后续连接信号
|
||||
self.luno_query_button = query_button
|
||||
|
||||
# 将容器作为值组件
|
||||
value = value_container
|
||||
elif field_name == "包装方式":
|
||||
# 包装方式字段使用QLineEdit,与其他字段保持一致
|
||||
value = QLineEdit("")
|
||||
value.setFont(self.normal_font)
|
||||
value.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
||||
value.setStyleSheet("background-color: white; padding: 5px; border: 1px solid #cccccc;")
|
||||
else:
|
||||
# 其他字段使用QLineEdit
|
||||
value = QLineEdit("")
|
||||
value.setFont(self.normal_font)
|
||||
value.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
|
||||
value.setStyleSheet("background-color: white; padding: 5px; border: 1px solid #cccccc;")
|
||||
value.setFrame(False)
|
||||
value.setContentsMargins(0, 0, 0, 0)
|
||||
value.setFixedHeight(35)
|
||||
|
||||
# 保存引用
|
||||
self.info_labels[field_name] = label
|
||||
@ -363,19 +510,21 @@ class MainWindowUI(QMainWindow):
|
||||
|
||||
# 设置行高
|
||||
for i in range(self.info_table.rowCount()):
|
||||
self.info_table.setRowHeight(i, 35) # 普通行高度调高
|
||||
if i == 10: # 备注行(第11行,索引为10)
|
||||
self.info_table.setRowHeight(i, 35) # 备注行第一行高度
|
||||
self.info_table.setRowHeight(i + 1, 35) # 备注行第二行高度
|
||||
break
|
||||
else:
|
||||
self.info_table.setRowHeight(i, 35) # 普通行高度
|
||||
|
||||
# 设置表格自适应
|
||||
self.info_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # 列宽自适应
|
||||
# 设置表格自适应宽度
|
||||
self.info_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
|
||||
# 设置表格填充整个容器
|
||||
self.info_table.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
|
||||
# 禁用滚动条,避免用户误触
|
||||
self.info_table.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
self.info_table.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
|
||||
# 添加表格到主布局
|
||||
# 直接添加表格到主布局
|
||||
self.material_content_layout.addWidget(self.info_table)
|
||||
|
||||
self.material_layout.addWidget(self.material_content)
|
||||
@ -399,47 +548,82 @@ class MainWindowUI(QMainWindow):
|
||||
self.tray_label.setFixedWidth(100) # 设置固定宽度
|
||||
self.tray_label.setStyleSheet("background-color: #e0e0e0; border-right: 1px solid #cccccc; font-weight: bold;")
|
||||
self.tray_layout.addWidget(self.tray_label)
|
||||
|
||||
self.tray_edit = QComboBox()
|
||||
self.tray_edit.setFixedHeight(40) # 设置固定高度与父容器相同
|
||||
self.tray_edit.setStyleSheet("QComboBox { border: none; padding: 0px 10px; background-color: white; } QComboBox::drop-down { border: none; width: 20px; }")
|
||||
self.tray_edit.setFont(QFont("微软雅黑", 12))
|
||||
self.tray_edit.setEditable(True) # 允许手动输入
|
||||
self.tray_edit.setInsertPolicy(QComboBox.NoInsert) # 不自动插入用户输入到列表中
|
||||
self.tray_edit.setMaxVisibleItems(10) # 设置下拉框最多显示10个项目
|
||||
self.tray_edit.completer().setCaseSensitivity(Qt.CaseInsensitive) # 设置补全不区分大小写
|
||||
self.tray_edit.completer().setFilterMode(Qt.MatchContains) # 设置模糊匹配模式
|
||||
# 允许清空选择
|
||||
self.tray_edit.setCurrentText("")
|
||||
|
||||
# 将 QComboBox 改为 QLineEdit
|
||||
self.tray_edit = QLineEdit()
|
||||
self.tray_edit.setFixedHeight(40) # 设置固定高度与父容器相同
|
||||
self.tray_edit.setStyleSheet("QLineEdit { border: none; padding: 0px 10px; background-color: white; }")
|
||||
self.tray_edit.setFont(QFont("微软雅黑", 12))
|
||||
self.tray_edit.setReadOnly(True) # 设置为只读
|
||||
self.tray_edit.setFocusPolicy(Qt.NoFocus) # 禁止获取焦点
|
||||
self.tray_edit.setText("") # 设置初始值为空
|
||||
self.tray_layout.addWidget(self.tray_edit)
|
||||
|
||||
self.left_layout.addWidget(self.tray_frame)
|
||||
|
||||
# 下料区 - 使用QFrame包裹,添加边框
|
||||
# 上下料区 - 使用QFrame包裹,添加边框
|
||||
self.output_frame = QFrame()
|
||||
self.output_frame.setFrameShape(QFrame.StyledPanel)
|
||||
self.output_frame.setLineWidth(1)
|
||||
self.output_frame.setFixedHeight(100) # 压缩下料区域的高度,从原来的150减少到100
|
||||
self.output_frame.setFixedHeight(100) # 保持高度不变
|
||||
self.output_frame.setStyleSheet("QFrame { background-color: #f8f8f8; }")
|
||||
self.output_layout = QHBoxLayout(self.output_frame)
|
||||
self.output_layout.setContentsMargins(0, 0, 0, 0)
|
||||
self.output_layout.setSpacing(0)
|
||||
|
||||
# 下料区标签
|
||||
self.output_label = QLabel("下料")
|
||||
self.output_label.setFont(self.normal_font)
|
||||
self.output_label.setAlignment(Qt.AlignCenter)
|
||||
self.output_label.setFixedWidth(100) # 设置固定宽度
|
||||
self.output_label.setStyleSheet("background-color: #e0e0e0; border-right: 1px solid #cccccc; font-weight: bold;")
|
||||
self.output_layout.addWidget(self.output_label)
|
||||
# 上料区(拆垛层数)
|
||||
self.input_area = QWidget()
|
||||
self.input_area.setFixedWidth(200) # 设置固定宽度
|
||||
self.input_area.setStyleSheet("background-color: white;")
|
||||
self.input_area_layout = QVBoxLayout(self.input_area)
|
||||
self.input_area_layout.setContentsMargins(5, 5, 5, 5)
|
||||
self.input_area_layout.setSpacing(5)
|
||||
|
||||
# 下料区内容 - 这里可以添加更多控件
|
||||
self.output_content = QWidget()
|
||||
self.output_content.setStyleSheet("background-color: white;")
|
||||
self.output_content_layout = QVBoxLayout(self.output_content)
|
||||
self.output_content_layout.setContentsMargins(5, 5, 5, 5) # 减小内部边距
|
||||
self.output_layout.addWidget(self.output_content)
|
||||
# 上料区标签
|
||||
self.input_area_label = QLabel("上料区")
|
||||
self.input_area_label.setFont(self.normal_font)
|
||||
self.input_area_label.setAlignment(Qt.AlignCenter)
|
||||
self.input_area_label.setStyleSheet("font-weight: bold; color: #333333;")
|
||||
self.input_area_layout.addWidget(self.input_area_label)
|
||||
|
||||
# 拆垛层数显示
|
||||
self.stow_level_label = QLabel("拆垛层数: 1")
|
||||
self.stow_level_label.setFont(self.normal_font)
|
||||
self.stow_level_label.setAlignment(Qt.AlignCenter)
|
||||
self.stow_level_label.setStyleSheet("color: #333333; background-color: #f5f5f5; padding: 5px; border: 1px solid #cccccc; border-radius: 3px;")
|
||||
self.input_area_layout.addWidget(self.stow_level_label)
|
||||
|
||||
self.output_layout.addWidget(self.input_area)
|
||||
|
||||
# 下料区(下料层数、下料位置)
|
||||
self.output_area = QWidget()
|
||||
self.output_area.setStyleSheet("background-color: white;")
|
||||
self.output_area_layout = QVBoxLayout(self.output_area)
|
||||
self.output_area_layout.setContentsMargins(5, 5, 5, 5)
|
||||
self.output_area_layout.setSpacing(5)
|
||||
|
||||
# 下料区标签
|
||||
self.output_area_label = QLabel("下料区")
|
||||
self.output_area_label.setFont(self.normal_font)
|
||||
self.output_area_label.setAlignment(Qt.AlignCenter)
|
||||
self.output_area_label.setStyleSheet("font-weight: bold; color: #333333;")
|
||||
self.output_area_layout.addWidget(self.output_area_label)
|
||||
|
||||
# 下料层数显示
|
||||
self.unload_level_label = QLabel("下料层数: 0/0")
|
||||
self.unload_level_label.setFont(self.normal_font)
|
||||
self.unload_level_label.setAlignment(Qt.AlignCenter)
|
||||
self.unload_level_label.setStyleSheet("color: #333333; background-color: #f5f5f5; padding: 5px; border: 1px solid #cccccc; border-radius: 3px;")
|
||||
self.output_area_layout.addWidget(self.unload_level_label)
|
||||
|
||||
# 下料位置显示
|
||||
self.unload_position_label = QLabel("下料位置: 0")
|
||||
self.unload_position_label.setFont(self.normal_font)
|
||||
self.unload_position_label.setAlignment(Qt.AlignCenter)
|
||||
self.unload_position_label.setStyleSheet("color: #333333; background-color: #f5f5f5; padding: 5px; border: 1px solid #cccccc; border-radius: 3px;")
|
||||
self.output_area_layout.addWidget(self.unload_position_label)
|
||||
|
||||
self.output_layout.addWidget(self.output_area)
|
||||
|
||||
self.left_layout.addWidget(self.output_frame)
|
||||
|
||||
@ -650,16 +834,20 @@ class MainWindowUI(QMainWindow):
|
||||
|
||||
self.process_table = QTableWidget(8, total_columns) # 8行:1行标题区域 + 1行列标题 + 6行数据
|
||||
|
||||
# 为微丝产线表格设置更大的字体
|
||||
process_font = QFont("微软雅黑", 16) # 比normal_font(12)大2个字号
|
||||
self.process_table.setFont(process_font)
|
||||
|
||||
# 应用通用表格设置
|
||||
self.setup_table_common(self.process_table)
|
||||
|
||||
# 设置行高
|
||||
self.process_table.setRowHeight(0, 30) # 标题区域行高
|
||||
self.process_table.setRowHeight(1, 30) # 列标题行高
|
||||
# 设置行高 - 适当增加以适应新字号
|
||||
self.process_table.setRowHeight(0, 40) # 标题区域行高,从30增加到35
|
||||
self.process_table.setRowHeight(1, 40) # 列标题行高,从30增加到35
|
||||
|
||||
# 设置数据行的行高
|
||||
for row in range(2, 8): # 工序行
|
||||
self.process_table.setRowHeight(row, 35)
|
||||
self.process_table.setRowHeight(row, 45) # 从35增加到40
|
||||
|
||||
# 设置列宽
|
||||
self.set_process_table_column_widths()
|
||||
|
||||
375
ui/order_query_dialog_ui.py
Normal file
375
ui/order_query_dialog_ui.py
Normal file
@ -0,0 +1,375 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog, QLabel, QLineEdit, QComboBox, QPushButton,
|
||||
QVBoxLayout, QHBoxLayout, QFrame, QTableWidget, QTableWidgetItem,
|
||||
QHeaderView, QDateEdit, QApplication
|
||||
)
|
||||
from PySide6.QtCore import Qt, QDate
|
||||
from PySide6.QtGui import QFont
|
||||
|
||||
class OrderQueryDialogUI(QDialog):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("订单查询")
|
||||
self.resize(1400, 700) # 增加宽度和高度
|
||||
|
||||
# 设置字体
|
||||
self.normal_font = QFont("微软雅黑", 10)
|
||||
self.title_font = QFont("微软雅黑", 10, QFont.Bold)
|
||||
|
||||
# 初始化UI
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
"""初始化UI"""
|
||||
# 主布局
|
||||
self.main_layout = QVBoxLayout(self)
|
||||
self.main_layout.setContentsMargins(20, 20, 20, 20)
|
||||
self.main_layout.setSpacing(10)
|
||||
|
||||
# 创建查询条件区域
|
||||
self.create_query_frame()
|
||||
|
||||
# 创建结果表格
|
||||
self.create_result_table()
|
||||
|
||||
# 创建按钮
|
||||
self.create_buttons()
|
||||
|
||||
def create_query_frame(self):
|
||||
"""创建查询条件区域"""
|
||||
# 创建一个带边框的容器
|
||||
query_frame = QFrame()
|
||||
query_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
border: 1px solid #e0e0e0;
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
}
|
||||
""")
|
||||
|
||||
# 容器的垂直布局
|
||||
query_layout = QVBoxLayout(query_frame)
|
||||
query_layout.setContentsMargins(15, 15, 15, 15)
|
||||
query_layout.setSpacing(10)
|
||||
|
||||
# 第一行:日期范围和订单Mo
|
||||
row1 = QHBoxLayout()
|
||||
|
||||
# 日期标签
|
||||
date_label = QLabel("日期:")
|
||||
date_label.setFont(self.normal_font)
|
||||
date_label.setFixedWidth(40)
|
||||
|
||||
# 开始日期
|
||||
self.start_date = QDateEdit()
|
||||
self.start_date.setFont(self.normal_font)
|
||||
self.start_date.setCalendarPopup(True)
|
||||
self.start_date.setDate(QDate.currentDate().addDays(-7))
|
||||
self.start_date.setFixedWidth(120)
|
||||
self.start_date.setStyleSheet("""
|
||||
QDateEdit {
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
""")
|
||||
|
||||
# 至标签
|
||||
to_label = QLabel("至:")
|
||||
to_label.setFont(self.normal_font)
|
||||
to_label.setFixedWidth(20)
|
||||
|
||||
# 结束日期
|
||||
self.end_date = QDateEdit()
|
||||
self.end_date.setFont(self.normal_font)
|
||||
self.end_date.setCalendarPopup(True)
|
||||
self.end_date.setDate(QDate.currentDate())
|
||||
self.end_date.setFixedWidth(120)
|
||||
self.end_date.setStyleSheet("""
|
||||
QDateEdit {
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
""")
|
||||
|
||||
# 订单Mo标签
|
||||
order_mo_label = QLabel("订单Mo:")
|
||||
order_mo_label.setFont(self.normal_font)
|
||||
order_mo_label.setFixedWidth(60)
|
||||
|
||||
# 订单Mo输入框
|
||||
self.order_mo_input = QLineEdit()
|
||||
self.order_mo_input.setFont(self.normal_font)
|
||||
self.order_mo_input.setStyleSheet("""
|
||||
QLineEdit {
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border: 1px solid #66afe9;
|
||||
}
|
||||
""")
|
||||
|
||||
# 材质标签
|
||||
material_label = QLabel("材质")
|
||||
material_label.setFont(self.normal_font)
|
||||
material_label.setFixedWidth(40)
|
||||
|
||||
# 材质下拉框
|
||||
self.material_combo = QComboBox()
|
||||
self.material_combo.setFont(self.normal_font)
|
||||
self.material_combo.addItem("全部")
|
||||
self.material_combo.setStyleSheet("""
|
||||
QComboBox {
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QComboBox::drop-down {
|
||||
subcontrol-origin: padding;
|
||||
subcontrol-position: top right;
|
||||
width: 20px;
|
||||
border-left: 1px solid #e0e0e0;
|
||||
}
|
||||
""")
|
||||
|
||||
# 规格标签
|
||||
spec_label = QLabel("规格")
|
||||
spec_label.setFont(self.normal_font)
|
||||
spec_label.setFixedWidth(40)
|
||||
|
||||
# 规格输入框
|
||||
self.spec_input = QLineEdit()
|
||||
self.spec_input.setFont(self.normal_font)
|
||||
self.spec_input.setStyleSheet("""
|
||||
QLineEdit {
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
border: 1px solid #66afe9;
|
||||
}
|
||||
""")
|
||||
|
||||
# 查询按钮
|
||||
self.query_button = QPushButton("查询")
|
||||
self.query_button.setFont(self.normal_font)
|
||||
self.query_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #0078d4;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 6px 16px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #106ebe;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
self.query_button.setFixedWidth(80)
|
||||
|
||||
# 添加组件到第一行布局
|
||||
row1.addWidget(date_label)
|
||||
row1.addWidget(self.start_date)
|
||||
row1.addWidget(to_label)
|
||||
row1.addWidget(self.end_date)
|
||||
row1.addSpacing(20)
|
||||
row1.addWidget(order_mo_label)
|
||||
row1.addWidget(self.order_mo_input, 1)
|
||||
row1.addSpacing(20)
|
||||
row1.addWidget(material_label)
|
||||
row1.addWidget(self.material_combo, 1)
|
||||
row1.addSpacing(20)
|
||||
row1.addWidget(spec_label)
|
||||
row1.addWidget(self.spec_input, 1)
|
||||
row1.addSpacing(20)
|
||||
row1.addWidget(self.query_button)
|
||||
|
||||
# 添加第一行到查询布局
|
||||
query_layout.addLayout(row1)
|
||||
|
||||
# 将查询框架添加到主布局
|
||||
self.main_layout.addWidget(query_frame)
|
||||
|
||||
def create_result_table(self):
|
||||
"""创建结果表格"""
|
||||
# 创建表格
|
||||
self.result_table = QTableWidget()
|
||||
self.result_table.setFont(self.normal_font)
|
||||
|
||||
# 设置表头样式
|
||||
header = self.result_table.horizontalHeader()
|
||||
header.setStyleSheet("""
|
||||
QHeaderView::section {
|
||||
background-color: #f5f5f5;
|
||||
color: #333333;
|
||||
padding: 5px;
|
||||
border: 1px solid #e0e0e0;
|
||||
font-weight: bold;
|
||||
}
|
||||
""")
|
||||
|
||||
# 设置表格样式
|
||||
self.result_table.setStyleSheet("""
|
||||
QTableWidget {
|
||||
border: 1px solid #e0e0e0;
|
||||
gridline-color: #e0e0e0;
|
||||
selection-background-color: #0078d4;
|
||||
selection-color: white;
|
||||
}
|
||||
QTableWidget::item {
|
||||
padding: 5px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
""")
|
||||
|
||||
# 启用水平滚动条
|
||||
self.result_table.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||||
|
||||
# 设置表头固定宽度模式,确保所有列都能显示
|
||||
header.setSectionResizeMode(QHeaderView.Fixed)
|
||||
|
||||
# 设置表格最小宽度,确保有足够空间显示滚动条
|
||||
self.result_table.setMinimumWidth(800)
|
||||
|
||||
# 设置默认列宽 - 常用列宽一些,不常用列窄一些
|
||||
self.result_table.setColumnWidth(0, 60) # 序号
|
||||
self.result_table.setColumnWidth(1, 100) # 日期
|
||||
self.result_table.setColumnWidth(2, 120) # 订单号
|
||||
self.result_table.setColumnWidth(3, 120) # 订单明细
|
||||
self.result_table.setColumnWidth(4, 120) # 客户
|
||||
self.result_table.setColumnWidth(5, 120) # 客户订单号
|
||||
self.result_table.setColumnWidth(6, 100) # 订单类别
|
||||
self.result_table.setColumnWidth(7, 100) # 客户交期
|
||||
self.result_table.setColumnWidth(8, 100) # 编码
|
||||
self.result_table.setColumnWidth(9, 100) # 产品类别
|
||||
self.result_table.setColumnWidth(10, 80) # 材质
|
||||
self.result_table.setColumnWidth(11, 80) # 规格
|
||||
self.result_table.setColumnWidth(12, 80) # 产地
|
||||
self.result_table.setColumnWidth(13, 80) # 最大入库量
|
||||
self.result_table.setColumnWidth(14, 100) # 托盘号
|
||||
self.result_table.setColumnWidth(15, 100) # 轴型
|
||||
self.result_table.setColumnWidth(16, 80) # 轴型code
|
||||
self.result_table.setColumnWidth(17, 80) # 轴型重量
|
||||
self.result_table.setColumnWidth(18, 100) # 标签类别
|
||||
self.result_table.setColumnWidth(19, 100) # 标签类别code
|
||||
self.result_table.setColumnWidth(20, 100) # 打印材质
|
||||
self.result_table.setColumnWidth(21, 100) # 炉号
|
||||
self.result_table.setColumnWidth(22, 80) # 公司
|
||||
self.result_table.setColumnWidth(23, 80) # 数量
|
||||
self.result_table.setColumnWidth(24, 80) # 上公差
|
||||
self.result_table.setColumnWidth(25, 80) # 下公差
|
||||
self.result_table.setColumnWidth(26, 80) # 延伸率
|
||||
self.result_table.setColumnWidth(27, 80) # 屈服强度
|
||||
self.result_table.setColumnWidth(28, 80) # 英制规格
|
||||
self.result_table.setColumnWidth(29, 80) # 强度上限
|
||||
self.result_table.setColumnWidth(30, 80) # 强度下限
|
||||
self.result_table.setColumnWidth(31, 100) # 包装方式
|
||||
self.result_table.setColumnWidth(32, 120) # 订单要求
|
||||
self.result_table.setColumnWidth(33, 150) # 备注
|
||||
self.result_table.setColumnWidth(34, 100) # 包装强度上限
|
||||
self.result_table.setColumnWidth(35, 100) # 包装强度下限
|
||||
self.result_table.setColumnWidth(36, 100) # 轴重要求
|
||||
|
||||
# 设置表格可以选择整行
|
||||
self.result_table.setSelectionBehavior(QTableWidget.SelectRows)
|
||||
|
||||
# 设置表格只能单选
|
||||
self.result_table.setSelectionMode(QTableWidget.SingleSelection)
|
||||
|
||||
# 添加表格到主布局
|
||||
self.main_layout.addWidget(self.result_table, 1) # 1表示拉伸因子,让表格占据更多空间
|
||||
|
||||
def create_buttons(self):
|
||||
"""创建底部按钮"""
|
||||
button_layout = QHBoxLayout()
|
||||
|
||||
# 分页控件(示例)
|
||||
page_layout = QHBoxLayout()
|
||||
|
||||
# 添加分页按钮(示例)
|
||||
self.first_page_btn = QPushButton("<<")
|
||||
self.prev_page_btn = QPushButton("<")
|
||||
self.page_label = QLabel("1 / 1")
|
||||
self.next_page_btn = QPushButton(">")
|
||||
self.last_page_btn = QPushButton(">>")
|
||||
|
||||
page_style = """
|
||||
QPushButton {
|
||||
border: 1px solid #e0e0e0;
|
||||
background-color: white;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
QLabel {
|
||||
padding: 0 10px;
|
||||
}
|
||||
"""
|
||||
|
||||
self.first_page_btn.setStyleSheet(page_style)
|
||||
self.prev_page_btn.setStyleSheet(page_style)
|
||||
self.next_page_btn.setStyleSheet(page_style)
|
||||
self.last_page_btn.setStyleSheet(page_style)
|
||||
|
||||
page_layout.addWidget(self.first_page_btn)
|
||||
page_layout.addWidget(self.prev_page_btn)
|
||||
page_layout.addWidget(self.page_label)
|
||||
page_layout.addWidget(self.next_page_btn)
|
||||
page_layout.addWidget(self.last_page_btn)
|
||||
|
||||
# 确认和取消按钮
|
||||
self.confirm_button = QPushButton("确认")
|
||||
self.confirm_button.setFont(self.normal_font)
|
||||
self.confirm_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: #0078d4;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #106ebe;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #005a9e;
|
||||
}
|
||||
""")
|
||||
self.confirm_button.setFixedSize(100, 35)
|
||||
|
||||
self.cancel_button = QPushButton("取消")
|
||||
self.cancel_button.setFont(self.normal_font)
|
||||
self.cancel_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
background-color: white;
|
||||
color: #333333;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
""")
|
||||
self.cancel_button.setFixedSize(100, 35)
|
||||
|
||||
# 添加分页控件和按钮到布局
|
||||
button_layout.addLayout(page_layout)
|
||||
button_layout.addStretch()
|
||||
button_layout.addWidget(self.confirm_button)
|
||||
button_layout.addSpacing(20)
|
||||
button_layout.addWidget(self.cancel_button)
|
||||
|
||||
# 添加按钮布局到主布局
|
||||
self.main_layout.addLayout(button_layout)
|
||||
@ -1,7 +1,8 @@
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QTableWidget, QTableWidgetItem, QHeaderView,
|
||||
QPushButton, QComboBox, QFrame, QDateEdit
|
||||
QPushButton, QComboBox, QFrame, QDateEdit,
|
||||
QLineEdit
|
||||
)
|
||||
from PySide6.QtCore import Qt, QDate
|
||||
from PySide6.QtGui import QFont
|
||||
@ -37,27 +38,67 @@ class ReportDialogUI(QDialog):
|
||||
# 筛选条件容器
|
||||
self.filter_frame = QFrame()
|
||||
self.filter_frame.setFrameShape(QFrame.StyledPanel)
|
||||
self.filter_layout = QHBoxLayout(self.filter_frame)
|
||||
self.filter_layout = QVBoxLayout(self.filter_frame)
|
||||
|
||||
# 日期选择
|
||||
# 第一行:日期选择
|
||||
self.date_row = QHBoxLayout()
|
||||
self.date_label = QLabel("日期范围:")
|
||||
self.date_label.setFont(self.normal_font)
|
||||
self.date_label.setFixedWidth(80)
|
||||
self.start_date = QDateEdit()
|
||||
self.start_date.setFont(self.normal_font)
|
||||
self.start_date.setCalendarPopup(True)
|
||||
self.start_date.setDate(QDate.currentDate())
|
||||
self.start_date.setDate(QDate.currentDate().addMonths(-1)) # 默认开始日期为一个月前
|
||||
self.date_separator = QLabel("-")
|
||||
self.end_date = QDateEdit()
|
||||
self.end_date.setFont(self.normal_font)
|
||||
self.end_date.setCalendarPopup(True)
|
||||
self.end_date.setDate(QDate.currentDate())
|
||||
|
||||
# 报表类型选择
|
||||
self.type_label = QLabel("报表类型:")
|
||||
self.type_label.setFont(self.normal_font)
|
||||
self.type_combo = QComboBox()
|
||||
self.type_combo.setFont(self.normal_font)
|
||||
self.type_combo.addItems(["日报表", "月报表", "年报表"])
|
||||
self.date_row.addWidget(self.date_label)
|
||||
self.date_row.addWidget(self.start_date)
|
||||
self.date_row.addWidget(self.date_separator)
|
||||
self.date_row.addWidget(self.end_date)
|
||||
self.date_row.addStretch()
|
||||
|
||||
# 第二行:客户、材质、规格
|
||||
self.filter_row = QHBoxLayout()
|
||||
|
||||
# 客户输入
|
||||
self.customer_label = QLabel("客户:")
|
||||
self.customer_label.setFont(self.normal_font)
|
||||
self.customer_label.setFixedWidth(80)
|
||||
self.customer_edit = QLineEdit()
|
||||
self.customer_edit.setFont(self.normal_font)
|
||||
self.customer_edit.setPlaceholderText("输入客户名称")
|
||||
|
||||
# 材质输入
|
||||
self.material_label = QLabel("材质:")
|
||||
self.material_label.setFont(self.normal_font)
|
||||
self.material_label.setFixedWidth(80)
|
||||
self.material_edit = QLineEdit()
|
||||
self.material_edit.setFont(self.normal_font)
|
||||
self.material_edit.setPlaceholderText("输入材质")
|
||||
|
||||
# 规格输入
|
||||
self.spec_label = QLabel("规格:")
|
||||
self.spec_label.setFont(self.normal_font)
|
||||
self.spec_label.setFixedWidth(80)
|
||||
self.spec_edit = QLineEdit()
|
||||
self.spec_edit.setFont(self.normal_font)
|
||||
self.spec_edit.setPlaceholderText("输入规格")
|
||||
|
||||
self.filter_row.addWidget(self.customer_label)
|
||||
self.filter_row.addWidget(self.customer_edit)
|
||||
self.filter_row.addSpacing(10)
|
||||
self.filter_row.addWidget(self.material_label)
|
||||
self.filter_row.addWidget(self.material_edit)
|
||||
self.filter_row.addSpacing(10)
|
||||
self.filter_row.addWidget(self.spec_label)
|
||||
self.filter_row.addWidget(self.spec_edit)
|
||||
|
||||
# 第三行:查询按钮
|
||||
self.button_row = QHBoxLayout()
|
||||
|
||||
# 查询按钮
|
||||
self.query_button = QPushButton("查询")
|
||||
@ -74,17 +115,29 @@ class ReportDialogUI(QDialog):
|
||||
}
|
||||
""")
|
||||
|
||||
# 添加组件到筛选布局
|
||||
self.filter_layout.addWidget(self.date_label)
|
||||
self.filter_layout.addWidget(self.start_date)
|
||||
self.filter_layout.addWidget(self.date_separator)
|
||||
self.filter_layout.addWidget(self.end_date)
|
||||
self.filter_layout.addSpacing(20)
|
||||
self.filter_layout.addWidget(self.type_label)
|
||||
self.filter_layout.addWidget(self.type_combo)
|
||||
self.filter_layout.addSpacing(20)
|
||||
self.filter_layout.addWidget(self.query_button)
|
||||
self.filter_layout.addStretch()
|
||||
# 清空按钮
|
||||
self.clear_button = QPushButton("清空条件")
|
||||
self.clear_button.setFont(self.normal_font)
|
||||
self.clear_button.setStyleSheet("""
|
||||
QPushButton {
|
||||
padding: 8px 16px;
|
||||
background-color: #757575;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #616161;
|
||||
}
|
||||
""")
|
||||
|
||||
self.button_row.addStretch()
|
||||
self.button_row.addWidget(self.clear_button)
|
||||
self.button_row.addWidget(self.query_button)
|
||||
|
||||
# 添加所有行到筛选布局
|
||||
self.filter_layout.addLayout(self.date_row)
|
||||
self.filter_layout.addLayout(self.filter_row)
|
||||
self.filter_layout.addLayout(self.button_row)
|
||||
|
||||
# 添加到主布局
|
||||
self.main_layout.addWidget(self.filter_frame)
|
||||
@ -95,11 +148,10 @@ class ReportDialogUI(QDialog):
|
||||
self.report_table = QTableWidget()
|
||||
self.report_table.setFont(self.normal_font)
|
||||
|
||||
# 设置列
|
||||
self.report_table.setColumnCount(8)
|
||||
# 设置列 - 根据SQL查询结果设置列
|
||||
self.report_table.setColumnCount(7)
|
||||
self.report_table.setHorizontalHeaderLabels([
|
||||
"日期", "工程号", "品名", "规格",
|
||||
"生产数量", "合格数量", "不合格数量", "合格率"
|
||||
"日期", "客户", "订单号", "轴数", "材质", "规格", "净重"
|
||||
])
|
||||
|
||||
# 设置表格样式
|
||||
@ -118,11 +170,78 @@ class ReportDialogUI(QDialog):
|
||||
|
||||
# 调整列宽
|
||||
header = self.report_table.horizontalHeader()
|
||||
for i in range(8):
|
||||
for i in range(7):
|
||||
header.setSectionResizeMode(i, QHeaderView.Stretch)
|
||||
|
||||
# 添加到主布局
|
||||
self.main_layout.addWidget(self.report_table)
|
||||
|
||||
# 创建汇总区域
|
||||
self.create_summary_section()
|
||||
|
||||
def create_summary_section(self):
|
||||
"""创建汇总区域"""
|
||||
# 汇总容器
|
||||
self.summary_frame = QFrame()
|
||||
self.summary_frame.setFrameShape(QFrame.StyledPanel)
|
||||
self.summary_frame.setStyleSheet("""
|
||||
QFrame {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
""")
|
||||
|
||||
self.summary_layout = QHBoxLayout(self.summary_frame)
|
||||
self.summary_layout.setContentsMargins(15, 10, 15, 10)
|
||||
self.summary_layout.setSpacing(20)
|
||||
|
||||
# 汇总标题
|
||||
self.summary_title = QLabel("汇总统计")
|
||||
self.summary_title.setFont(self.title_font)
|
||||
self.summary_title.setStyleSheet("color: #495057; font-weight: bold;")
|
||||
|
||||
# 总轴数
|
||||
self.total_axes_label = QLabel("总轴数:")
|
||||
self.total_axes_label.setFont(self.normal_font)
|
||||
self.total_axes_label.setStyleSheet("color: #495057; font-weight: bold;")
|
||||
self.total_axes_value = QLabel("0")
|
||||
self.total_axes_value.setFont(self.normal_font)
|
||||
self.total_axes_value.setStyleSheet("color: #007bff; font-weight: bold;")
|
||||
self.total_axes_value.setMinimumWidth(80)
|
||||
|
||||
# 总净重
|
||||
self.total_weight_label = QLabel("总净重:")
|
||||
self.total_weight_label.setFont(self.normal_font)
|
||||
self.total_weight_label.setStyleSheet("color: #495057; font-weight: bold;")
|
||||
self.total_weight_value = QLabel("0.00 ")
|
||||
self.total_weight_value.setFont(self.normal_font)
|
||||
self.total_weight_value.setStyleSheet("color: #28a745; font-weight: bold;")
|
||||
self.total_weight_value.setMinimumWidth(100)
|
||||
|
||||
# 记录数
|
||||
self.record_count_label = QLabel("记录数:")
|
||||
self.record_count_label.setFont(self.normal_font)
|
||||
self.record_count_label.setStyleSheet("color: #495057; font-weight: bold;")
|
||||
self.record_count_value = QLabel("0")
|
||||
self.record_count_value.setFont(self.normal_font)
|
||||
self.record_count_value.setStyleSheet("color: #6c757d; font-weight: bold;")
|
||||
self.record_count_value.setMinimumWidth(60)
|
||||
|
||||
# 添加到汇总布局
|
||||
self.summary_layout.addWidget(self.summary_title)
|
||||
self.summary_layout.addStretch()
|
||||
self.summary_layout.addWidget(self.total_axes_label)
|
||||
self.summary_layout.addWidget(self.total_axes_value)
|
||||
self.summary_layout.addSpacing(20)
|
||||
self.summary_layout.addWidget(self.total_weight_label)
|
||||
self.summary_layout.addWidget(self.total_weight_value)
|
||||
self.summary_layout.addSpacing(20)
|
||||
self.summary_layout.addWidget(self.record_count_label)
|
||||
self.summary_layout.addWidget(self.record_count_value)
|
||||
|
||||
# 添加到主布局
|
||||
self.main_layout.addWidget(self.summary_frame)
|
||||
|
||||
def create_button_section(self):
|
||||
"""创建按钮区域"""
|
||||
|
||||
@ -9,7 +9,7 @@ class UnloadingDialogUI(QDialog):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("下料操作")
|
||||
self.setFixedSize(600, 250) # 减小高度,因为移除了第一行
|
||||
self.setFixedSize(700, 250) # 增加宽度以适应较长的托盘类型选项
|
||||
|
||||
# 设置字体
|
||||
self.normal_font = QFont("微软雅黑", 12)
|
||||
@ -92,13 +92,15 @@ class UnloadingDialogUI(QDialog):
|
||||
self.tray_label.setStyleSheet(label_style)
|
||||
self.tray_label.setFixedWidth(100)
|
||||
self.tray_label.setFixedHeight(45)
|
||||
|
||||
|
||||
self.tray_input = QLineEdit()
|
||||
self.tray_input.setFont(self.normal_font)
|
||||
self.tray_input.setPlaceholderText("请扫描托盘号")
|
||||
self.tray_input.setStyleSheet(input_style)
|
||||
self.tray_input.setFixedHeight(45)
|
||||
|
||||
# 禁止用户修改
|
||||
self.tray_input.setReadOnly(True)
|
||||
self.tray_input.setFocusPolicy(Qt.NoFocus)
|
||||
row1.addWidget(self.tray_label)
|
||||
row1.addWidget(self.tray_input, 1)
|
||||
container_layout.addLayout(row1)
|
||||
|
||||
@ -48,7 +48,7 @@ class ModbusMonitor(QObject):
|
||||
register_error = Signal(int, str)
|
||||
monitor_status_changed = Signal(bool, str)
|
||||
|
||||
def __init__(self, polling_interval=1.0, max_errors=3, retry_interval=5.0):
|
||||
def __init__(self, polling_interval=0.5, max_errors=3, retry_interval=5.0):
|
||||
"""
|
||||
初始化Modbus监控器
|
||||
|
||||
@ -77,7 +77,7 @@ class ModbusMonitor(QObject):
|
||||
def _initialize_registers(self):
|
||||
"""初始化要监控的寄存器列表"""
|
||||
# 默认监控的寄存器地址
|
||||
register_addresses = [0, 4, 5, 6, 11, 13, 20, 21, 22, 23, 24, 25, 30]
|
||||
register_addresses = [0, 2, 3, 4, 5, 6, 11, 13, 20, 21, 22, 23, 24, 25, 30]
|
||||
for address in register_addresses:
|
||||
self.registers[address] = RegisterValue(address)
|
||||
|
||||
|
||||
@ -301,4 +301,17 @@ class EmergencyStopHandler:
|
||||
changed, desc = self.handle(value)
|
||||
if changed:
|
||||
logging.info(f"急停状态变化: {desc} (值={value})")
|
||||
return changed, desc
|
||||
return changed, desc
|
||||
|
||||
|
||||
class RegisterChangeHandler(RegisterHandler):
|
||||
"""通用寄存器变化处理器,用于处理寄存器2和3等"""
|
||||
def __init__(self, callback=None, address=None):
|
||||
super().__init__()
|
||||
self.callback = callback
|
||||
self.address = address
|
||||
|
||||
def handle_change(self, value):
|
||||
logging.info(f"寄存器D{self.address}变化: {value}")
|
||||
if self.callback:
|
||||
self.callback(self.address, value)
|
||||
@ -51,6 +51,12 @@ class SerialManager:
|
||||
# 稳定性时间跟踪
|
||||
self.stability_start_time = 0 # 开始检测稳定性的时间
|
||||
|
||||
# 串口失败跟踪
|
||||
self.port_failure_count: Dict[str, int] = {} # 记录每个串口的连续失败次数
|
||||
self.port_last_failure_time: Dict[str, float] = {} # 记录每个串口的最后失败时间
|
||||
self.max_failures = 5 # 最大连续失败次数
|
||||
self.failure_cooldown = 300 # 失败冷却时间(秒),5分钟
|
||||
|
||||
# 数据存储
|
||||
self.data = {
|
||||
'mdz': 0,
|
||||
@ -996,20 +1002,26 @@ class SerialManager:
|
||||
port_name = self.cz_config['ser']
|
||||
baud_rate = self.cz_config.get('port', 9600)
|
||||
|
||||
if not self.is_port_open(port_name):
|
||||
# 检查串口是否在冷却期内
|
||||
if self._should_skip_port_attempt(port_name):
|
||||
logging.info(f"称重串口 {port_name} 在冷却期内,跳过重试(失败{self.port_failure_count.get(port_name, 0)}次)")
|
||||
elif not self.is_port_open(port_name):
|
||||
try:
|
||||
if self.open_port(port_name, 'cz', baud_rate):
|
||||
logging.info(f"自动打开称重串口 {port_name} 成功")
|
||||
self._reset_port_failure(port_name) # 成功后重置失败计数
|
||||
else:
|
||||
logging.error(f"自动打开称重串口 {port_name} 失败")
|
||||
self._record_port_failure(port_name)
|
||||
success = False
|
||||
except Exception as e:
|
||||
logging.error(f"自动打开称重串口 {port_name} 时发生异常: {e}")
|
||||
self._record_port_failure(port_name)
|
||||
success = False
|
||||
else:
|
||||
logging.info(f"称重串口 {port_name} 已经打开,无需重新打开")
|
||||
else:
|
||||
logging.warning("称重串口未配置或设置为不使用,跳过自动打开")
|
||||
logging.info("称重串口未配置或设置为不使用,跳过自动打开")
|
||||
|
||||
# 尝试打开线径串口
|
||||
if self.xj_config and 'ser' in self.xj_config and self.xj_config['ser'] and self.xj_config['ser'].strip():
|
||||
@ -1117,8 +1129,8 @@ class SerialManager:
|
||||
byte_data = bytes.fromhex(query_cmd.replace(' ', ''))
|
||||
self.serial_ports[port_name].write(byte_data)
|
||||
|
||||
# 等待响应
|
||||
time.sleep(0.5)
|
||||
# 等待响应 - 减少等待时间以加快数据获取
|
||||
time.sleep(0.2)
|
||||
|
||||
if self.serial_ports[port_name].in_waiting > 0:
|
||||
response = self.serial_ports[port_name].read(self.serial_ports[port_name].in_waiting)
|
||||
@ -1128,14 +1140,14 @@ class SerialManager:
|
||||
logging.error(f"线径数据处理异常: {e}")
|
||||
|
||||
# 查询间隔,从配置中获取或使用默认值
|
||||
query_interval = self.xj_config.get('query_interval', 5) if self.xj_config else 5
|
||||
wait_cycles = int(query_interval * 10) # 转换为0.1秒的周期数
|
||||
query_interval = self.xj_config.get('query_interval', 1) if self.xj_config else 1
|
||||
|
||||
# 每隔query_interval秒查询一次
|
||||
for i in range(wait_cycles):
|
||||
if not self.running_flags.get(port_name, False):
|
||||
break
|
||||
time.sleep(0.1)
|
||||
# 确保查询间隔不小于0.2秒
|
||||
if query_interval < 0.2:
|
||||
query_interval = 0.2
|
||||
|
||||
# 每隔query_interval秒查询一次,使用更简单的等待方式
|
||||
time.sleep(query_interval)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"线径串口 {port_name} 读取线程异常: {e}")
|
||||
@ -1325,4 +1337,46 @@ class SerialManager:
|
||||
logging.error(f"线程监控主循环异常: {e}")
|
||||
# 尝试重新启动监控
|
||||
time.sleep(10)
|
||||
self._start_thread_monitor()
|
||||
self._start_thread_monitor()
|
||||
|
||||
def _should_skip_port_attempt(self, port_name: str) -> bool:
|
||||
"""检查是否应该跳过串口重试(基于失败次数和冷却时间)"""
|
||||
current_time = time.time()
|
||||
|
||||
# 如果没有失败记录,允许尝试
|
||||
if port_name not in self.port_failure_count:
|
||||
return False
|
||||
|
||||
failure_count = self.port_failure_count[port_name]
|
||||
last_failure_time = self.port_last_failure_time.get(port_name, 0)
|
||||
|
||||
# 如果失败次数超过最大值且在冷却期内,跳过
|
||||
if failure_count >= self.max_failures:
|
||||
time_since_failure = current_time - last_failure_time
|
||||
if time_since_failure < self.failure_cooldown:
|
||||
return True
|
||||
else:
|
||||
# 冷却期结束,重置失败计数给一次机会
|
||||
logging.info(f"串口 {port_name} 冷却期结束,重置失败计数")
|
||||
self._reset_port_failure(port_name)
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def _record_port_failure(self, port_name: str):
|
||||
"""记录串口打开失败"""
|
||||
current_time = time.time()
|
||||
self.port_failure_count[port_name] = self.port_failure_count.get(port_name, 0) + 1
|
||||
self.port_last_failure_time[port_name] = current_time
|
||||
|
||||
failure_count = self.port_failure_count[port_name]
|
||||
if failure_count >= self.max_failures:
|
||||
cooldown_minutes = self.failure_cooldown // 60
|
||||
logging.warning(f"串口 {port_name} 连续失败{failure_count}次,将在{cooldown_minutes}分钟内暂停重试")
|
||||
|
||||
def _reset_port_failure(self, port_name: str):
|
||||
"""重置串口失败计数"""
|
||||
if port_name in self.port_failure_count:
|
||||
del self.port_failure_count[port_name]
|
||||
if port_name in self.port_last_failure_time:
|
||||
del self.port_last_failure_time[port_name]
|
||||
@ -1,25 +1,30 @@
|
||||
from ui.loading_dialog_ui import LoadingDialogUI
|
||||
from apis.tary_api import TaryApi
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from PySide6.QtWidgets import QMessageBox, QDialog
|
||||
import logging
|
||||
from PySide6.QtWidgets import QMessageBox
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from ui.loading_dialog_ui import LoadingDialogUI
|
||||
from widgets.order_query_dialog import OrderQueryDialog
|
||||
from utils.app_mode import AppMode
|
||||
from utils.pallet_type_manager import PalletTypeManager
|
||||
from apis.gc_api import GcApi
|
||||
class LoadingDialog(LoadingDialogUI):
|
||||
|
||||
# 定义一个信号,用于向主窗口传递托盘号
|
||||
class LoadingDialog(LoadingDialogUI):
|
||||
"""上料操作对话框"""
|
||||
|
||||
# 定义信号,用于向主窗口传递托盘号
|
||||
tray_code_signal = Signal(str, str, str, str)
|
||||
# 定义一个信号,用于向主窗口传递订单号
|
||||
# 定义信号,用于向主窗口传递订单号
|
||||
order_code_signal = Signal(str)
|
||||
|
||||
def __init__(self, parent=None,user_id=None,user_name=None,corp_id=None):
|
||||
"""初始化加载对话框"""
|
||||
def __init__(self, parent=None, user_id=None, user_name=None, corp_id=None):
|
||||
super().__init__()
|
||||
self.parent = parent
|
||||
self.user_id = user_id
|
||||
self.user_id = user_id,
|
||||
self.user_name = user_name
|
||||
self.corp_id = corp_id
|
||||
|
||||
# 存储订单数据
|
||||
self.order_data = None
|
||||
|
||||
# 彻底禁用对话框的回车键关闭功能
|
||||
self.setModal(True)
|
||||
# 禁用所有按钮的默认行为
|
||||
@ -35,11 +40,14 @@ class LoadingDialog(LoadingDialogUI):
|
||||
from utils.focus_tracker import FocusTracker
|
||||
self.focus_tracker = FocusTracker.get_instance()
|
||||
|
||||
# 绑定事件
|
||||
self.setup_connections()
|
||||
# 连接信号和槽
|
||||
self.connect_signals()
|
||||
|
||||
def connect_signals(self):
|
||||
"""连接信号和槽"""
|
||||
# 订单查询按钮点击事件
|
||||
self.order_query_btn.clicked.connect(self.show_order_query_dialog)
|
||||
|
||||
def setup_connections(self):
|
||||
"""设置事件连接"""
|
||||
# 订单号输入框回车事件触发查询
|
||||
self.order_input.returnPressed.connect(self.handle_order_return_pressed)
|
||||
|
||||
@ -48,6 +56,10 @@ class LoadingDialog(LoadingDialogUI):
|
||||
|
||||
# 取消按钮点击事件
|
||||
self.cancel_button.clicked.connect(self.reject)
|
||||
|
||||
# 托盘号输入框回车事件
|
||||
self.tray_input.returnPressed.connect(self.on_tray_entered)
|
||||
|
||||
def handle_order_return_pressed(self):
|
||||
"""处理订单输入框的回车事件"""
|
||||
logging.info("订单输入框回车事件触发")
|
||||
@ -63,67 +75,199 @@ class LoadingDialog(LoadingDialogUI):
|
||||
if main_window and isinstance(main_window, MainWindow):
|
||||
logging.info(f"发送订单号到主窗口: {order_code}")
|
||||
self.order_code_signal.emit(order_code)
|
||||
|
||||
#判断是否是接口,如果不是接口直接添加如果是则走接口
|
||||
# 如果开启接口模式,则需要调用接口同步到业务库
|
||||
order_info = None
|
||||
|
||||
# 查询订单信息
|
||||
self.on_order_query(order_code)
|
||||
|
||||
|
||||
# 阻止事件继续传播
|
||||
return True
|
||||
|
||||
def on_order_query(self,order_code):
|
||||
"""查询订单信息,同时生成托盘号,并且回显到页面上"""
|
||||
|
||||
def show_order_query_dialog(self):
|
||||
"""显示订单查询对话框"""
|
||||
try:
|
||||
if AppMode.is_api():
|
||||
# 调用接口
|
||||
gc_api = GcApi()
|
||||
# 防止response为None导致异常
|
||||
# 获取工程号信息,并且初始化数据
|
||||
order_response = gc_api.get_order_info(order_code,self.corp_id)
|
||||
# 生成托盘号
|
||||
if(order_response.get("status",False)):
|
||||
# 将接口数据保存到数据库,用于后续的关联使用
|
||||
from dao.inspection_dao import InspectionDAO
|
||||
inspection_dao = InspectionDAO()
|
||||
order_info = order_response.get("data", {})[0]
|
||||
# 设置轴数
|
||||
order_info['user_id'] = self.user_id
|
||||
order_info['user_name'] = self.user_name
|
||||
order_info['data_corp'] = self.corp_id
|
||||
inspection_dao.save_order_info(order_code,order_info)
|
||||
self.axis_value.setText(order_info['zx_name'])
|
||||
self.quantity_value.setText(str(order_info['sl']))
|
||||
self.weight_value.setText(str(order_info['sl']))
|
||||
xpack_response = gc_api.get_xpack(order_code,self.corp_id)
|
||||
if(xpack_response.get("status",False)):
|
||||
xpack = xpack_response['xpack']
|
||||
self.tray_input.setText(xpack)
|
||||
self.pallet_tier_value.setText('3')
|
||||
# 发送托盘号到主窗
|
||||
# 创建订单查询对话框
|
||||
dialog = OrderQueryDialog(self)
|
||||
|
||||
# 连接订单选择信号
|
||||
dialog.order_selected.connect(self.on_order_selected)
|
||||
|
||||
# 显示对话框
|
||||
dialog.exec()
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"显示订单查询对话框失败: {e}")
|
||||
QMessageBox.critical(self, "错误", f"显示订单查询对话框失败: {str(e)}")
|
||||
|
||||
def on_order_selected(self, order_data):
|
||||
"""处理订单选择事件
|
||||
|
||||
Args:
|
||||
order_data: 订单数据字典
|
||||
"""
|
||||
try:
|
||||
# 存储订单数据
|
||||
self.order_data = order_data
|
||||
logging.info(f"已存储订单数据: {order_data.get('mo', '')}")
|
||||
|
||||
# 更新UI
|
||||
# 注意:此时order_data["note"]已经被修改为order_data["mo"]的值
|
||||
self.order_input.setText(order_data.get("mo", ""))
|
||||
|
||||
# 更新轴型
|
||||
self.axis_value.setText(order_data.get("zx_name", "--"))
|
||||
|
||||
# 更新数量
|
||||
self.quantity_value.setText(str(order_data.get("sl", "--")))
|
||||
|
||||
# 更新重量
|
||||
self.weight_value.setText(str(order_data.get("zx_zl", "--")))
|
||||
|
||||
# 自动填充托盘号
|
||||
if "xpack" in order_data:
|
||||
self.tray_input.setText(order_data["xpack"])
|
||||
|
||||
# 发送订单号到主窗口
|
||||
from widgets.main_window import MainWindow
|
||||
main_window = self.parent
|
||||
if main_window and isinstance(main_window, MainWindow):
|
||||
# 检查托盘号是否已存在
|
||||
existed = False
|
||||
for i in range(main_window.tray_edit.count()):
|
||||
if main_window.tray_edit.itemText(i) == xpack:
|
||||
existed = True
|
||||
break
|
||||
|
||||
# 如果不存在,则添加
|
||||
if not existed:
|
||||
logging.info(f"添加托盘号到主窗口: {xpack}")
|
||||
main_window.tray_edit.addItem(xpack)
|
||||
|
||||
# 设置当前选中的托盘号
|
||||
main_window.tray_edit.setCurrentText(xpack)
|
||||
logging.info(f"设置主窗口当前托盘号: {xpack}")
|
||||
|
||||
return order_info
|
||||
# 使用note字段作为订单号(已经被修改为mo的值)
|
||||
order_code = order_data.get("mo", "")
|
||||
logging.info(f"发送订单号到主窗口: {order_code}")
|
||||
self.order_code_signal.emit(order_code)
|
||||
|
||||
# 设置焦点到托盘号输入框
|
||||
self.tray_input.setFocus()
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"查询订单信息异常: {str(e)}")
|
||||
return None
|
||||
logging.error(f"处理订单选择事件失败: {e}")
|
||||
QMessageBox.critical(self, "错误", f"处理订单选择事件失败: {str(e)}")
|
||||
|
||||
def on_order_query(self, order_code):
|
||||
"""查询订单信息,同时生成托盘号,并且回显到页面上"""
|
||||
try:
|
||||
# 构建查询参数
|
||||
query_params = {
|
||||
"srch_mo": order_code,
|
||||
"srch_note": order_code,
|
||||
"corp_id": self.corp_id
|
||||
}
|
||||
|
||||
# 创建订单查询对话框(仅用于执行查询)
|
||||
dialog = OrderQueryDialog(self)
|
||||
|
||||
# 执行查询
|
||||
results = dialog.query_orders(query_params)
|
||||
|
||||
# 处理查询结果
|
||||
if results:
|
||||
# 如果只有一个结果,直接使用
|
||||
if len(results) == 1:
|
||||
self.on_order_selected(results[0])
|
||||
return
|
||||
else:
|
||||
# 如果有多个结果,显示查询对话框让用户选择
|
||||
dialog.update_result_table(results)
|
||||
dialog.order_selected.connect(self.on_order_selected)
|
||||
dialog.exec()
|
||||
return
|
||||
|
||||
# 如果没有结果,并且不是API模式,显示提示
|
||||
if not AppMode.is_api():
|
||||
QMessageBox.warning(self, "提示", f"未找到订单: {order_code}")
|
||||
return
|
||||
|
||||
# 如果是API模式,尝试使用旧的API方式查询
|
||||
# 调用接口
|
||||
gc_api = GcApi()
|
||||
# 防止response为None导致异常
|
||||
# 获取工程号信息,并且初始化数据
|
||||
order_response = gc_api.get_order_info(order_code, self.corp_id)
|
||||
# 生成托盘号
|
||||
if order_response and order_response.get("status", False):
|
||||
# 将接口数据保存到数据库,用于后续的关联使用
|
||||
from dao.inspection_dao import InspectionDAO
|
||||
inspection_dao = InspectionDAO()
|
||||
order_info = order_response.get("data", {})[0]
|
||||
# 设置轴数
|
||||
order_info['user_id'] = self.user_id[0]
|
||||
order_info['user_name'] = self.user_name
|
||||
order_info['data_corp'] = self.corp_id
|
||||
inspection_dao.save_order_info(order_code, order_info)
|
||||
self.axis_value.setText(order_info['zx_name'])
|
||||
self.quantity_value.setText(str(order_info['sl']))
|
||||
self.weight_value.setText(str(order_info['zx_zl']))
|
||||
|
||||
# 存储订单数据
|
||||
self.order_data = order_info
|
||||
logging.info(f"已存储订单数据: {order_code}")
|
||||
else:
|
||||
QMessageBox.warning(self, "提示", f"未找到订单: {order_code}")
|
||||
return
|
||||
|
||||
xpack_response = gc_api.get_xpack(order_code, self.corp_id)
|
||||
if xpack_response and xpack_response.get("status", False):
|
||||
xpack = xpack_response['xpack']
|
||||
spack = xpack_response['spack']
|
||||
self.tray_input.setText(xpack)
|
||||
self.pallet_tier_value.setText('3')
|
||||
|
||||
# 将spack保存到order_data中,以便后续使用
|
||||
if hasattr(self, 'order_data') and self.order_data:
|
||||
self.order_data['spack'] = spack
|
||||
logging.info(f"已将spack: {spack}保存到order_data中")
|
||||
|
||||
# 发送托盘号到主窗口
|
||||
from widgets.main_window import MainWindow
|
||||
main_window = self.parent
|
||||
if main_window and isinstance(main_window, MainWindow):
|
||||
# 检查托盘号是否已存在
|
||||
# 由于 tray_edit 现在是 QLineEdit,不再需要检查是否存在
|
||||
# 直接设置文本
|
||||
logging.info(f"设置主窗口当前托盘号: {xpack}")
|
||||
main_window.tray_edit.setText(xpack)
|
||||
|
||||
# 手动触发托盘号变更事件
|
||||
main_window.handle_tray_changed(xpack)
|
||||
|
||||
# spack值已经获取,可以在后续处理中使用
|
||||
logging.info(f"已获取spack: {spack},将与xpack一起使用")
|
||||
|
||||
return order_info
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"查询订单信息失败: {e}")
|
||||
QMessageBox.critical(self, "错误", f"查询订单信息失败: {str(e)}")
|
||||
|
||||
def on_xpack_query(self, tray_id):
|
||||
"""通过托盘号查询订单信息,并回显到页面(完全仿照on_order_query)"""
|
||||
try:
|
||||
query_params = {
|
||||
"srch_spack": tray_id,
|
||||
"corp_id": self.corp_id
|
||||
}
|
||||
dialog = OrderQueryDialog(self)
|
||||
results = dialog.query_orders_xpack(query_params)
|
||||
if results:
|
||||
if len(results) == 1:
|
||||
self.on_order_selected(results[0])
|
||||
return
|
||||
else:
|
||||
dialog.update_result_table(results)
|
||||
dialog.order_selected.connect(self.on_order_selected)
|
||||
dialog.exec()
|
||||
return
|
||||
QMessageBox.warning(self, "提示", "未找到该托盘号对应的订单信息")
|
||||
except Exception as e:
|
||||
logging.error(f"托盘号查订单接口异常: {e}")
|
||||
QMessageBox.warning(self, "错误", f"托盘号查订单接口异常: {str(e)}")
|
||||
|
||||
def on_tray_entered(self):
|
||||
tray_id = self.tray_input.text().strip()
|
||||
if not tray_id:
|
||||
QMessageBox.warning(self, "提示", "请输入托盘号")
|
||||
return
|
||||
self.on_xpack_query(tray_id)
|
||||
self.pallet_tier_value.setFocus()
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""重写键盘事件处理,防止回车关闭对话框"""
|
||||
@ -132,9 +276,7 @@ class LoadingDialog(LoadingDialogUI):
|
||||
logging.info(f"捕获到回车键事件,当前焦点部件: {self.focusWidget()}")
|
||||
|
||||
# 如果焦点在托盘输入框上,触发查询
|
||||
# 注释掉此处的on_tray_query调用,因为已经通过returnPressed信号处理了
|
||||
if self.focusWidget() == self.tray_input:
|
||||
# self.on_tray_query() - 通过returnPressed信号已经处理,这里不需要再调用
|
||||
event.accept() # 消费掉这个事件
|
||||
return
|
||||
|
||||
@ -158,28 +300,38 @@ class LoadingDialog(LoadingDialogUI):
|
||||
if not tier_value:
|
||||
QMessageBox.warning(self, "提示", "请输入托盘层数")
|
||||
return
|
||||
|
||||
|
||||
# 获取包装号
|
||||
pallet_code = self.tray_input.text().strip()
|
||||
if not pallet_code:
|
||||
QMessageBox.warning(self, "提示", "请输入托盘号")
|
||||
return
|
||||
|
||||
|
||||
# 检查订单号
|
||||
order_id = self.order_input.text().strip()
|
||||
if not order_id:
|
||||
QMessageBox.warning(self, "提示", "请输入订单号")
|
||||
self.order_input.setFocus()
|
||||
return
|
||||
|
||||
# if not self.order_data:
|
||||
# QMessageBox.warning(self, "提示", "请先查询订单信息")
|
||||
# return
|
||||
|
||||
try:
|
||||
# 保存托盘档案信息
|
||||
from utils.pallet_type_manager import PalletTypeManager
|
||||
pallet_manager = PalletTypeManager.get_instance()
|
||||
success = pallet_manager.save_pallet_archives(
|
||||
pallet_code=pallet_code,
|
||||
tier=int(tier_value),
|
||||
user_id=self.user_id,
|
||||
user_id=self.user_id[0],
|
||||
user_name=self.user_name
|
||||
)
|
||||
|
||||
if not success:
|
||||
QMessageBox.warning(self, "提示", "保存托盘档案失败")
|
||||
return
|
||||
|
||||
|
||||
logging.info(f"已保存托盘档案:托盘号={pallet_code}, 层数={tier_value}")
|
||||
except Exception as e:
|
||||
logging.error(f"保存托盘档案时发生错误: {str(e)}")
|
||||
@ -212,4 +364,31 @@ class LoadingDialog(LoadingDialogUI):
|
||||
"""重写拒绝方法"""
|
||||
logging.info("取消按钮被点击或ESC触发取消")
|
||||
# 调用父类的reject方法关闭对话框
|
||||
super().reject()
|
||||
super().reject()
|
||||
|
||||
def get_loading_data(self):
|
||||
"""获取上料数据
|
||||
|
||||
Returns:
|
||||
dict: 上料数据字典
|
||||
"""
|
||||
order_id = self.order_input.text().strip()
|
||||
tray_id = self.tray_input.text().strip()
|
||||
spack = ""
|
||||
|
||||
gc_api = GcApi()
|
||||
response = gc_api.get_spack_info(order_id, tray_id, self.corp_id)
|
||||
if response and response.get("status", False) and response.get("data", {}).get("spack", ""):
|
||||
spack = response.get("data", {}).get("spack", "")
|
||||
elif self.order_data and isinstance(self.order_data, dict) and 'spack' in self.order_data:
|
||||
spack = self.order_data['spack']
|
||||
else:
|
||||
spack = ""
|
||||
|
||||
return {
|
||||
"order_id": order_id,
|
||||
"tray_id": tray_id,
|
||||
"spack": spack, # 添加spack字段
|
||||
"pallet_tier": self.pallet_tier_value.text().strip(),
|
||||
"order_data": self.order_data if self.order_data else {}
|
||||
}
|
||||
413
widgets/luno_query_dialog.py
Normal file
413
widgets/luno_query_dialog.py
Normal file
@ -0,0 +1,413 @@
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from PySide6.QtWidgets import QTableWidgetItem, QMessageBox
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from ui.luno_query_dialog_ui import LunoQueryDialogUI
|
||||
from utils.sql_utils import SQLUtils
|
||||
from utils.app_mode import AppMode
|
||||
from apis.gc_api import GcApi
|
||||
|
||||
class LunoQueryDialog(LunoQueryDialogUI):
|
||||
"""炉号查询对话框,用于查询炉号信息"""
|
||||
|
||||
# 定义信号,用于向上传递选中的炉号信息
|
||||
luno_selected = Signal(dict)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__()
|
||||
|
||||
# 存储查询结果
|
||||
self.query_results = []
|
||||
|
||||
# 获取父窗口的用户信息
|
||||
self.corp_id = None
|
||||
self.user_id = None
|
||||
self.user_name = None
|
||||
if parent:
|
||||
if hasattr(parent, 'corp_id'):
|
||||
self.corp_id = parent.corp_id
|
||||
if hasattr(parent, 'user_id'):
|
||||
self.user_id = parent.user_id[0] if isinstance(parent.user_id, tuple) else parent.user_id
|
||||
if hasattr(parent, 'user_name'):
|
||||
self.user_name = parent.user_name
|
||||
|
||||
# 连接信号和槽
|
||||
self.connect_signals()
|
||||
|
||||
def connect_signals(self):
|
||||
"""连接信号和槽"""
|
||||
# 查询按钮点击事件
|
||||
self.query_button.clicked.connect(self.on_luno_query)
|
||||
|
||||
# 确认按钮点击事件
|
||||
self.confirm_button.clicked.connect(self.on_confirm)
|
||||
|
||||
# 取消按钮点击事件
|
||||
self.cancel_button.clicked.connect(self.reject)
|
||||
|
||||
# 表格双击事件
|
||||
self.result_table.cellDoubleClicked.connect(self.on_table_double_clicked)
|
||||
|
||||
def on_luno_query(self):
|
||||
"""执行炉号查询"""
|
||||
try:
|
||||
# 获取查询参数
|
||||
start_date = self.start_date.date().toString("yyyy-MM-dd")
|
||||
end_date = self.end_date.date().toString("yyyy-MM-dd")
|
||||
luno = self.luno_input.text().strip()
|
||||
material = self.material_combo.currentText()
|
||||
if material == "全部":
|
||||
material = ""
|
||||
spec = self.spec_input.text().strip()
|
||||
steel = self.steel_input.text().strip()
|
||||
|
||||
# 构建查询参数
|
||||
query_params = {
|
||||
"srch_rq1": start_date,
|
||||
"srch_rq2": end_date,
|
||||
"luono": luno,
|
||||
"cz": material,
|
||||
"gg": spec,
|
||||
"gc": steel,
|
||||
"corp_id": self.corp_id
|
||||
}
|
||||
|
||||
# 执行查询
|
||||
results = self.query_lunos(query_params)
|
||||
|
||||
# 更新表格
|
||||
self.update_result_table(results)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"炉号查询失败: {e}")
|
||||
QMessageBox.critical(self, "查询错误", f"查询炉号时发生错误: {str(e)}")
|
||||
|
||||
def query_lunos(self, params):
|
||||
"""查询炉号数据
|
||||
|
||||
Args:
|
||||
params: 查询参数字典
|
||||
|
||||
Returns:
|
||||
list: 炉号数据列表
|
||||
"""
|
||||
try:
|
||||
# 如果是API模式,优先从接口获取数据
|
||||
if AppMode.is_api():
|
||||
return self._query_lunos_from_api(params)
|
||||
else:
|
||||
return self._query_lunos_from_db(params)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"查询炉号数据失败: {e}")
|
||||
raise
|
||||
|
||||
def _query_lunos_from_api(self, params):
|
||||
"""从API获取炉号数据
|
||||
|
||||
Args:
|
||||
params: 查询参数字典
|
||||
|
||||
Returns:
|
||||
list: 炉号数据列表
|
||||
"""
|
||||
try:
|
||||
# 调用接口
|
||||
gc_api = GcApi()
|
||||
|
||||
# 调用接口查询炉号列表
|
||||
response = gc_api.get_luno_list(params)
|
||||
|
||||
# 处理接口返回结果 - 接口直接返回数据列表
|
||||
if response and isinstance(response, list):
|
||||
# 接口直接返回炉号数据列表
|
||||
lunos_data = response
|
||||
results = []
|
||||
|
||||
for luno_data in lunos_data:
|
||||
# 转换为统一格式的字典,处理接口返回的实际字段
|
||||
luno_dict = {
|
||||
"luono": luno_data.get("luono", ""),
|
||||
"cz": luno_data.get("cz", ""),
|
||||
"gg": luno_data.get("gg", ""),
|
||||
"gc": luno_data.get("gc", ""),
|
||||
"bz": luno_data.get("bz", ""),
|
||||
"data_corp": luno_data.get("data_corp", ""),
|
||||
"data_corp_name": luno_data.get("data_corp_name", ""),
|
||||
"c": luno_data.get("c", ""),
|
||||
"si": luno_data.get("si", ""),
|
||||
"mn": luno_data.get("mn", ""),
|
||||
"p": luno_data.get("p", ""),
|
||||
"s": luno_data.get("s", ""),
|
||||
"ni": luno_data.get("ni", ""),
|
||||
"cr": luno_data.get("cr", ""),
|
||||
"ti": luno_data.get("ti", ""),
|
||||
"mo": luno_data.get("mo", ""),
|
||||
"cu": luno_data.get("cu", ""),
|
||||
"others": luno_data.get("others", ""),
|
||||
"klqd": luno_data.get("klqd", ""),
|
||||
"ysl": luno_data.get("ysl", ""),
|
||||
"rq": luno_data.get("rq", "")
|
||||
}
|
||||
|
||||
# 处理null值,转换为空字符串
|
||||
for key, value in luno_dict.items():
|
||||
if value is None:
|
||||
luno_dict[key] = ""
|
||||
|
||||
results.append(luno_dict)
|
||||
|
||||
# 保存查询结果
|
||||
self.query_results = results
|
||||
|
||||
# 如果接口查询到数据,则保存到数据库中
|
||||
if results:
|
||||
self._save_lunos_to_db(results)
|
||||
|
||||
return results
|
||||
else:
|
||||
# 如果接口返回的不是列表或为空,则尝试从数据库查询
|
||||
logging.warning("从API获取炉号数据失败或返回格式不正确,尝试从数据库查询")
|
||||
return self._query_lunos_from_db(params)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"从API获取炉号数据失败: {e}")
|
||||
# 如果接口查询失败,则尝试从数据库查询
|
||||
logging.warning("从API获取炉号数据失败,尝试从数据库查询")
|
||||
return self._query_lunos_from_db(params)
|
||||
|
||||
def _save_lunos_to_db(self, lunos):
|
||||
"""将炉号数据保存到数据库
|
||||
|
||||
Args:
|
||||
lunos: 炉号数据列表
|
||||
"""
|
||||
try:
|
||||
# 导入DAO
|
||||
from dao.inspection_dao import InspectionDAO
|
||||
inspection_dao = InspectionDAO()
|
||||
|
||||
# 遍历炉号数据,保存到数据库
|
||||
for luno_data in lunos:
|
||||
luno_code = luno_data.get("luono", "")
|
||||
if luno_code:
|
||||
# 设置用户信息
|
||||
if self.user_id:
|
||||
luno_data['user_id'] = self.user_id
|
||||
if self.user_name:
|
||||
luno_data['user_name'] = self.user_name
|
||||
if self.corp_id:
|
||||
luno_data['data_corp'] = self.corp_id
|
||||
|
||||
# 保存炉号信息
|
||||
inspection_dao.save_luno_info(luno_code, luno_data)
|
||||
|
||||
logging.info(f"成功保存{len(lunos)}条炉号数据到数据库")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"保存炉号数据到数据库失败: {e}")
|
||||
|
||||
def _query_lunos_from_db(self, params):
|
||||
"""从数据库获取炉号数据
|
||||
|
||||
Args:
|
||||
params: 查询参数字典
|
||||
|
||||
Returns:
|
||||
list: 炉号数据列表
|
||||
"""
|
||||
# 构建SQL查询
|
||||
sql = """
|
||||
SELECT
|
||||
l.luono, l.cz, l.gg, l.gc, l.bz, l.data_corp, l.data_corp_name,
|
||||
l.c, l.si, l.mn, l.p, l.s, l.ni, l.cr, l.ti, l.mo, l.cu, l.others,
|
||||
l.klqd, l.ysl, l.create_time as rq
|
||||
FROM wsbz_luno_info l
|
||||
WHERE 1=1
|
||||
"""
|
||||
|
||||
query_params = []
|
||||
|
||||
# 添加查询条件
|
||||
if params.get("srch_rq1") and params.get("srch_rq2"):
|
||||
sql += " AND l.create_time BETWEEN ? AND ?"
|
||||
query_params.append(params["srch_rq1"] + " 00:00:00")
|
||||
query_params.append(params["srch_rq2"] + " 23:59:59")
|
||||
|
||||
if params.get("luono"):
|
||||
sql += " AND l.luono LIKE ?"
|
||||
query_params.append(f"%{params['luono']}%")
|
||||
|
||||
if params.get("cz"):
|
||||
sql += " AND l.cz = ?"
|
||||
query_params.append(params["cz"])
|
||||
|
||||
if params.get("gg"):
|
||||
sql += " AND l.gg LIKE ?"
|
||||
query_params.append(f"%{params['gg']}%")
|
||||
|
||||
if params.get("gc"):
|
||||
sql += " AND l.gc LIKE ?"
|
||||
query_params.append(f"%{params['gc']}%")
|
||||
|
||||
# 添加排序
|
||||
sql += " ORDER BY l.create_time DESC"
|
||||
|
||||
# 执行查询
|
||||
with SQLUtils('sqlite') as db:
|
||||
db.execute_query(sql, query_params)
|
||||
rows = db.fetchall()
|
||||
|
||||
# 处理查询结果
|
||||
results = []
|
||||
for row in rows:
|
||||
# 将元组转换为字典
|
||||
luno_data = {
|
||||
"luono": row[0],
|
||||
"cz": row[1],
|
||||
"gg": row[2],
|
||||
"gc": row[3],
|
||||
"bz": row[4],
|
||||
"data_corp": row[5],
|
||||
"data_corp_name": row[6],
|
||||
"c": row[7],
|
||||
"si": row[8],
|
||||
"mn": row[9],
|
||||
"p": row[10],
|
||||
"s": row[11],
|
||||
"ni": row[12],
|
||||
"cr": row[13],
|
||||
"ti": row[14],
|
||||
"mo": row[15],
|
||||
"cu": row[16],
|
||||
"others": row[17],
|
||||
"klqd": row[18],
|
||||
"ysl": row[19],
|
||||
"rq": row[20]
|
||||
}
|
||||
results.append(luno_data)
|
||||
|
||||
# 保存查询结果
|
||||
self.query_results = results
|
||||
|
||||
return results
|
||||
|
||||
def update_result_table(self, results):
|
||||
"""更新结果表格
|
||||
|
||||
Args:
|
||||
results: 炉号数据列表
|
||||
"""
|
||||
# 清空表格
|
||||
self.result_table.setRowCount(0)
|
||||
|
||||
if not results:
|
||||
return
|
||||
|
||||
# 定义表头和对应的字段名
|
||||
columns = [
|
||||
{"title": "序号", "field": None},
|
||||
{"title": "日期", "field": "rq"},
|
||||
{"title": "炉号", "field": "luono"},
|
||||
{"title": "材质", "field": "cz"},
|
||||
{"title": "规格", "field": "gg"},
|
||||
{"title": "钢厂", "field": "gc"},
|
||||
{"title": "标准", "field": "bz"},
|
||||
{"title": "公司账套", "field": "data_corp_name"},
|
||||
{"title": "C", "field": "c"},
|
||||
{"title": "Si", "field": "si"},
|
||||
{"title": "Mn", "field": "mn"},
|
||||
{"title": "P", "field": "p"},
|
||||
{"title": "S", "field": "s"},
|
||||
{"title": "Ni", "field": "ni"},
|
||||
{"title": "Cr", "field": "cr"},
|
||||
{"title": "Ti", "field": "ti"},
|
||||
{"title": "Mo", "field": "mo"},
|
||||
{"title": "Cu", "field": "cu"},
|
||||
{"title": "其他", "field": "others"},
|
||||
{"title": "抗拉强度", "field": "klqd"},
|
||||
{"title": "延伸率", "field": "ysl"}
|
||||
]
|
||||
|
||||
# 设置表头
|
||||
self.result_table.setColumnCount(len(columns))
|
||||
header_labels = [col["title"] for col in columns]
|
||||
self.result_table.setHorizontalHeaderLabels(header_labels)
|
||||
|
||||
# 设置行数
|
||||
self.result_table.setRowCount(len(results))
|
||||
|
||||
# 填充数据
|
||||
for row, luno_data in enumerate(results):
|
||||
# 序号
|
||||
item_index = QTableWidgetItem(str(row + 1))
|
||||
item_index.setTextAlignment(Qt.AlignCenter)
|
||||
self.result_table.setItem(row, 0, item_index)
|
||||
|
||||
# 遍历列,填充数据
|
||||
for col_idx, column in enumerate(columns[1:], 1): # 从1开始,跳过序号列
|
||||
field = column["field"]
|
||||
if field:
|
||||
value = ""
|
||||
|
||||
# 特殊处理某些字段
|
||||
if field == "rq":
|
||||
create_time = luno_data.get(field, "")
|
||||
if create_time:
|
||||
try:
|
||||
# 尝试解析日期时间字符串,支持多种格式
|
||||
if " " in create_time:
|
||||
# 包含时间的格式:2025-07-09 10:30:00
|
||||
dt = datetime.strptime(create_time, "%Y-%m-%d %H:%M:%S")
|
||||
value = dt.strftime("%Y-%m-%d")
|
||||
else:
|
||||
# 只有日期的格式:2025-07-09
|
||||
dt = datetime.strptime(create_time, "%Y-%m-%d")
|
||||
value = dt.strftime("%Y-%m-%d")
|
||||
except:
|
||||
# 如果解析失败,直接使用原值
|
||||
value = create_time
|
||||
else:
|
||||
# 其他普通字段
|
||||
value = str(luno_data.get(field, ""))
|
||||
|
||||
# 创建表格项并设置文本
|
||||
item = QTableWidgetItem(value)
|
||||
|
||||
# 居中对齐特定的列
|
||||
if field in ["rq", "c", "si", "mn", "p", "s", "ni", "cr", "ti", "mo", "cu", "klqd", "ysl"]:
|
||||
item.setTextAlignment(Qt.AlignCenter)
|
||||
|
||||
# 设置表格项
|
||||
self.result_table.setItem(row, col_idx, item)
|
||||
|
||||
# 存储原始数据到第一列的item中
|
||||
item_index.setData(Qt.UserRole, luno_data)
|
||||
|
||||
def on_table_double_clicked(self, row, column):
|
||||
"""表格双击事件处理"""
|
||||
self.select_current_row(row)
|
||||
|
||||
def on_confirm(self):
|
||||
"""确认按钮点击事件处理"""
|
||||
# 获取当前选中行
|
||||
selected_rows = self.result_table.selectionModel().selectedRows()
|
||||
if not selected_rows:
|
||||
QMessageBox.warning(self, "提示", "请选择一个炉号")
|
||||
return
|
||||
|
||||
# 获取选中行的索引
|
||||
row_index = selected_rows[0].row()
|
||||
self.select_current_row(row_index)
|
||||
|
||||
def select_current_row(self, row):
|
||||
"""选择当前行并返回数据"""
|
||||
if 0 <= row < self.result_table.rowCount():
|
||||
# 获取存储在item中的原始数据
|
||||
item = self.result_table.item(row, 0)
|
||||
if item:
|
||||
luno_data = item.data(Qt.UserRole)
|
||||
if luno_data:
|
||||
# 发出信号
|
||||
self.luno_selected.emit(luno_data)
|
||||
self.accept()
|
||||
File diff suppressed because it is too large
Load Diff
490
widgets/order_query_dialog.py
Normal file
490
widgets/order_query_dialog.py
Normal file
@ -0,0 +1,490 @@
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from PySide6.QtWidgets import QTableWidgetItem, QMessageBox
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from ui.order_query_dialog_ui import OrderQueryDialogUI
|
||||
from utils.sql_utils import SQLUtils
|
||||
from utils.app_mode import AppMode
|
||||
from apis.gc_api import GcApi
|
||||
|
||||
class OrderQueryDialog(OrderQueryDialogUI):
|
||||
"""订单查询对话框,用于查询订单信息"""
|
||||
|
||||
# 定义信号,用于向上传递选中的订单信息
|
||||
order_selected = Signal(dict)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__()
|
||||
|
||||
# 存储查询结果
|
||||
self.query_results = []
|
||||
|
||||
# 获取父窗口的用户信息
|
||||
self.corp_id = None
|
||||
self.user_id = None
|
||||
self.user_name = None
|
||||
if parent:
|
||||
if hasattr(parent, 'corp_id'):
|
||||
self.corp_id = parent.corp_id
|
||||
if hasattr(parent, 'user_id'):
|
||||
self.user_id = parent.user_id[0]
|
||||
if hasattr(parent, 'user_name'):
|
||||
self.user_name = parent.user_name
|
||||
|
||||
# 连接信号和槽
|
||||
self.connect_signals()
|
||||
|
||||
def connect_signals(self):
|
||||
"""连接信号和槽"""
|
||||
# 查询按钮点击事件
|
||||
self.query_button.clicked.connect(self.on_order_query)
|
||||
|
||||
# 确认按钮点击事件
|
||||
self.confirm_button.clicked.connect(self.on_confirm)
|
||||
|
||||
# 取消按钮点击事件
|
||||
self.cancel_button.clicked.connect(self.reject)
|
||||
|
||||
# 表格双击事件
|
||||
self.result_table.cellDoubleClicked.connect(self.on_table_double_clicked)
|
||||
|
||||
def on_order_query(self):
|
||||
"""执行订单查询"""
|
||||
try:
|
||||
# 获取查询参数
|
||||
start_date = self.start_date.date().toString("yyyy-MM-dd")
|
||||
end_date = self.end_date.date().toString("yyyy-MM-dd")
|
||||
order_mo = self.order_mo_input.text().strip()
|
||||
material = self.material_combo.currentText()
|
||||
if material == "全部":
|
||||
material = ""
|
||||
spec = self.spec_input.text().strip()
|
||||
|
||||
# 构建查询参数
|
||||
query_params = {
|
||||
"srch_rq1": start_date,
|
||||
"srch_rq2": end_date,
|
||||
"srch_mo": order_mo,
|
||||
"srch_note": order_mo,
|
||||
"srch_cz": material,
|
||||
"srch_size": spec,
|
||||
"corp_id": self.corp_id
|
||||
}
|
||||
|
||||
# 执行查询
|
||||
results = self.query_orders(query_params)
|
||||
|
||||
# 更新表格
|
||||
self.update_result_table(results)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"订单查询失败: {e}")
|
||||
QMessageBox.critical(self, "查询错误", f"查询订单时发生错误: {str(e)}")
|
||||
|
||||
def query_orders(self, params):
|
||||
"""查询订单数据
|
||||
|
||||
Args:
|
||||
params: 查询参数字典
|
||||
|
||||
Returns:
|
||||
list: 订单数据列表
|
||||
"""
|
||||
try:
|
||||
# 如果是API模式,优先从接口获取数据
|
||||
if AppMode.is_api():
|
||||
return self._query_orders_from_api(params)
|
||||
else:
|
||||
return self._query_orders_from_db(params)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"查询订单数据失败: {e}")
|
||||
raise
|
||||
|
||||
def _query_orders_from_api(self, params):
|
||||
"""从API获取订单数据
|
||||
|
||||
Args:
|
||||
params: 查询参数字典
|
||||
|
||||
Returns:
|
||||
list: 订单数据列表
|
||||
"""
|
||||
try:
|
||||
# 调用接口
|
||||
gc_api = GcApi()
|
||||
|
||||
# 调用接口查询订单列表
|
||||
response = gc_api.get_order_list(params)
|
||||
|
||||
# 处理接口返回结果
|
||||
if response and response.get("status", False):
|
||||
# 将接口返回的数据转换为标准格式
|
||||
orders_data = response.get("data", [])
|
||||
results = []
|
||||
|
||||
for order_data in orders_data:
|
||||
# 转换为统一格式的字典
|
||||
order_dict = {
|
||||
"note": order_data.get("note", ""),
|
||||
"mo": order_data.get("mo", ""),
|
||||
"customer": order_data.get("customer", ""),
|
||||
"customerexp": order_data.get("customerexp", ""),
|
||||
"khno": order_data.get("khno", ""),
|
||||
"ddzl": order_data.get("ddzl", ""),
|
||||
"khjq": order_data.get("khjq", ""),
|
||||
"code": order_data.get("code", ""),
|
||||
"type": order_data.get("type", ""),
|
||||
"cz": order_data.get("cz", ""),
|
||||
"size": order_data.get("size", ""),
|
||||
"cd": order_data.get("cd", ""),
|
||||
"maxsl": order_data.get("maxsl", ""),
|
||||
"spack": order_data.get("spack", ""),
|
||||
"zx_name": order_data.get("zx_name", ""),
|
||||
"zx_code": order_data.get("zx_code", ""),
|
||||
"zx_zl": order_data.get("zx_zl", ""),
|
||||
"template_name": order_data.get("template_name", ""),
|
||||
"bqlb": order_data.get("bqlb", ""),
|
||||
# 删除重复的khno字段
|
||||
"dycz": order_data.get("dycz", ""),
|
||||
"luno": order_data.get("luno", ""),
|
||||
"corp": order_data.get("corp", ""),
|
||||
"sl": order_data.get("sl", ""),
|
||||
"tccd": order_data.get("tccd", ""),
|
||||
"bccd": order_data.get("bccd", ""),
|
||||
"ysl": order_data.get("ysl", ""),
|
||||
"qfqd": order_data.get("qfqd", ""),
|
||||
"yzgg": order_data.get("yzgg", ""),
|
||||
"tqd": order_data.get("tqd", ""),
|
||||
"bqd": order_data.get("bqd", ""),
|
||||
"bzfs": order_data.get("bzfs", ""),
|
||||
"ddyq": order_data.get("ddyq", ""),
|
||||
"remarks_hb": order_data.get("remarks_hb", ""),
|
||||
"bz_tqd": order_data.get("bz_tqd", ""),
|
||||
"bz_bqd": order_data.get("bz_bqd", ""),
|
||||
"zzyq": order_data.get("zzyq", ""),
|
||||
"rq": order_data.get("rq", "")
|
||||
}
|
||||
results.append(order_dict)
|
||||
|
||||
# 保存查询结果
|
||||
self.query_results = results
|
||||
|
||||
# 如果接口查询到数据,则保存到数据库中
|
||||
if results:
|
||||
self._save_orders_to_db(results)
|
||||
|
||||
return results
|
||||
else:
|
||||
# 如果接口查询失败,则尝试从数据库查询
|
||||
logging.warning("从API获取订单数据失败,尝试从数据库查询")
|
||||
return self._query_orders_from_db(params)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"从API获取订单数据失败: {e}")
|
||||
# 如果接口查询失败,则尝试从数据库查询
|
||||
logging.warning("从API获取订单数据失败,尝试从数据库查询")
|
||||
return self._query_orders_from_db(params)
|
||||
|
||||
def _save_orders_to_db(self, orders):
|
||||
"""将订单数据保存到数据库
|
||||
|
||||
Args:
|
||||
orders: 订单数据列表
|
||||
"""
|
||||
try:
|
||||
# 导入DAO
|
||||
from dao.inspection_dao import InspectionDAO
|
||||
inspection_dao = InspectionDAO()
|
||||
|
||||
# 遍历订单数据,保存到数据库
|
||||
for order_data in orders:
|
||||
order_code = order_data.get("note", "")
|
||||
if order_code:
|
||||
# 设置用户信息
|
||||
if self.user_id:
|
||||
order_data['user_id'] = self.user_id
|
||||
if self.user_name:
|
||||
order_data['user_name'] = self.user_name
|
||||
if self.corp_id:
|
||||
order_data['data_corp'] = self.corp_id
|
||||
|
||||
# 保存订单信息
|
||||
inspection_dao.save_order_info(order_code, order_data)
|
||||
|
||||
logging.info(f"成功保存{len(orders)}条订单数据到数据库")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"保存订单数据到数据库失败: {e}")
|
||||
|
||||
def _query_orders_from_db(self, params):
|
||||
"""从数据库获取订单数据
|
||||
|
||||
Args:
|
||||
params: 查询参数字典
|
||||
|
||||
Returns:
|
||||
list: 订单数据列表
|
||||
"""
|
||||
# 构建SQL查询
|
||||
sql = """
|
||||
SELECT
|
||||
o.note, o.mo, o.customer, o.customerexp, o.khno, o.ddzl, o.khjq, o.code, o.type,
|
||||
o.cz, o.size, o.cd, o.maxsl, o.spack, o.zx_name, o.zx_code, o.zx_zl,
|
||||
o.template_name, o.bqlb, o.dycz, o.luno, o.corp, o.sl, o.tccd, o.bccd,
|
||||
o.ysl, o.qfqd, o.yzgg, o.tqd, o.bqd, o.bzfs, o.ddyq, o.remarks_hb,
|
||||
o.bz_tqd, o.bz_bqd, o.zzyq, o.create_time as rq
|
||||
FROM wsbz_order_info o
|
||||
WHERE 1=1
|
||||
"""
|
||||
|
||||
query_params = []
|
||||
|
||||
# 添加查询条件
|
||||
if params.get("srch_rq1") and params.get("srch_rq2"):
|
||||
sql += " AND o.create_time BETWEEN ? AND ?"
|
||||
query_params.append(params["srch_rq1"] + " 00:00:00")
|
||||
query_params.append(params["srch_rq2"] + " 23:59:59")
|
||||
|
||||
if params.get("srch_mo"):
|
||||
sql += " AND (o.mo LIKE ? OR o.note LIKE ?)"
|
||||
query_params.append(f"%{params['srch_mo']}%")
|
||||
query_params.append(f"%{params['srch_mo']}%")
|
||||
|
||||
if params.get("srch_cz"):
|
||||
sql += " AND o.cz = ?"
|
||||
query_params.append(params["srch_cz"])
|
||||
|
||||
if params.get("srch_size"):
|
||||
sql += " AND o.size LIKE ?"
|
||||
query_params.append(f"%{params['srch_size']}%")
|
||||
|
||||
# 添加排序
|
||||
sql += " ORDER BY o.create_time DESC"
|
||||
|
||||
# 执行查询
|
||||
with SQLUtils('sqlite') as db:
|
||||
db.execute_query(sql, query_params)
|
||||
rows = db.fetchall()
|
||||
|
||||
# 处理查询结果
|
||||
results = []
|
||||
for row in rows:
|
||||
# 将元组转换为字典,使用安全的索引访问
|
||||
try:
|
||||
order_data = {
|
||||
"note": row[0] if len(row) > 0 else "",
|
||||
"mo": row[1] if len(row) > 1 else "",
|
||||
"customer": row[2] if len(row) > 2 else "",
|
||||
"customerexp": row[3] if len(row) > 3 else "",
|
||||
"khno": row[4] if len(row) > 4 else "",
|
||||
"ddzl": row[5] if len(row) > 5 else "",
|
||||
"khjq": row[6] if len(row) > 6 else "",
|
||||
"code": row[7] if len(row) > 7 else "",
|
||||
"type": row[8] if len(row) > 8 else "",
|
||||
"cz": row[9] if len(row) > 9 else "",
|
||||
"size": row[10] if len(row) > 10 else "",
|
||||
"cd": row[11] if len(row) > 11 else "",
|
||||
"maxsl": row[12] if len(row) > 12 else "",
|
||||
"spack": row[13] if len(row) > 13 else "",
|
||||
"zx_name": row[14] if len(row) > 14 else "",
|
||||
"zx_code": row[15] if len(row) > 15 else "",
|
||||
"zx_zl": row[16] if len(row) > 16 else "",
|
||||
"template_name": row[17] if len(row) > 17 else "",
|
||||
"bqlb": row[18] if len(row) > 18 else "",
|
||||
"dycz": row[19] if len(row) > 19 else "",
|
||||
"luno": row[20] if len(row) > 20 else "",
|
||||
"corp": row[21] if len(row) > 21 else "",
|
||||
"sl": row[22] if len(row) > 22 else "",
|
||||
"tccd": row[23] if len(row) > 23 else "",
|
||||
"bccd": row[24] if len(row) > 24 else "",
|
||||
"ysl": row[25] if len(row) > 25 else "",
|
||||
"qfqd": row[26] if len(row) > 26 else "",
|
||||
"yzgg": row[27] if len(row) > 27 else "",
|
||||
"tqd": row[28] if len(row) > 28 else "",
|
||||
"bqd": row[29] if len(row) > 29 else "",
|
||||
"bzfs": row[30] if len(row) > 30 else "",
|
||||
"ddyq": row[31] if len(row) > 31 else "",
|
||||
"remarks_hb": row[32] if len(row) > 32 else "",
|
||||
"bz_tqd": row[33] if len(row) > 33 else "",
|
||||
"bz_bqd": row[34] if len(row) > 34 else "",
|
||||
"zzyq": row[35] if len(row) > 35 else "",
|
||||
"rq": row[36] if len(row) > 36 else ""
|
||||
}
|
||||
results.append(order_data)
|
||||
except Exception as e:
|
||||
logging.error(f"处理查询结果行时出错: {str(e)}, 行数据: {row}")
|
||||
continue
|
||||
|
||||
# 保存查询结果
|
||||
self.query_results = results
|
||||
|
||||
return results
|
||||
|
||||
def query_orders_xpack(self, params):
|
||||
"""通过托盘号查订单数据,返回列表"""
|
||||
try:
|
||||
gc_api = GcApi()
|
||||
response = gc_api.get_order_info_by_xpack(params.get("srch_spack"), params.get("corp_id"))
|
||||
if response and response.get("status", False):
|
||||
orders_data = response.get("data", [])
|
||||
results = []
|
||||
for order_data in orders_data:
|
||||
results.append(order_data)
|
||||
self.query_results = results
|
||||
return results
|
||||
else:
|
||||
return []
|
||||
except Exception as e:
|
||||
logging.error(f"通过托盘号查订单数据失败: {e}")
|
||||
return []
|
||||
|
||||
def update_result_table(self, results):
|
||||
"""更新结果表格
|
||||
|
||||
Args:
|
||||
results: 订单数据列表
|
||||
"""
|
||||
# 清空表格
|
||||
self.result_table.setRowCount(0)
|
||||
|
||||
if not results:
|
||||
return
|
||||
|
||||
# 定义表头和对应的字段名(完整列表)
|
||||
columns = [
|
||||
{"title": "序号", "field": None},
|
||||
{"title": "日期", "field": "rq"},
|
||||
{"title": "订单号", "field": "note"},
|
||||
{"title": "订单明细", "field": "mo"},
|
||||
{"title": "客户", "field": "customer"},
|
||||
{"title": "客户名称", "field": "customerexp"},
|
||||
{"title": "客户订单号", "field": "khno"}, # 修改标题以区分
|
||||
{"title": "订单类别", "field": "ddzl"},
|
||||
{"title": "客户交期", "field": "khjq"},
|
||||
{"title": "编码", "field": "code"},
|
||||
{"title": "产品类别", "field": "type"},
|
||||
{"title": "材质", "field": "cz"},
|
||||
{"title": "规格", "field": "size"},
|
||||
{"title": "产地", "field": "cd"},
|
||||
{"title": "最大入库量", "field": "maxsl"},
|
||||
{"title": "托盘号", "field": "spack"},
|
||||
{"title": "轴型", "field": "zx_name"},
|
||||
{"title": "轴型code", "field": "zx_code"},
|
||||
{"title": "轴型重量", "field": "zx_zl"},
|
||||
{"title": "标签类别", "field": "template_name"},
|
||||
{"title": "标签类别code", "field": "bqlb"},
|
||||
# 删除重复的"客户实际订单号"列
|
||||
{"title": "打印材质", "field": "dycz"},
|
||||
{"title": "炉号", "field": "luno"},
|
||||
{"title": "公司", "field": "corp"},
|
||||
{"title": "数量", "field": "sl"},
|
||||
{"title": "上公差", "field": "tccd"},
|
||||
{"title": "下公差", "field": "bccd"},
|
||||
{"title": "延伸率", "field": "ysl"},
|
||||
{"title": "屈服强度", "field": "qfqd"},
|
||||
{"title": "英制规格", "field": "yzgg"},
|
||||
{"title": "强度上限", "field": "tqd"},
|
||||
{"title": "强度下限", "field": "bqd"},
|
||||
{"title": "包装方式", "field": "bzfs"},
|
||||
{"title": "订单要求", "field": "ddyq"},
|
||||
{"title": "备注", "field": "remarks_hb"},
|
||||
{"title": "包装强度上限", "field": "bz_tqd"},
|
||||
{"title": "包装强度下限", "field": "bz_bqd"},
|
||||
{"title": "轴重要求", "field": "zzyq"}
|
||||
]
|
||||
|
||||
# 设置表头
|
||||
self.result_table.setColumnCount(len(columns))
|
||||
header_labels = [col["title"] for col in columns]
|
||||
self.result_table.setHorizontalHeaderLabels(header_labels)
|
||||
|
||||
# 设置行数
|
||||
self.result_table.setRowCount(len(results))
|
||||
|
||||
# 填充数据
|
||||
for row, order_data in enumerate(results):
|
||||
# 序号
|
||||
item_index = QTableWidgetItem(str(row + 1))
|
||||
item_index.setTextAlignment(Qt.AlignCenter)
|
||||
self.result_table.setItem(row, 0, item_index)
|
||||
|
||||
# 遍历列,填充数据
|
||||
for col_idx, column in enumerate(columns[1:], 1): # 从1开始,跳过序号列
|
||||
field = column["field"]
|
||||
if field:
|
||||
value = ""
|
||||
|
||||
# 特殊处理某些字段
|
||||
if field == "rq":
|
||||
create_time = order_data.get(field, "")
|
||||
if create_time:
|
||||
try:
|
||||
# 尝试解析日期时间字符串
|
||||
dt = datetime.strptime(create_time, "%Y-%m-%d %H:%M:%S")
|
||||
value = dt.strftime("%Y-%m-%d")
|
||||
except:
|
||||
value = create_time
|
||||
else:
|
||||
# 其他普通字段
|
||||
value = str(order_data.get(field, ""))
|
||||
|
||||
# 创建表格项并设置文本
|
||||
item = QTableWidgetItem(value)
|
||||
|
||||
# 居中对齐特定的列
|
||||
if field in ["rq", "sl", "maxsl", "zx_zl"]:
|
||||
item.setTextAlignment(Qt.AlignCenter)
|
||||
|
||||
# 设置表格项
|
||||
self.result_table.setItem(row, col_idx, item)
|
||||
|
||||
# 存储原始数据到第一列的item中
|
||||
item_index.setData(Qt.UserRole, order_data)
|
||||
|
||||
def on_table_double_clicked(self, row, column):
|
||||
"""表格双击事件处理"""
|
||||
self.select_current_row(row)
|
||||
|
||||
def on_confirm(self):
|
||||
"""确认按钮点击事件处理"""
|
||||
# 获取当前选中行
|
||||
selected_rows = self.result_table.selectionModel().selectedRows()
|
||||
if not selected_rows:
|
||||
QMessageBox.warning(self, "提示", "请选择一个订单")
|
||||
return
|
||||
|
||||
# 获取选中行的索引
|
||||
row_index = selected_rows[0].row()
|
||||
self.select_current_row(row_index)
|
||||
|
||||
def select_current_row(self, row):
|
||||
"""选择当前行并返回数据
|
||||
|
||||
将订单明细(mo)作为订单号传递给上层组件,并自动获取托盘号
|
||||
"""
|
||||
if 0 <= row < self.result_table.rowCount():
|
||||
# 获取存储在item中的原始数据
|
||||
item = self.result_table.item(row, 0)
|
||||
if item:
|
||||
order_data = item.data(Qt.UserRole)
|
||||
if order_data:
|
||||
# 修改订单数据,将mo字段作为note字段的值
|
||||
mo_value = order_data.get("mo", "")
|
||||
if mo_value:
|
||||
order_data["note"] = mo_value
|
||||
# 自动获取托盘号
|
||||
order_code = mo_value
|
||||
if order_code and self.corp_id:
|
||||
gc_api = GcApi()
|
||||
xpack_response = gc_api.get_xpack(order_code, self.corp_id)
|
||||
if xpack_response and xpack_response.get("status", False):
|
||||
xpack = xpack_response.get("xpack", "")
|
||||
spack = xpack_response.get("spack", "")
|
||||
order_data["xpack"] = xpack
|
||||
order_data["spack"] = spack
|
||||
|
||||
logging.info(f"已获取spack: {spack},已添加到order_data中")
|
||||
# 发出信号
|
||||
self.order_selected.emit(order_data)
|
||||
self.accept()
|
||||
@ -1,14 +1,17 @@
|
||||
from PySide6.QtWidgets import QMessageBox
|
||||
from PySide6.QtWidgets import QMessageBox, QFileDialog, QLineEdit, QTableWidgetItem
|
||||
from PySide6.QtCore import Qt
|
||||
from ui.report_dialog_ui import ReportDialogUI
|
||||
import pandas as pd
|
||||
from datetime import datetime
|
||||
import logging
|
||||
import os
|
||||
from dao.report_dao import ReportDAO
|
||||
|
||||
class ReportDialog(ReportDialogUI):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.init_signals()
|
||||
self.current_data = [] # 存储当前查询的数据
|
||||
|
||||
def init_signals(self):
|
||||
"""初始化信号连接"""
|
||||
@ -21,34 +24,59 @@ class ReportDialog(ReportDialogUI):
|
||||
# 关闭按钮点击事件
|
||||
self.close_button.clicked.connect(self.close)
|
||||
|
||||
# 清空条件按钮点击事件
|
||||
self.clear_button.clicked.connect(self.on_clear)
|
||||
|
||||
def on_clear(self):
|
||||
"""清空条件按钮点击处理"""
|
||||
# 重置日期为默认值
|
||||
self.start_date.setDate(datetime.now().date().addMonths(-1))
|
||||
self.end_date.setDate(datetime.now().date())
|
||||
|
||||
# 清空其他输入框
|
||||
self.customer_edit.clear()
|
||||
self.material_edit.clear()
|
||||
self.spec_edit.clear()
|
||||
|
||||
# 清空表格和汇总信息
|
||||
self.report_table.setRowCount(0)
|
||||
self.update_summary([])
|
||||
|
||||
def on_query(self):
|
||||
"""查询按钮点击处理"""
|
||||
try:
|
||||
# 获取查询条件
|
||||
start_date = self.start_date.date().toString(Qt.ISODate)
|
||||
end_date = self.end_date.date().toString(Qt.ISODate)
|
||||
report_type = self.type_combo.currentText()
|
||||
start_date = self.start_date.date().toString("yyyy-MM-dd")
|
||||
end_date = self.end_date.date().toString("yyyy-MM-dd")
|
||||
customer = self.customer_edit.text().strip()
|
||||
material = self.material_edit.text().strip()
|
||||
spec = self.spec_edit.text().strip()
|
||||
|
||||
# TODO: 根据条件从数据库查询数据
|
||||
# 这里需要实现具体的查询逻辑
|
||||
# 使用ReportDAO获取数据
|
||||
report_dao = ReportDAO()
|
||||
data = report_dao.get_production_report(
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
customer=customer if customer else None,
|
||||
material=material if material else None,
|
||||
spec=spec if spec else None
|
||||
)
|
||||
|
||||
# 示例数据
|
||||
data = [
|
||||
{
|
||||
"日期": "2024-03-20",
|
||||
"工程号": "GC001",
|
||||
"品名": "产品A",
|
||||
"规格": "规格1",
|
||||
"生产数量": 100,
|
||||
"合格数量": 95,
|
||||
"不合格数量": 5,
|
||||
"合格率": "95%"
|
||||
}
|
||||
]
|
||||
# 保存当前数据用于导出
|
||||
self.current_data = data
|
||||
|
||||
# 更新表格显示
|
||||
self.update_table(data)
|
||||
|
||||
# 显示查询结果数量
|
||||
if data:
|
||||
if self.statusBar():
|
||||
self.statusBar().showMessage(f"查询到 {len(data)} 条记录")
|
||||
else:
|
||||
if self.statusBar():
|
||||
self.statusBar().showMessage("未查询到符合条件的记录")
|
||||
QMessageBox.information(self, "提示", "未查询到符合条件的记录")
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"查询报表数据失败: {str(e)}")
|
||||
QMessageBox.warning(self, "错误", f"查询数据失败: {str(e)}")
|
||||
@ -62,44 +90,91 @@ class ReportDialog(ReportDialogUI):
|
||||
# 清空表格
|
||||
self.report_table.setRowCount(0)
|
||||
|
||||
if not data:
|
||||
# 如果没有数据,清空汇总信息
|
||||
self.update_summary([])
|
||||
return
|
||||
|
||||
# 添加数据行
|
||||
for row_idx, row_data in enumerate(data):
|
||||
self.report_table.insertRow(row_idx)
|
||||
for col_idx, (key, value) in enumerate(row_data.items()):
|
||||
|
||||
# 按照表头顺序添加数据
|
||||
headers = ["日期", "客户", "订单号", "轴数", "材质", "规格", "净重"]
|
||||
for col_idx, header in enumerate(headers):
|
||||
value = row_data.get(header, "")
|
||||
item = QTableWidgetItem(str(value))
|
||||
item.setTextAlignment(Qt.AlignCenter)
|
||||
self.report_table.setItem(row_idx, col_idx, item)
|
||||
|
||||
# 更新汇总信息
|
||||
self.update_summary(data)
|
||||
|
||||
def update_summary(self, data):
|
||||
"""更新汇总信息
|
||||
|
||||
Args:
|
||||
data: 包含报表数据的列表
|
||||
"""
|
||||
if not data:
|
||||
# 重置汇总信息
|
||||
self.total_axes_value.setText("0")
|
||||
self.total_weight_value.setText("0.00 kg")
|
||||
self.record_count_value.setText("0")
|
||||
return
|
||||
|
||||
# 计算总轴数
|
||||
total_axes = sum(row.get("轴数", 0) for row in data)
|
||||
|
||||
# 计算总净重
|
||||
total_weight = sum(float(row.get("净重", 0)) for row in data)
|
||||
|
||||
# 计算记录数
|
||||
record_count = len(data)
|
||||
|
||||
# 更新显示
|
||||
self.total_axes_value.setText(str(total_axes))
|
||||
self.total_weight_value.setText(f"{total_weight:.2f} kg")
|
||||
self.record_count_value.setText(str(record_count))
|
||||
|
||||
def on_export(self):
|
||||
"""导出按钮点击处理"""
|
||||
try:
|
||||
# 获取表格数据
|
||||
data = []
|
||||
for row in range(self.report_table.rowCount()):
|
||||
row_data = {}
|
||||
for col in range(self.report_table.columnCount()):
|
||||
header = self.report_table.horizontalHeaderItem(col).text()
|
||||
item = self.report_table.item(row, col)
|
||||
value = item.text() if item else ""
|
||||
row_data[header] = value
|
||||
data.append(row_data)
|
||||
|
||||
if not data:
|
||||
if not self.current_data:
|
||||
QMessageBox.warning(self, "警告", "没有数据可以导出")
|
||||
return
|
||||
|
||||
# 创建DataFrame
|
||||
df = pd.DataFrame(data)
|
||||
df = pd.DataFrame(self.current_data)
|
||||
|
||||
# 生成文件名
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
filename = f"统计报表_{timestamp}.xlsx"
|
||||
# 让用户选择保存路径
|
||||
default_name = f"统计报表_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
|
||||
filename, _ = QFileDialog.getSaveFileName(
|
||||
self, "保存报表", default_name, "Excel 文件 (*.xlsx);;所有文件 (*)"
|
||||
)
|
||||
|
||||
if not filename: # 用户取消了保存
|
||||
return
|
||||
|
||||
# 确保文件名有正确的扩展名
|
||||
if not filename.endswith('.xlsx'):
|
||||
filename += '.xlsx'
|
||||
|
||||
# 导出到Excel
|
||||
df.to_excel(filename, index=False, engine='openpyxl')
|
||||
|
||||
# 显示成功消息并打开文件所在目录
|
||||
QMessageBox.information(self, "成功", f"报表已导出到: {filename}")
|
||||
|
||||
# 打开文件所在目录
|
||||
os.system(f'explorer /select,"{os.path.normpath(filename)}"')
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"导出报表失败: {str(e)}")
|
||||
QMessageBox.warning(self, "错误", f"导出报表失败: {str(e)}")
|
||||
QMessageBox.warning(self, "错误", f"导出报表失败: {str(e)}")
|
||||
|
||||
def statusBar(self):
|
||||
"""获取状态栏,如果父窗口有状态栏则使用父窗口的状态栏"""
|
||||
if self.parent() and hasattr(self.parent(), 'statusBar'):
|
||||
return self.parent().statusBar()
|
||||
return None
|
||||
@ -178,20 +178,13 @@ class UnloadingDialog(UnloadingDialogUI):
|
||||
main_window = self.parent
|
||||
if main_window and isinstance(main_window, MainWindow):
|
||||
# 检查托盘号是否已存在
|
||||
existed = False
|
||||
for i in range(main_window.tray_edit.count()):
|
||||
if main_window.tray_edit.itemText(i) == tray_code:
|
||||
existed = True
|
||||
break
|
||||
|
||||
# 如果不存在,则添加
|
||||
if not existed:
|
||||
logging.info(f"添加托盘号到主窗口: {tray_code}")
|
||||
main_window.tray_edit.addItem(tray_code)
|
||||
|
||||
# 设置当前选中的托盘号
|
||||
main_window.tray_edit.setCurrentText(tray_code)
|
||||
# 由于 tray_edit 现在是 QLineEdit,不再需要检查是否存在
|
||||
# 直接设置文本
|
||||
logging.info(f"设置主窗口当前托盘号: {tray_code}")
|
||||
main_window.tray_edit.setText(tray_code)
|
||||
|
||||
# 手动触发托盘号变更事件
|
||||
main_window.handle_tray_changed(tray_code)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"查询托盘信息异常: {str(e)}")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user