feat: add column sorting functionality to material validation table

Add sorting capability for "选择" and "材料名称" columns in the CheckboxTreeview:
- Click column header to cycle through: asc (↑) → desc (↓) → unsort
- Sort "选择" column by checkbox state (checked/unchecked)
- Sort "材料名称" column alphabetically
- Preserve checkbox states during sorting using move() instead of delete+insert
- Separate event handlers for cell clicks and heading clicks

Implementation details:
- Added 6 new helper methods to CheckboxTreeview class
- Store original headings to properly display sort arrows
- Use identify_region() to distinguish between cell and heading clicks
- Column index conversion (#1/#2) to column identifiers

The sorting is a pure frontend feature with no database changes.
Available to all users without permission restrictions.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-02-24 15:45:55 +08:00
parent 4fcb29f488
commit 0644bdfb11
4 changed files with 820 additions and 0 deletions

View File

@@ -0,0 +1,145 @@
# 排序功能快速参考
## 用户使用指南
### 如何使用排序功能
1. **点击列头排序**
- 点击"选择"或"材料名称"列头
- 第一次点击:升序排列(显示 ↑)
- 第二次点击:降序排列(显示 ↓)
- 第三次点击:取消排序(箭头消失)
2. **切换排序列**
- 点击其他可排序列的列头
- 原列的排序箭头自动消失
- 新列显示排序箭头
3. **排序时复选框状态**
- 排序操作不会改变复选框的选中状态
- 所有项目的复选框状态在排序后保持不变
### 支持的列
**可排序**:
- 选择
- 材料名称
**不可排序**:
- 材料代码
- 规格
- 型号
- 负责人
## 开发者参考
### 核心方法
| 方法 | 功能 |
|------|------|
| `_store_original_headings()` | 存储原始列标题 |
| `_get_column_id_from_column_index()` | 列索引转列标识符 |
| `_on_heading_click()` | 处理表头点击事件 |
| `_toggle_sort()` | 切换排序状态 |
| `_sort_by_column()` | 执行排序操作 |
| `_update_heading_display()` | 更新列标题显示 |
### 排序状态变量
```python
self.sort_column = None # 当前排序列('选择' 或 '材料名称'
self.sort_direction = None # 排序方向('asc', 'desc', 或 None
self.sortable_columns = ["选择", "材料名称"] # 可排序列白名单
self.original_headings = {} # 原始列标题文本
```
### 扩展排序到其他列
如果要添加新的可排序列,修改 `sortable_columns` 列表:
```python
self.sortable_columns = ["选择", "材料名称", "材料代码", "负责人"]
```
然后在 `_sort_by_column()` 方法中添加对应的排序逻辑:
```python
elif column_id == "材料代码":
items_data.sort(
key=lambda x: str(x['values'][2]) if len(x['values']) > 2 else "",
reverse=(direction == 'desc')
)
elif column_id == "负责人":
items_data.sort(
key=lambda x: str(x['values'][5]) if len(x['values']) > 5 else "",
reverse=(direction == 'desc')
)
```
### 排序逻辑
**"选择"列**:
```python
# 按复选框状态排序
items_data.sort(key=lambda x: x['checked'], reverse=(direction == 'desc'))
```
**"材料名称"列**:
```python
# 按字符串排序
items_data.sort(
key=lambda x: str(x['values'][1]) if len(x['values']) > 1 else "",
reverse=(direction == 'desc')
)
```
### 保持复选框状态的关键
使用 `move()` 方法而不是 `delete()` + `insert()`
```python
# ✅ 正确:保留复选框状态
self.move(item_data['item_id'], '', 'end')
# ❌ 错误:会丢失复选框状态
# self.delete(item)
# self.insert("", tk.END, values=values)
```
## 故障排查
### 问题:点击列头没有反应
**可能原因**:
1. 点击的不是可排序列
2. 表格为空
**解决方法**:
- 确保点击的是"选择"或"材料名称"列
- 确保表格中有数据
### 问题:排序后复选框状态丢失
**可能原因**:
使用了 `delete()` + `insert()` 而不是 `move()`
**解决方法**:
检查 `_sort_by_column()` 方法中使用的是 `move()` 而不是 `delete()`
### 问题:排序箭头显示不正确
**可能原因**:
原始列标题没有正确存储
**解决方法**:
检查 `_store_original_headings()` 是否被正确调用延迟100ms
## 测试命令
```bash
# 运行排序功能测试
python tests/test_sorting.py
# 语法检查
python -m py_compile gui/material_validation_tab.py
```