本设计文档描述工作统计系统的手机适配和数据导入功能的技术实现方案。手机适配将通过CSS媒体查询和Ant Design的响应式组件实现;导入功能将在后端使用openpyxl解析XLSX文件,前端使用Ant Design的Upload组件。
graph TB
subgraph Frontend
A[index.html] --> B[Viewport Meta]
C[Layout.jsx] --> D[Responsive Sider]
E[Components] --> F[Mobile CSS]
G[Import.jsx] --> H[Upload Component]
end
subgraph Backend
I[import_routes.py] --> J[ImportService]
J --> K[Template Generator]
J --> L[XLSX Parser]
L --> M[Data Validator]
M --> N[WorkRecord Creator]
end
H -->|POST /api/import/upload| I
H -->|GET /api/import/template| I
修改viewport meta标签以禁止用户缩放:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
增强现有Layout组件以支持移动端:
// 移动端检测
const [isMobile, setIsMobile] = useState(window.innerWidth < 768)
// 移动端使用Drawer替代Sider
{isMobile ? (
<Drawer placement="left" onClose={toggleMenu} open={menuVisible}>
<Menu items={menuItems} onClick={handleMenuClick} />
</Drawer>
) : (
<Sider collapsible collapsed={collapsed}>
<Menu items={menuItems} onClick={handleMenuClick} />
</Sider>
)}
新增 backend/app/services/import_service.py:
class ImportService:
REQUIRED_COLUMNS = ['人员姓名', '物品名称', '工作日期', '数量']
MAX_FILE_SIZE = 5 * 1024 * 1024 # 5MB
@staticmethod
def generate_template() -> BytesIO:
"""生成导入模板XLSX文件"""
pass
@staticmethod
def parse_and_validate(file_content: bytes) -> Tuple[List[dict], List[str]]:
"""解析并验证XLSX文件内容
Returns: (valid_records, errors)
"""
pass
@staticmethod
def import_records(records: List[dict]) -> int:
"""批量创建工作记录
Returns: 成功导入的记录数
"""
pass
新增 backend/app/routes/import_routes.py:
@import_bp.route('/template', methods=['GET'])
def download_template():
"""下载导入模板"""
pass
@import_bp.route('/upload', methods=['POST'])
def upload_import():
"""上传并导入XLSX文件"""
pass
新增 frontend/src/components/Import.jsx:
function Import() {
// 模板下载
const handleDownloadTemplate = async () => { ... }
// 文件上传
const handleUpload = async (file) => { ... }
return (
<Card>
<Button onClick={handleDownloadTemplate}>下载导入模板</Button>
<Upload beforeUpload={handleUpload} accept=".xlsx">
<Button>选择文件上传</Button>
</Upload>
{errors && <Alert type="error" message={errors} />}
{success && <Alert type="success" message={success} />}
</Card>
)
}
扩展 frontend/src/services/api.js:
export const importApi = {
downloadTemplate: () => axios.get('/api/import/template', { responseType: 'blob' }),
upload: (file) => {
const formData = new FormData()
formData.append('file', file)
return api.post('/api/import/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
}
}
| 列名 | 字段 | 类型 | 说明 |
|---|---|---|---|
| 人员姓名 | person_name | string | 必须匹配系统中已存在的人员 |
| 物品名称 | item_name | string | 必须匹配系统中已存在的物品 |
| 工作日期 | work_date | date | 格式: YYYY-MM-DD |
| 数量 | quantity | number | 正整数 |
// 成功响应
interface ImportSuccessResponse {
success: true
count: number // 导入记录数
}
// 失败响应
interface ImportErrorResponse {
success: false
errors: string[] // 错误信息列表
}
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.
For any generated import template, it SHALL contain all required columns (人员姓名, 物品名称, 工作日期, 数量) in the header row.
Validates: Requirements 3.2
For any uploaded file that is not a valid XLSX format, the Import_Service SHALL reject it with a format error.
Validates: Requirements 4.2
For any uploaded XLSX file missing one or more required columns, the Import_Service SHALL reject it with a column missing error.
Validates: Requirements 4.3
For any row with invalid data (non-existent person, non-existent item, invalid date format, non-positive quantity, or empty required field), the Import_Service SHALL return an error message containing the row number and specific error reason.
Validates: Requirements 4.4, 4.5, 5.1, 5.2, 5.3, 5.4, 5.5
For any valid import file with N rows of data, the Import_Service SHALL create exactly N work records and return count=N.
Validates: Requirements 4.6, 4.7
For any import file containing at least one invalid row, the Import_Service SHALL NOT create any work records (all-or-nothing).
Validates: Requirements 4.8
For any uploaded file exceeding 5MB, the System SHALL reject the upload with a file size error.
Validates: Requirements 4.11
| 错误类型 | HTTP状态码 | 错误消息格式 |
|---|---|---|
| 文件格式错误 | 400 | "文件格式错误,请上传XLSX文件" |
| 文件过大 | 400 | "文件大小超过限制(最大5MB)" |
| 缺少必需列 | 400 | "缺少必需列: xxx" |
| 人员不存在 | 400 | "第X行: 人员 'xxx' 不存在" |
| 物品不存在 | 400 | "第X行: 物品 'xxx' 不存在" |
| 日期格式错误 | 400 | "第X行: 日期格式错误,应为 YYYY-MM-DD" |
| 数量无效 | 400 | "第X行: 数量必须为正数" |
| 字段为空 | 400 | "第X行: 'xxx' 不能为空" |
Template Generation Tests
Validation Tests
Import Tests
使用 hypothesis 库进行属性测试:
**Feature: mobile-and-import, Property N: {property_text}**/* 移动端 */
@media (max-width: 767px) {
/* 单列布局 */
/* 抽屉式导航 */
/* 卡片式表格 */
}
/* 平板 */
@media (min-width: 768px) and (max-width: 991px) {
/* 两列布局 */
}
/* 桌面 */
@media (min-width: 992px) {
/* 多列布局 */
}