feat: 细化差旅票据费用明细分类并自动计算出差补贴

将差旅费用明细拆分为火车票、机票、住宿票、乘车等细分类
型,根据票据字段自动生成行程/事由描述,结合规则引擎自
动计算出差补贴金额,前端适配费用明细编辑和差旅票据审
核交互,补充单元测试覆盖。
This commit is contained in:
caoxiaozhu
2026-05-21 10:57:06 +08:00
parent 8f65661809
commit b183b0bd5e
26 changed files with 2588 additions and 362 deletions

View File

@@ -561,6 +561,46 @@
white-space: pre-wrap;
}
.detail-note.readonly {
background: #f8fafc;
border-color: #e2e8f0;
}
.detail-note-editor {
display: grid;
gap: 10px;
}
.detail-note-editor textarea {
min-height: 92px;
border-color: rgba(16, 185, 129, .28);
background: #fff;
}
.detail-note-editor textarea:focus {
border-color: #10b981;
box-shadow: 0 0 0 3px rgba(16, 185, 129, .12);
outline: none;
}
.detail-note-editor-meta {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
color: #64748b;
font-size: 12px;
line-height: 1.5;
}
.detail-note-actions {
display: inline-flex;
align-items: center;
justify-content: flex-end;
flex-shrink: 0;
gap: 8px;
}
.leader-approval-card {
border-color: rgba(5, 150, 105, .18);
background: linear-gradient(180deg, #ffffff 0%, #f7fdfb 100%);
@@ -633,6 +673,15 @@
background: #fbfefd;
}
.detail-expense-table tbody tr.system-generated-row td {
background: #f0fdf4;
border-bottom-color: #bbf7d0;
}
.detail-expense-table tbody tr.system-generated-row:hover td {
background: #ecfdf5;
}
.detail-expense-table .col-time { width: 11%; }
.detail-expense-table .col-filled-at { width: 15%; }
.detail-expense-table .col-type { width: 13%; }
@@ -756,6 +805,36 @@
color: #ea580c;
}
.over-tag.system {
background: #dcfce7;
color: #047857;
}
.expense-total-under-table {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
margin-top: 12px;
padding: 12px 14px;
border: 1px solid #d1fae5;
border-radius: 8px;
background: #f0fdf4;
color: #0f766e;
}
.expense-total-under-table span {
color: #475569;
font-size: 12px;
font-weight: 800;
}
.expense-total-under-table strong {
color: #047857;
font-size: 17px;
font-weight: 900;
}
.attachment-action-group {
display: inline-flex;
align-items: center;
@@ -932,6 +1011,36 @@
min-width: 128px;
}
.system-row-lock {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 5px;
min-height: 28px;
padding: 0 9px;
border-radius: 8px;
background: #dcfce7;
color: #047857;
font-size: 11px;
font-weight: 850;
white-space: nowrap;
}
.system-attachment-note {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 5px;
min-height: 28px;
padding: 0 9px;
border-radius: 8px;
background: #ecfdf5;
color: #047857;
font-size: 11px;
font-weight: 850;
white-space: nowrap;
}
.row-action-group {
display: flex;
flex-wrap: wrap;
@@ -1332,8 +1441,9 @@
}
.validation-card {
border: 1px solid #e6f0eb;
background: linear-gradient(180deg, #fcfffd 0%, #f7fbf9 100%);
border: 1px solid #e5e7eb;
background: #ffffff;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.validation-head {
@@ -1341,11 +1451,14 @@
align-items: flex-start;
justify-content: space-between;
gap: 12px;
margin-bottom: 8px;
margin-bottom: 10px;
}
.validation-head h3 {
margin-bottom: 4px;
color: #0f172a;
font-size: 15px;
font-weight: 800;
}
.validation-head p {
@@ -1356,28 +1469,32 @@
}
.validation-pill {
min-height: 26px;
min-height: 24px;
display: inline-flex;
align-items: center;
padding: 0 10px;
border-radius: 999px;
font-size: 12px;
border: 1px solid transparent;
font-size: 11px;
font-weight: 800;
}
.validation-pill.ready {
background: #dcfce7;
color: #047857;
background: #f0fdf4;
border-color: #bbf7d0;
color: #166534;
}
.validation-pill.pending {
background: #fff7ed;
border-color: #fed7aa;
color: #c2410c;
}
.validation-pill.warning {
background: #fef2f2;
color: #dc2626;
border-color: #fecaca;
color: #b91c1c;
}
.validation-summary {
@@ -1387,29 +1504,155 @@
line-height: 1.6;
}
.validation-sections {
display: grid;
gap: 18px;
margin-top: 16px;
}
.validation-section {
display: grid;
gap: 10px;
padding-top: 14px;
border-top: 1px solid #e5e7eb;
}
.validation-section:first-child {
padding-top: 0;
border-top: none;
}
.validation-section-title {
display: flex;
align-items: center;
gap: 8px;
margin: 0;
color: #0f172a;
font-size: 13px;
font-weight: 800;
line-height: 1.4;
}
.validation-section-title::before {
content: '';
width: 6px;
height: 6px;
border-radius: 999px;
background: #10b981;
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.12);
}
.validation-section--risk .validation-section-title {
color: #b91c1c;
}
.validation-section--risk .validation-section-title::before {
background: #ef4444;
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
}
.validation-list {
display: grid;
gap: 6px;
margin-top: 12px;
padding-left: 18px;
color: #b45309;
margin: 0;
padding: 0 0 0 18px;
color: #0f766e;
font-size: 13px;
line-height: 1.55;
}
.risk-advice-list {
display: grid;
gap: 12px;
margin-top: 14px;
.validation-list li::marker {
color: #14b8a6;
}
.risk-advice-card {
.validation-section--risk .risk-advice-list {
display: grid;
gap: 10px;
padding: 14px;
border: 1px solid #fee2e2;
margin-top: 0;
}
.validation-section--risk .risk-advice-card {
display: grid;
gap: 8px;
padding: 12px 12px 11px;
border: 1px solid #e5e7eb;
border-radius: 10px;
background: #ffffff;
box-shadow: 0 1px 1px rgba(15, 23, 42, 0.03);
}
.validation-section--risk .risk-advice-card.medium {
border-color: #f3e8d9;
background: #fffcf7;
}
.validation-section--risk .risk-advice-card-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.validation-section--risk .risk-advice-card-head span {
min-height: 20px;
display: inline-flex;
align-items: center;
padding: 0 8px;
border-radius: 999px;
background: #fef2f2;
color: #b91c1c;
font-size: 10px;
font-weight: 800;
white-space: nowrap;
}
.validation-section--risk .risk-advice-card.medium .risk-advice-card-head span {
background: #fff7ed;
color: #c2410c;
}
.validation-section--risk .risk-advice-card-head strong {
min-width: 0;
color: #0f172a;
font-size: 12px;
line-height: 1.4;
text-align: right;
}
.validation-section--risk .risk-advice-point {
margin: 0;
color: #334155;
font-size: 13px;
line-height: 1.5;
}
.validation-section--risk .risk-advice-meta {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
gap: 8px;
}
.validation-section--risk .risk-advice-meta > div {
min-width: 0;
display: grid;
gap: 4px;
padding: 8px 9px;
border-radius: 8px;
background: #fffafa;
background: #f8fafc;
}
.validation-section--risk .risk-advice-meta span {
color: #64748b;
font-size: 10px;
font-weight: 800;
}
.validation-section--risk .risk-advice-meta ul,
.validation-section--risk .risk-advice-meta p {
margin: 0;
color: #334155;
font-size: 11px;
line-height: 1.5;
}
.risk-advice-card.medium {