PayrollManagementSystem/frontend/src/views/calc/ledger/index.vue
liuwenxin 8e8bcaa5fb feat(frontend): 视觉规范 v4 全站落地与实测修复
token 补齐与 EP 形体基准、全站版式工具类(.page-head/.card 系/.filter-bar/.ys-pill 等)、布局与登录页、工作台 Bento 与图表主题、打印稿纸、全部业务页统一到全局类;StatusTag 改 pill、AppPagination 加 background。

浏览器实测修复:表格行高收到 1.5;月结生命周期条圆点按阶段着色并加操作提示;工作台空态垂直居中;状态/结果列宽按胶囊内容给足(96/108/120),消除 Element Plus 省略号伪『小点』。设计稿反向同步圆点着色与流程条提示。
2026-10-23 09:58:48 +08:00

236 lines
8.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import * as calcApi from '@/api/calc'
import type { LedgerRow } from '@/api/calc'
import AppPagination from '@/components/table/AppPagination.vue'
import { usePagedList } from '@/composables/usePagedList'
import { handledError } from '@/utils/error'
/**
* 绩效核算台账(M3 ④):核算事实的固化点。
*
* - 核算=把回款固化为台账行(每笔回款仅一次;同业务重复回款仅登记进度)
* - 重算=作废旧版本、按当前参数落新版本(管理员/财务授权,原因留痕)
* - 行展开看计算快照(所用参数版本与行级折算明细)
*/
const filters = reactive({
settlement_month: '',
is_active: 1 as number | undefined,
})
const { records, total, pageNum, pageSize, loading, load, search, onPageChange, onSizeChange } =
usePagedList<LedgerRow, { settlement_month?: string; is_active?: number }>(calcApi.listLedger, () => ({
settlement_month: filters.settlement_month.trim() || undefined,
is_active: filters.is_active,
}))
onMounted(() => {
void load()
})
function resetFilters(): void {
filters.settlement_month = ''
filters.is_active = 1
search()
}
const recalcVisible = ref(false)
const recalcTarget = ref<LedgerRow | null>(null)
const recalcReason = ref('')
function openRecalc(row: LedgerRow): void {
// 聚合行(calc_type=2)receipt_id 为 NULL,无回款可重算(按钮已限量,防御)
if (row.receipt_id == null) return
recalcTarget.value = row
recalcReason.value = ''
recalcVisible.value = true
}
async function submitRecalc(): Promise<void> {
const row = recalcTarget.value
if (!row || row.receipt_id == null) return
if (!recalcReason.value.trim()) {
ElMessage.warning('重算必须填写原因')
return
}
try {
// 重算接口按回款 ID 定位(台账行主键 ≠ 回款 ID:传 row.id 会重算到错误回款)
await calcApi.recalcReceipt(row.receipt_id, recalcReason.value.trim())
ElMessage.success('重算完成(新版本已生效)')
recalcVisible.value = false
await load()
} catch (error) {
handledError(error)
}
}
</script>
<template>
<div class="page">
<!-- 页面头:标题 + 一句口径说明(台账是核算事实的固化点)+ 签名水线 -->
<div class="page-head">
<h1 class="page-title">绩效台账</h1>
<p class="page-desc">
每笔回款只核算一次;重算作废旧版本、按当前参数落新版本并留痕,展开行可看当次核算的参数快照
</p>
<span class="waterline"></span>
</div>
<!--
.filter-bar 直接挂在 el-form 上(去掉 inline):el-form 不是 flex 透明容器,
外面再包一层 div 会让 el-form-item 丢掉 gap 排版,筛选条退化成竖排。
-->
<el-form class="filter-bar" @submit.prevent>
<el-form-item label="归属月">
<el-input
v-model="filters.settlement_month"
placeholder="YYYY-MM"
clearable
style="width: 140px"
@keyup.enter="search"
/>
</el-form-item>
<el-form-item label="版本">
<!-- 可清空:清空=不加 is_active 过滤(axios 自动剔除 undefined),
即「全部版本」;0 精确匹配历史行,不能用来表达「含历史」 -->
<el-select
v-model="filters.is_active"
placeholder="全部版本"
clearable
style="width: 130px"
>
<el-option label="仅当前生效" :value="1" />
<el-option label="仅历史版本" :value="0" />
</el-select>
</el-form-item>
<div class="filter-bar__actions">
<el-button type="primary" @click="search">查询</el-button>
<el-button @click="resetFilters">重置</el-button>
</div>
</el-form>
<div class="card">
<el-table
v-loading="loading"
:data="records"
stripe
empty-text="台账还没有核算行 —— 换个归属月看看,或先在回款管理里登记回款后再核算"
>
<el-table-column type="expand">
<template #default="{ row }">
<pre class="snapshot">{{ JSON.stringify(row.snapshot_json ?? {}, null, 2) }}</pre>
</template>
</el-table-column>
<el-table-column prop="settlement_month" label="归属月" width="90" />
<el-table-column label="业务编号" min-width="150">
<template #default="{ row }">
<span class="cell-code">{{ row.business_no }}</span>
</template>
</el-table-column>
<el-table-column prop="personnel_name" label="归属人员" width="100" />
<el-table-column prop="role_code" label="岗位" width="110" />
<el-table-column prop="receipt_date" label="回款日" width="100" />
<el-table-column label="天数" width="70" align="right">
<template #default="{ row }">
<span class="cell-num">{{ row.payment_days }}</span>
</template>
</el-table-column>
<el-table-column label="计提比例" min-width="90" align="right">
<template #default="{ row }">
<span class="cell-num">{{ row.rate_text }}</span>
</template>
</el-table-column>
<el-table-column label="折算毛利" min-width="100" align="right">
<template #default="{ row }">
<span class="cell-num">{{ row.converted_profit }}</span>
</template>
</el-table-column>
<el-table-column label="绩效金额" min-width="100" align="right">
<template #default="{ row }">
<span class="cell-num">{{ row.performance_amount }}</span>
</template>
</el-table-column>
<el-table-column label="应付绩效" min-width="100" align="right">
<template #default="{ row }">
<span class="cell-num">{{ row.payable_amount }}</span>
</template>
</el-table-column>
<el-table-column label="版本" width="108">
<template #default="{ row }">
<span class="cell-code">v{{ row.calc_version }}</span>
<!-- 历史版本用中性 pill:被替代不是异常,只是不再是生效版本 -->
<span v-if="row.is_active === 0" class="ys-pill">已替代</span>
</template>
</el-table-column>
<el-table-column label="操作" width="90" fixed="right" align="center">
<template #default="{ row }">
<el-button
v-if="row.is_active === 1 && row.calc_type === 1"
link
type="primary"
@click="openRecalc(row)"
>
重算
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页落进 card-foot:与表体同一张卡,卡底微渐变收边 -->
<div class="card-foot">
<AppPagination
:total="total"
:page-num="pageNum"
:page-size="pageSize"
@page-change="onPageChange"
@size-change="onSizeChange"
/>
</div>
</div>
<el-dialog v-model="recalcVisible" title="重算确认" width="460px">
<p class="recalc-warn">
重算会作废当前版本(v{{ recalcTarget?.calc_version }})并按当前参数落新版本,
历史版本可追溯。请填写重算原因。
</p>
<el-input
v-model="recalcReason"
type="textarea"
:rows="2"
maxlength="255"
placeholder="重算原因(必填,留痕)"
/>
<template #footer>
<el-button @click="recalcVisible = false">取消</el-button>
<el-button type="primary" @click="submitRecalc">确认重算</el-button>
</template>
</el-dialog>
</div>
</template>
<style scoped>
/* 筛选条(.filter-bar)、卡片与表尾(.card/.card-foot)的皮肤来自全局 index.css,
此处只保留本页特有的快照块与重算提示 */
.snapshot {
margin: 0;
padding: 10px;
font-size: 12px;
line-height: 1.6;
background: #f4f5f8;
border-radius: 6px;
white-space: pre-wrap;
word-break: break-all;
}
.recalc-warn {
margin: 0 0 10px;
font-size: 12px;
color: var(--ys-ink-3);
}
</style>