feat: 优化差旅报销预审流程与个人工作台 UI 体系

- 完善 user_agent_application 申请差旅报销预审槽位与消息组装
- 增强预算助理报告与风险建议卡片交互
- 重构登录页视觉样式与移动端响应式适配
- 优化个人工作台、文档中心、政策中心、员工管理等页面布局
- 拆分 travelRequestDetailPreReviewModel 为 advice/submit 模型
- 补充报销草稿、风险复核、Item Sync 与模板执行器测试覆盖
This commit is contained in:
caoxiaozhu
2026-06-02 14:01:51 +08:00
parent 92444e7eae
commit ca691f3ee0
107 changed files with 5663 additions and 1542 deletions

View File

@@ -99,9 +99,7 @@ class UserAgentReviewTravelReceiptMixin:
}
has_hotel_invoice = any(self._is_review_hotel_card(card) for card in document_cards)
has_local_transport = any(self._is_local_transport_receipt_card(card) for card in document_cards)
required_missing_labels = [] if has_hotel_invoice else ["酒店的报销票据待上传(必须)"]
optional_missing_labels = [] if has_local_transport else ["市内交通/乘车票据可继续上传(非必须)"]
ticket_amount = sum(
(self._extract_amount_decimal_from_card(card) or Decimal("0.00"))
for card in long_distance_cards
@@ -116,9 +114,9 @@ class UserAgentReviewTravelReceiptMixin:
"destination": self._resolve_travel_receipt_destination(payload, long_distance_cards),
"days": self._resolve_travel_receipt_days(payload, long_distance_cards),
"has_hotel_invoice": has_hotel_invoice,
"has_local_transport": has_local_transport,
"has_local_transport": any(self._is_local_transport_receipt_card(card) for card in document_cards),
"required_missing_labels": required_missing_labels,
"optional_missing_labels": optional_missing_labels,
"optional_missing_labels": [],
"blocks_next_step": bool(required_missing_labels),
}
@@ -273,32 +271,20 @@ class UserAgentReviewTravelReceiptMixin:
for item in travel_receipt_state.get("required_missing_labels", [])
if str(item).strip()
]
optional_labels = [
str(item).strip()
for item in travel_receipt_state.get("optional_missing_labels", [])
if str(item).strip()
]
if not required_labels and not optional_labels:
if not required_labels:
return []
content_parts = [*required_labels, *optional_labels]
required_text = "".join(required_labels)
optional_text = "".join(optional_labels)
return [
UserAgentReviewRiskBrief(
title="差旅票据待补充",
level="warning" if required_labels else "info",
content="".join(content_parts),
level="warning",
content=required_text,
detail=(
"系统已识别到长途交通票据,会按差旅报销口径核对住宿、交通等票据完整性。"
+ (f"当前必须补充:{required_text}" if required_text else "")
+ (f"当前还可以补充:{optional_text}" if optional_text else "")
),
suggestion=(
"请先补充酒店住宿发票或住宿清单;在补齐前只能保存为草稿。"
if required_labels
else "如还有市内交通、打车、地铁或停车等乘车票据,可以继续上传;没有也可以进入下一步或保存草稿。"
+ f"当前必须补充:{required_text}"
),
suggestion="请先补充酒店住宿发票或住宿清单;在补齐前只能保存为草稿。",
)
]
@@ -606,6 +592,10 @@ class UserAgentReviewTravelReceiptMixin:
message = str(payload.tool_payload.get("message") or "").strip()
for prefix in (
"提交前请先补全信息:",
"自动检测暂未通过,原因如下:",
"自动检测未通过,原因如下:",
"自动检测暂未通过:",
"自动检测未通过:",
"AI预审暂未通过原因如下",
"AI预审未通过原因如下",
"AI预审暂未通过",
@@ -618,7 +608,9 @@ class UserAgentReviewTravelReceiptMixin:
reasons.extend(
item.strip()
for item in re.split(r"[;\n]+", message)
if item.strip() and not item.strip().startswith("AI预审暂未通过")
if item.strip()
and not item.strip().startswith("AI预审暂未通过")
and not item.strip().startswith("自动检测暂未通过")
)
return list(dict.fromkeys(reason for reason in reasons if reason))