# Design Document: UI Enhancements
## Overview
本设计对现有系统进行多项UI增强和优化:
1. 全局隐藏所有表格中的ID列,使界面更简洁
2. 工作记录列表增加结算状态筛选功能
3. 仪表盘年度汇总结算状态显示优化
4. 导出报表中未结算状态的视觉标注
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ Frontend (React) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ List Components (5个) │ │
│ │ - PersonList.jsx: 移除ID列 │ │
│ │ - ItemList.jsx: 移除ID列 │ │
│ │ - SupplierList.jsx: 移除ID列 │ │
│ │ - WorkRecordList.jsx: 移除ID列 + 增加结算筛选 │ │
│ │ - AdminList.jsx: 移除ID列 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Dashboard.jsx │ │
│ │ - 年度汇总: 合并结算状态列,调整位置 │ │
│ │ - 人员按供应商明细: 已结算状态黑色字体 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Backend (Flask) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ work_record_service.py │ │
│ │ - get_all(): 支持 is_settled 筛选参数 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ export_service.py │ │
│ │ - 年度汇总: 未结算列底色标注 │ │
│ │ - 明细表: 未结算行底色标注 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## Components and Interfaces
### Frontend Component Changes
#### 1. List Components - 隐藏ID列
需要修改的组件:
- `PersonList.jsx` - 移除 ID 列定义
- `ItemList.jsx` - 移除 ID 列定义
- `SupplierList.jsx` - 移除 ID 列定义
- `WorkRecordList.jsx` - 移除 ID 列定义
- `AdminList.jsx` - 移除 ID 列定义
修改方式:从 `columns` 数组中删除 `{ title: 'ID', dataIndex: 'id', ... }` 对象。
#### 2. WorkRecordList - 增加结算状态筛选
在筛选区域增加结算状态下拉框:
```jsx
// 新增状态
const [selectedSettlement, setSelectedSettlement] = useState(null)
// 筛选选项
const settlementOptions = [
{ label: '全部', value: null },
{ label: '已结算', value: true },
{ label: '未结算', value: false }
]
// 在筛选区域增加
```
API调用时传递 `is_settled` 参数:
```javascript
if (selectedSettlement !== null) {
params.is_settled = selectedSettlement
}
```
#### 3. Dashboard - 年度汇总结算状态优化
将"已结算"和"未结算"两列合并为单一"结算状态"列,放在"人员"列后面:
```jsx
const yearlySummaryColumns = [
{
title: '人员',
dataIndex: 'person_name',
key: 'person_name',
fixed: 'left',
width: 100
},
{
title: '结算状态',
key: 'settlement_status',
width: 180,
render: (_, record) => (
已结算: ¥{(record.settled_total || 0).toFixed(2)} /
未结算: ¥{(record.unsettled_total || 0).toFixed(2)}
)
},
// ... 月份列 ...
{
title: '年度合计',
dataIndex: 'yearly_total',
// ...
}
]
```
#### 4. Dashboard - 人员按供应商明细已结算状态字体颜色
修改 `supplierBreakdownColumns` 中的结算状态列渲染:
```jsx
{
title: '结算状态',
dataIndex: 'is_settled',
key: 'is_settled',
align: 'center',
render: (value) => value ? (
已结算 // 黑色
) : (
未结算 // 保持橙色
)
}
```
### Backend API Changes
#### Work Record Service - 支持结算状态筛选
修改 `work_record_service.py` 的 `get_all()` 方法,支持 `is_settled` 查询参数:
```python
@staticmethod
def get_all(person_id=None, date=None, year=None, month=None, is_settled=None):
query = WorkRecord.query
# ... 现有筛选逻辑 ...
# 新增结算状态筛选
if is_settled is not None:
query = query.filter(WorkRecord.is_settled == is_settled)
return query.order_by(WorkRecord.work_date.desc()).all()
```
修改路由接收参数:
```python
is_settled = request.args.get('is_settled')
if is_settled is not None:
is_settled = is_settled.lower() == 'true'
```
### Export Service Changes
#### 1. 年度汇总未结算列底色标注
使用浅橙色背景 (#FFF2E8) 标注"未结算"列:
```python
from openpyxl.styles import PatternFill
UNSETTLED_FILL = PatternFill(start_color='FFF2E8', end_color='FFF2E8', fill_type='solid')
# 在 _create_yearly_summary_sheet 中
# 为未结算列标题应用底色
cell = ws.cell(row=1, column=16, value='未结算')
cell.fill = UNSETTLED_FILL
# 为未结算列数据单元格应用底色
cell = ws.cell(row=row_idx, column=16, value=round(unsettled, 2))
cell.fill = UNSETTLED_FILL
```
#### 2. 明细表未结算行底色标注
在 `_create_detail_sheet` 中,为未结算记录的整行应用底色:
```python
# 在写入数据行时
for row_idx, record in enumerate(records, 2):
# ... 写入数据 ...
# 如果是未结算记录,为整行应用底色
if not record.is_settled:
for col_idx in range(1, len(ExportService.DETAIL_HEADERS) + 1):
ws.cell(row=row_idx, column=col_idx).fill = UNSETTLED_FILL
```
## Data Models
无需修改数据模型。
## Correctness Properties
*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
### Property 1: Settlement Filter Correctness
*For any* set of work records and any settlement filter value (true, false, or null), the filtered results SHALL contain only records matching the filter criteria: when filter is true, all returned records have is_settled=true; when filter is false, all returned records have is_settled=false; when filter is null, all records are returned.
**Validates: Requirements 2.2, 2.3, 2.4, 2.5**
### Property 2: Unsettled Row Highlighting in Exports
*For any* exported report (monthly or yearly), all rows in the detail sheet where settlement status is "未结算" SHALL have the same distinct background color applied to all cells in that row.
**Validates: Requirements 6.1, 6.2, 6.3**
### Property 3: Unsettled Column Highlighting in Yearly Summary
*For any* yearly report export, all cells in the "未结算" column (including header) in the yearly summary sheet SHALL have a distinct background color applied.
**Validates: Requirements 5.1, 5.2**
## Error Handling
1. **筛选参数处理**
- 如果 `is_settled` 参数值无效,忽略该参数并返回所有记录
- 保持现有的错误处理模式
2. **导出样式处理**
- 如果样式应用失败,继续导出但不应用样式
- 记录错误日志但不中断导出流程
## Testing Strategy
### Unit Tests
1. **Backend Service Tests**
- 测试 `get_all()` 方法正确处理 `is_settled` 参数
- 测试导出服务正确应用未结算行/列底色
2. **Frontend Component Tests**
- 测试各列表组件不显示ID列
- 测试工作记录列表的结算状态筛选功能
- 测试仪表盘年度汇总的结算状态列格式
### Property-Based Tests
使用 Hypothesis 库进行属性测试:
1. **Settlement Filter Property Test**
- 生成随机工作记录集合
- 验证筛选结果符合筛选条件
2. **Export Styling Property Test**
- 生成随机工作记录
- 验证导出文件中未结算记录的样式正确应用