feat(approval): 审批单内容快照与二次分配方案版本化
补齐 code-review 遗留 2 项:审批单此前只固化 ref_id/refer_no 指针,复核者看到 的是业务当前内容;二次分配方案在编辑替换与驳回清空时一律物理删除,方案事实 无处回溯。 - 审批单加 version_no 与 snapshot_json(信封 schema_version/order_type/ captured_at/data);二次分配、月结、调整单三类提交均固化送审内容,开关关闭 的自动通过路径同样写快照;详情按 snapshot/live/missing 三态回显,历史单 只读回退当前内容并显式告警,不回填历史快照 - 二次分配方案行改 append-only 版本化(version_no/is_active,唯一键扩为 business+version+personnel):编辑替换与驳回清空只置历史,核算与详情默认 只取生效行,业务详情新增 allocation_history - 送审内容未变不重提、方案未变不新增版本:判据统一为送审内容,两个 version_no 语义解耦(方案版本=内容第几版,审批单版本=第几次送审) - 审批单列表、工作台待办、审批超时预警三处 defer 快照列,避免整列拉取 - 月结提交取行加 for_update,防并发提交算出重复的审批单版本号 - 修复审批类型筛选上界 le=2(调整单无法筛选)、快照金额把空值写成 0.00 - V11 迁移三件套同步(Alembic / db/migration / init-dev.sql) 测试:单元 +16(方案内容比较 10、审批入参 6)、集成 +12;全量 891 passed。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
1 parent
2e0c64849b
commit
4c32a556de
28 files changed
+1458
-76
No files matched your search
@@ -5,8 +5,12 @@
|
||||
并填比例 → 提交成功
|
||||
- 详情/编辑回显:再次打开编辑抽屉,二次分配两行仍在(全量替换语义)
|
||||
- 合计提示:两行比例非满额时展示「余额…(归属待业务确认)」
|
||||
- 审批内容:审批详情展示**提交时**的送审方案(参与人 + 比例)
|
||||
- 数据清理:删除本次创建的业务(t_ 前缀客户/业务,走界面删除)
|
||||
|
||||
审批开关在脚本内显式关闭(结束还原默认 1):既不依赖种子默认值,也让本次
|
||||
业务不留在途审批单——业务删除保护在途单,开关开启时删不掉。
|
||||
|
||||
运行前置(缺一不可):
|
||||
docker compose up -d # MySQL + Redis
|
||||
cd backend && uv run uvicorn app.main:app --port 8000
|
||||
@@ -14,8 +18,11 @@
|
||||
cd frontend && E2E_ADMIN_PASSWORD=<开发库admin密码> python e2e/secondary_allocation_flow.py
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
from playwright.sync_api import Page, sync_playwright
|
||||
|
||||
@@ -32,9 +39,50 @@ from common import (
|
||||
)
|
||||
|
||||
ADMIN = "admin"
|
||||
API_BASE = "http://127.0.0.1:8000"
|
||||
SUFFIX = f"{int(time.time())}"
|
||||
console_errors: list[str] = []
|
||||
|
||||
_ALLOCATION_SWITCH = "approval.secondary_allocation.enabled"
|
||||
|
||||
|
||||
def _api(method: str, path: str, token: str, body: dict | None = None) -> dict:
|
||||
"""直连后端接口(仅用于开关预置;浏览器交互一律走页面)。"""
|
||||
request = urllib.request.Request(
|
||||
f"{API_BASE}{path}",
|
||||
method=method,
|
||||
headers={
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
data=json.dumps(body).encode() if body is not None else None,
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(request, timeout=20) as response:
|
||||
payload = json.loads(response.read().decode())
|
||||
except urllib.error.HTTPError as exc:
|
||||
detail = exc.read().decode()[:200]
|
||||
raise E2EFailure(f"API {method} {path} HTTP {exc.code}:{detail}") from exc
|
||||
if payload.get("code") != 200:
|
||||
raise E2EFailure(f"API {method} {path} 业务失败:{payload.get('message')}")
|
||||
return payload["data"]
|
||||
|
||||
|
||||
def _set_allocation_switch(token: str, value: str) -> None:
|
||||
"""显式控制二次分配审批开关(用例不依赖种子默认值;结束还原默认 1)。"""
|
||||
_api(
|
||||
"POST",
|
||||
"/api/v1/system/params/changes",
|
||||
token,
|
||||
{
|
||||
"change_type": 1,
|
||||
"remark": f"e2e secondary_allocation_flow 设置二次分配审批开关={value}",
|
||||
"items": [
|
||||
{"param_code": _ALLOCATION_SWITCH, "dimension_value": "", "value": value}
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def step_navigate_to_business(page: Page, admin_password: str) -> None:
|
||||
login(page, ADMIN, admin_password)
|
||||
@@ -170,6 +218,44 @@ def step_verify_allocation_echo(page: Page) -> None:
|
||||
page.wait_for_timeout(400)
|
||||
|
||||
|
||||
def _current_business_no(page: Page, customer_name: str) -> str:
|
||||
"""取本次创建业务的业务编号(列表首列),用于审批单检索。"""
|
||||
page.goto(f"{BASE_URL}/business/businesses", wait_until="networkidle")
|
||||
page.wait_for_timeout(600)
|
||||
page.click("button:has-text('查询')")
|
||||
page.wait_for_timeout(500)
|
||||
row = page.locator(".el-table__row", has_text=customer_name).first
|
||||
if row.count() == 0:
|
||||
fail("取业务编号", f"未找到本次业务(客户 {customer_name})")
|
||||
return row.locator("td").first.inner_text().strip()
|
||||
|
||||
|
||||
def step_verify_approval_snapshot(page: Page, business_no: str) -> None:
|
||||
"""审批详情:展示提交时的送审方案(参与人 + 比例 + 合计余额)。"""
|
||||
page.goto(f"{BASE_URL}/system/approvals", wait_until="networkidle")
|
||||
page.wait_for_timeout(800)
|
||||
page.locator('.filters:visible input[placeholder="审批单号 / 业务编号"]').fill(business_no)
|
||||
page.locator('.filters:visible button:has-text("查询")').first.click()
|
||||
page.wait_for_timeout(800)
|
||||
rows = page.locator(".el-table__body .el-table__row:visible")
|
||||
if rows.count() == 0:
|
||||
fail("审批内容", f"未找到业务 {business_no} 的审批单")
|
||||
rows.first.locator("button:has-text('详情')").click()
|
||||
page.wait_for_timeout(1000)
|
||||
drawer = page.locator(".el-drawer:visible").last
|
||||
drawer.wait_for(state="visible")
|
||||
text = drawer.inner_text()
|
||||
if "审批内容" not in text:
|
||||
fail("审批内容", "审批详情未展示「审批内容」区块")
|
||||
for i in (0, 1):
|
||||
name = f"t_e2e员_{SUFFIX}_{i}"
|
||||
if name not in text:
|
||||
fail("审批内容", f"送审内容缺少参与人 {name}")
|
||||
if "30.00%" not in text or "40.00%" not in text:
|
||||
fail("审批内容", f"送审内容缺少分配比例:{text[:200]}")
|
||||
print("✓ 5. 审批内容 审批详情展示送审方案(两名参与人与 30%/40% 比例)")
|
||||
|
||||
|
||||
def step_cleanup(page: Page) -> None:
|
||||
"""删除本次创建的业务与其客户(t_ 前缀,走界面删除)。"""
|
||||
page.goto(f"{BASE_URL}/business/businesses", wait_until="networkidle")
|
||||
@@ -184,7 +270,7 @@ def step_cleanup(page: Page) -> None:
|
||||
# 删除确认框按钮文案为「删除」(useConfirmAction confirmButtonText)
|
||||
confirm_message_box(page, "删除")
|
||||
page.wait_for_timeout(600)
|
||||
print("✓ 5. 数据清理 已删除本次创建的 t_ 业务")
|
||||
print("✓ 6. 数据清理 已删除本次创建的 t_ 业务")
|
||||
|
||||
|
||||
def run(page: Page) -> None:
|
||||
@@ -194,13 +280,25 @@ def run(page: Page) -> None:
|
||||
fail("环境变量", "未设置 E2E_ADMIN_PASSWORD(开发库 admin 密码)")
|
||||
|
||||
step_navigate_to_business(page, admin_password)
|
||||
customer_name = step_create_customer_via_ui(page)
|
||||
token = page.evaluate("localStorage.getItem('ys_token')")
|
||||
if not token:
|
||||
fail("API 预置", "localStorage 中未取到 ys_token")
|
||||
# 显式关闭二次分配审批开关(结束还原默认 1):自动通过留痕,既不依赖
|
||||
# 种子默认值,也让本次业务不留“在途审批单”——业务删除保护在途单,
|
||||
# 开关开启时该业务在界面上删不掉
|
||||
_set_allocation_switch(token, "0")
|
||||
try:
|
||||
step_create_personnel(page)
|
||||
step_create_business_with_allocation(page, customer_name)
|
||||
step_verify_allocation_echo(page)
|
||||
customer_name = step_create_customer_via_ui(page)
|
||||
try:
|
||||
step_create_personnel(page)
|
||||
step_create_business_with_allocation(page, customer_name)
|
||||
business_no = _current_business_no(page, customer_name)
|
||||
step_verify_allocation_echo(page)
|
||||
step_verify_approval_snapshot(page, business_no)
|
||||
finally:
|
||||
step_cleanup(page)
|
||||
finally:
|
||||
step_cleanup(page)
|
||||
_set_allocation_switch(token, "1")
|
||||
|
||||
if console_errors:
|
||||
print(f" (控制台错误 {len(console_errors)} 条:{console_errors[:2]})")
|
||||
|
||||
@@ -39,6 +39,65 @@ export function stepNodeLabel(node: number): string {
|
||||
return ({ 0: '撤回', 1: '财务复核', 2: '领导审核' } as const)[node] ?? `节点${node}`
|
||||
}
|
||||
|
||||
/** 审批对象类型:1二次分配方案 2月度结算 3调整单 */
|
||||
export function orderTypeLabel(orderType: number): string {
|
||||
return (
|
||||
({ 1: '二次分配方案', 2: '月度结算', 3: '调整单' } as const)[orderType] ??
|
||||
`类型${orderType}`
|
||||
)
|
||||
}
|
||||
|
||||
/** 二次分配方案送审内容(snapshot.data 按 order_type=1 解释) */
|
||||
export interface ApprovalAllocationData {
|
||||
business_no: string
|
||||
business_date: string | null
|
||||
customer_name: string | null
|
||||
owner_personnel_name: string | null
|
||||
assistant_personnel_name: string | null
|
||||
sale_amount: string | null
|
||||
cost_amount: string | null
|
||||
allocations: { personnel_id: number; personnel_name: string; ratio: string }[]
|
||||
allocation_total: string
|
||||
allocation_balance: string
|
||||
}
|
||||
|
||||
/** 月结送审内容(snapshot.data 按 order_type=2 解释) */
|
||||
export interface ApprovalSettlementData {
|
||||
settlement_month: string
|
||||
total_performance: string | null
|
||||
total_payable: string | null
|
||||
person_count: number
|
||||
items: {
|
||||
personnel_name: string
|
||||
role_code: string
|
||||
calc_type: number
|
||||
performance_sum: string
|
||||
payable_sum: string
|
||||
basic_amount: string | null
|
||||
salary_mode: string | null
|
||||
net_amount: string | null
|
||||
is_probation: number
|
||||
}[]
|
||||
}
|
||||
|
||||
/** 调整单送审内容(snapshot.data 按 order_type=3 解释) */
|
||||
export interface ApprovalAdjustmentData {
|
||||
order_no: string
|
||||
settlement_month: string
|
||||
personnel_name: string
|
||||
direction: number
|
||||
amount: string
|
||||
reason: string
|
||||
}
|
||||
|
||||
/** 审批内容信封:snapshot=提交时快照;live=历史单回退当前内容;missing=缺失 */
|
||||
export interface ApprovalSnapshot {
|
||||
schema_version: number
|
||||
order_type: number
|
||||
captured_at: string | null
|
||||
data: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface ApprovalStep {
|
||||
id: number
|
||||
node: number
|
||||
@@ -52,10 +111,12 @@ export interface ApprovalStep {
|
||||
export interface ApprovalOrder {
|
||||
id: number
|
||||
order_no: string
|
||||
/** 1二次分配方案 2月度结算(预留) */
|
||||
/** 1二次分配方案 2月度结算 3调整单 */
|
||||
order_type: number
|
||||
ref_id: number
|
||||
refer_no: string
|
||||
/** 同一引用对象的第几次提交(重提产生新版本) */
|
||||
version_no: number
|
||||
status: number
|
||||
submitter_id: number | null
|
||||
submitter_name: string
|
||||
@@ -64,6 +125,8 @@ export interface ApprovalOrder {
|
||||
|
||||
export interface ApprovalOrderDetail extends ApprovalOrder {
|
||||
steps: ApprovalStep[]
|
||||
snapshot: ApprovalSnapshot | null
|
||||
content_source: 'snapshot' | 'live' | 'missing'
|
||||
}
|
||||
|
||||
export interface ApprovalQueryParams {
|
||||
|
||||
@@ -58,12 +58,23 @@ export interface SecondaryAllocationRow {
|
||||
ratio: string
|
||||
}
|
||||
|
||||
/** 业务详情:附产品明细行与二次分配方案(编辑弹窗一次回显) */
|
||||
/** 方案版本行(append-only;编辑替换/驳回清空后旧行仍保留为历史) */
|
||||
export interface AllocationVersionRow extends SecondaryAllocationRow {
|
||||
/** 方案版本号(同一业务从 1 递增) */
|
||||
version_no: number
|
||||
/** 1 当前生效 0 历史版本 */
|
||||
is_active: number
|
||||
create_time: string
|
||||
}
|
||||
|
||||
/** 业务详情:附产品明细行、当前方案与方案版本历史(编辑弹窗一次回显) */
|
||||
export interface BusinessDetail extends BusinessItem {
|
||||
items: BusinessItemRow[]
|
||||
secondary_allocations: SecondaryAllocationRow[]
|
||||
/** 方案合计/余额提示(余额归属待澄清 #2,仅展示) */
|
||||
allocation_hint: string | null
|
||||
/** 方案全部版本(含历史;被驳回方案亦保留) */
|
||||
allocation_history: AllocationVersionRow[]
|
||||
}
|
||||
|
||||
export interface BusinessQueryParams {
|
||||
|
||||
@@ -1141,6 +1141,43 @@ onMounted(() => {
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="sub-section">
|
||||
<div class="sub-section-title">二次分配方案(含历史版本)</div>
|
||||
<el-table
|
||||
v-if="detail && (detail.allocation_history?.length ?? 0) > 0"
|
||||
:data="detail.allocation_history"
|
||||
stripe
|
||||
size="small"
|
||||
>
|
||||
<el-table-column label="版本" width="70" align="center">
|
||||
<template #default="{ row }">V{{ row.version_no }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="personnel_name" label="参与业务员" min-width="140" />
|
||||
<el-table-column label="分配比例" width="110" align="right">
|
||||
<template #default="{ row }">
|
||||
<span class="cell-amount">{{ row.ratio }}%</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
v-if="row.is_active === 1"
|
||||
type="success"
|
||||
size="small"
|
||||
disable-transitions
|
||||
>
|
||||
生效
|
||||
</el-tag>
|
||||
<el-tag v-else type="info" size="small" disable-transitions>历史</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="录入时间" min-width="150">
|
||||
<template #default="{ row }">{{ formatDateTime(row.create_time) }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-else class="cell-muted">未设置二次分配 —— 全部绩效归负责营销人员</div>
|
||||
</div>
|
||||
|
||||
<div class="sub-section">
|
||||
<div class="sub-section-title">状态流转</div>
|
||||
<el-timeline v-if="statusLogs.length > 0" class="status-timeline">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
import {
|
||||
@@ -8,10 +8,14 @@ import {
|
||||
approvalStatusTagType,
|
||||
getApproval,
|
||||
listApprovals,
|
||||
orderTypeLabel,
|
||||
stepActionLabel,
|
||||
stepNodeLabel,
|
||||
type ApprovalAdjustmentData,
|
||||
type ApprovalAllocationData,
|
||||
type ApprovalOrder,
|
||||
type ApprovalOrderDetail,
|
||||
type ApprovalSettlementData,
|
||||
} from '@/api/approvals'
|
||||
import AppPagination from '@/components/table/AppPagination.vue'
|
||||
import { useConfirmAction } from '@/composables/useConfirmAction'
|
||||
@@ -78,6 +82,26 @@ async function openDetail(row: ApprovalOrder): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 审批内容(按对象类型解释快照 data) ====================
|
||||
|
||||
const allocationData = computed<ApprovalAllocationData | null>(() =>
|
||||
detail.value?.order_type === 1 && detail.value.snapshot
|
||||
? (detail.value.snapshot.data as unknown as ApprovalAllocationData)
|
||||
: null,
|
||||
)
|
||||
|
||||
const settlementData = computed<ApprovalSettlementData | null>(() =>
|
||||
detail.value?.order_type === 2 && detail.value.snapshot
|
||||
? (detail.value.snapshot.data as unknown as ApprovalSettlementData)
|
||||
: null,
|
||||
)
|
||||
|
||||
const adjustmentData = computed<ApprovalAdjustmentData | null>(() =>
|
||||
detail.value?.order_type === 3 && detail.value.snapshot
|
||||
? (detail.value.snapshot.data as unknown as ApprovalAdjustmentData)
|
||||
: null,
|
||||
)
|
||||
|
||||
// ==================== 节点动作 ====================
|
||||
|
||||
const actingId = ref<number | null>(null)
|
||||
@@ -154,6 +178,8 @@ async function submitAction(): Promise<void> {
|
||||
<el-form-item label="类型">
|
||||
<el-select v-model="filters.order_type" placeholder="全部" clearable style="width: 150px">
|
||||
<el-option label="二次分配方案" :value="1" />
|
||||
<el-option label="月度结算" :value="2" />
|
||||
<el-option label="调整单" :value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
@@ -165,11 +191,12 @@ async function submitAction(): Promise<void> {
|
||||
<el-table v-loading="loading" :data="records" stripe>
|
||||
<el-table-column prop="order_no" label="审批单号" min-width="150" />
|
||||
<el-table-column label="类型" width="120">
|
||||
<template #default="{ row }">
|
||||
{{ row.order_type === 1 ? '二次分配方案' : `类型${row.order_type}` }}
|
||||
</template>
|
||||
<template #default="{ row }">{{ orderTypeLabel(row.order_type) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="refer_no" label="业务编号" min-width="140" />
|
||||
<el-table-column label="版本" width="70" align="center">
|
||||
<template #default="{ row }">V{{ row.version_no }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="refer_no" label="引用单号" min-width="140" />
|
||||
<el-table-column label="状态" width="110">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="approvalStatusTagType(row.status)" size="small" disable-transitions>
|
||||
@@ -196,7 +223,7 @@ async function submitAction(): Promise<void> {
|
||||
@size-change="onSizeChange"
|
||||
/>
|
||||
|
||||
<el-drawer v-model="detailVisible" title="审批详情" size="480px">
|
||||
<el-drawer v-model="detailVisible" title="审批详情" size="720px">
|
||||
<div v-loading="detailLoading">
|
||||
<template v-if="detail">
|
||||
<div class="detail-head">
|
||||
@@ -210,11 +237,120 @@ async function submitAction(): Promise<void> {
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="detail-meta">
|
||||
<span>业务编号:{{ detail.refer_no }}</span>
|
||||
<span>类型:{{ orderTypeLabel(detail.order_type) }} · 版本 V{{ detail.version_no }}</span>
|
||||
<span>引用单号:{{ detail.refer_no }}</span>
|
||||
<span>提交人:{{ detail.submitter_name }}</span>
|
||||
<span>提交时间:{{ formatDateTime(detail.create_time) }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 审批内容:优先提交时快照;历史单缺快照时读当前并显式标注 -->
|
||||
<div class="content-section">
|
||||
<div class="content-title">审批内容</div>
|
||||
<el-alert
|
||||
v-if="detail.content_source === 'live'"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="该单为历史单,未留提交时快照;以下为当前业务内容,仅供参考,不等同送审版本。"
|
||||
/>
|
||||
<el-alert
|
||||
v-else-if="detail.content_source === 'missing'"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
title="送审内容缺失:引用对象已不存在且未留快照,仅可查看节点留痕。"
|
||||
/>
|
||||
|
||||
<template v-if="allocationData">
|
||||
<el-descriptions :column="2" border size="small" class="content-desc">
|
||||
<el-descriptions-item label="业务编号">
|
||||
{{ allocationData.business_no }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="业务日期">
|
||||
{{ allocationData.business_date ?? '—' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="客户名称">
|
||||
{{ allocationData.customer_name ?? '—' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="负责人员">
|
||||
{{ allocationData.owner_personnel_name ?? '(待指定)' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="销售额(元)">
|
||||
{{ allocationData.sale_amount ?? '—' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="成本(元)">
|
||||
{{ allocationData.cost_amount ?? '—' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-table :data="allocationData.allocations" stripe size="small" class="content-table">
|
||||
<el-table-column prop="personnel_name" label="参与业务员" min-width="160" />
|
||||
<el-table-column label="分配比例" width="120" align="right">
|
||||
<template #default="{ row }">{{ row.ratio }}%</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<span class="content-empty">未设置二次分配 —— 全部绩效归负责营销人员</span>
|
||||
</template>
|
||||
</el-table>
|
||||
<div class="content-total">
|
||||
合计 {{ allocationData.allocation_total }}%,余额
|
||||
{{ allocationData.allocation_balance }}%(归合同负责营销人员)
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else-if="settlementData">
|
||||
<el-descriptions :column="2" border size="small" class="content-desc">
|
||||
<el-descriptions-item label="结算月份">
|
||||
{{ settlementData.settlement_month }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="结算人数">
|
||||
{{ settlementData.person_count }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="绩效合计(元)">
|
||||
{{ settlementData.total_performance ?? '—' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="应付合计(元)">
|
||||
{{ settlementData.total_payable ?? '—' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-table :data="settlementData.items" stripe size="small" class="content-table">
|
||||
<el-table-column prop="personnel_name" label="人员" min-width="110" />
|
||||
<el-table-column prop="role_code" label="岗位" width="110" />
|
||||
<el-table-column label="绩效(元)" width="110" align="right">
|
||||
<template #default="{ row }">{{ row.performance_sum }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="应付(元)" width="110" align="right">
|
||||
<template #default="{ row }">{{ row.payable_sum }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="净工资(元)" width="120" align="right">
|
||||
<template #default="{ row }">{{ row.net_amount ?? '—' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<template v-else-if="adjustmentData">
|
||||
<el-descriptions :column="2" border size="small" class="content-desc">
|
||||
<el-descriptions-item label="调整单号">
|
||||
{{ adjustmentData.order_no }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="调整月份">
|
||||
{{ adjustmentData.settlement_month }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="调整人员">
|
||||
{{ adjustmentData.personnel_name }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="方向">
|
||||
{{ adjustmentData.direction === 1 ? '补发' : '扣减' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="金额(元)">
|
||||
{{ adjustmentData.amount }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="扣补说明">
|
||||
{{ adjustmentData.reason }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 签名元素:竖排节点时间线(财务复核 → 领导审核的处理链) -->
|
||||
<div class="timeline">
|
||||
<div
|
||||
@@ -315,6 +451,40 @@ async function submitAction(): Promise<void> {
|
||||
color: var(--ys-ink-3);
|
||||
}
|
||||
|
||||
.content-section {
|
||||
margin-top: 12px;
|
||||
padding: 12px;
|
||||
background: #f7f8fa;
|
||||
border: 1px solid var(--ys-line);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
margin-bottom: 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: var(--ys-ink);
|
||||
}
|
||||
|
||||
.content-desc {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.content-table {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.content-total {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--ys-ink-2);
|
||||
}
|
||||
|
||||
.content-empty {
|
||||
font-size: 12px;
|
||||
color: var(--ys-ink-3);
|
||||
}
|
||||
|
||||
/* 竖排时间线:左侧轨道 + 节点圆点,动作色区分人工/自动/驳回 */
|
||||
.timeline {
|
||||
margin-top: 14px;
|
||||
|
||||
Reference in new issue
Block a user