test(web): update travel request detail tests and styles

- test_reimbursement_endpoints.py: update reimbursement endpoint tests
- travel-request-detail-view.css: update travel request detail styles
- TravelRequestDetailView.vue: update travel request detail view
This commit is contained in:
caoxiaozhu
2026-05-13 06:56:30 +00:00
parent 755f935c9d
commit 0f7bd43ce3
3 changed files with 82 additions and 4 deletions

View File

@@ -12,7 +12,9 @@ from sqlalchemy.pool import StaticPool
from app.api.deps import get_db from app.api.deps import get_db
from app.db.base import Base from app.db.base import Base
from app.main import create_app from app.main import create_app
from app.models.employee import Employee
from app.models.financial_record import ExpenseClaim, ExpenseClaimItem from app.models.financial_record import ExpenseClaim, ExpenseClaimItem
from app.models.role import Role
from app.schemas.ocr import OcrRecognizeBatchRead, OcrRecognizeDocumentRead from app.schemas.ocr import OcrRecognizeBatchRead, OcrRecognizeDocumentRead
from app.services.expense_claims import ExpenseClaimService from app.services.expense_claims import ExpenseClaimService
from app.services.ocr import OcrService from app.services.ocr import OcrService
@@ -44,10 +46,34 @@ def build_client() -> tuple[TestClient, sessionmaker[Session]]:
def seed_claim(db: Session) -> tuple[ExpenseClaim, ExpenseClaimItem]: def seed_claim(db: Session) -> tuple[ExpenseClaim, ExpenseClaimItem]:
manager = Employee(
id="mgr-1",
employee_no="E20001",
name="李总",
email="manager@example.com",
position="市场总监",
grade="P7",
)
role = Role(
id="role-user",
role_code="user",
name="员工",
description="普通员工",
)
employee = Employee(
id="emp-1",
employee_no="E10001",
name="张三",
email="zhangsan@example.com",
position="招商主管",
grade="P4",
manager=manager,
roles=[role],
)
claim = ExpenseClaim( claim = ExpenseClaim(
id="claim-attachment-1", id="claim-attachment-1",
claim_no="EXP-202605-101", claim_no="EXP-202605-101",
employee_id="emp-1", employee_id=employee.id,
employee_name="张三", employee_name="张三",
department_id="dept-1", department_id="dept-1",
department_name="市场部", department_name="市场部",
@@ -75,6 +101,9 @@ def seed_claim(db: Session) -> tuple[ExpenseClaim, ExpenseClaimItem]:
invoice_id=None, invoice_id=None,
) )
claim.items = [item] claim.items = [item]
db.add(manager)
db.add(role)
db.add(employee)
db.add(claim) db.add(claim)
db.commit() db.commit()
return claim, item return claim, item
@@ -302,6 +331,10 @@ def test_claim_item_delete_removes_item_and_attachment(monkeypatch, tmp_path) ->
detail_payload = detail_response.json() detail_payload = detail_response.json()
assert detail_payload["items"] == [] assert detail_payload["items"] == []
assert detail_payload["invoice_count"] == 0 assert detail_payload["invoice_count"] == 0
assert detail_payload["employee_position"] == "招商主管"
assert detail_payload["employee_grade"] == "P4"
assert detail_payload["manager_name"] == "李总"
assert detail_payload["role_labels"] == ["员工"]
deleted_meta_response = client.get( deleted_meta_response = client.get(
f"/api/v1/reimbursements/claims/{claim_id}/items/{item_id}/attachment/meta", f"/api/v1/reimbursements/claims/{claim_id}/items/{item_id}/attachment/meta",

View File

@@ -48,7 +48,7 @@
.applicant-card { .applicant-card {
display: grid; display: grid;
grid-template-columns: 48px minmax(0, 1fr); grid-template-columns: 48px minmax(0, 1fr);
align-items: center; align-items: start;
gap: 12px; gap: 12px;
min-width: 0; min-width: 0;
padding: 0; padding: 0;
@@ -99,6 +99,41 @@
line-height: 1.5; line-height: 1.5;
} }
.applicant-copy {
min-width: 0;
}
.applicant-meta {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 8px 14px;
margin-top: 10px;
}
.applicant-meta-item {
min-width: 0;
display: grid;
gap: 3px;
}
.applicant-meta-item span {
color: #94a3b8;
font-size: 11px;
font-weight: 700;
letter-spacing: .03em;
text-transform: uppercase;
}
.applicant-meta-item strong {
color: #0f172a;
font-size: 12px;
font-weight: 700;
line-height: 1.45;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.hero-stat-strip { .hero-stat-strip {
display: grid; display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
@@ -1624,6 +1659,10 @@
gap: 14px; gap: 14px;
} }
.applicant-meta {
grid-template-columns: 1fr 1fr;
}
.hero-stat-strip { .hero-stat-strip {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }

View File

@@ -6,9 +6,15 @@
<div class="hero-topline"> <div class="hero-topline">
<div class="applicant-card"> <div class="applicant-card">
<div class="portrait">{{ profile.avatar }}</div> <div class="portrait">{{ profile.avatar }}</div>
<div> <div class="applicant-copy">
<h2>{{ profile.name }} <span>{{ request.typeLabel }}</span></h2> <h2>{{ profile.name }} <span>{{ request.typeLabel }}</span></h2>
<p>{{ profile.department }}</p> <p>{{ profile.position }} · {{ profile.identity }}</p>
<div class="applicant-meta">
<div v-for="item in profile.facts" :key="item.label" class="applicant-meta-item">
<span>{{ item.label }}</span>
<strong>{{ item.value }}</strong>
</div>
</div>
</div> </div>
</div> </div>