token 补齐与 EP 形体基准、全站版式工具类(.page-head/.card 系/.filter-bar/.ys-pill 等)、布局与登录页、工作台 Bento 与图表主题、打印稿纸、全部业务页统一到全局类;StatusTag 改 pill、AppPagination 加 background。 浏览器实测修复:表格行高收到 1.5;月结生命周期条圆点按阶段着色并加操作提示;工作台空态垂直居中;状态/结果列宽按胶囊内容给足(96/108/120),消除 Element Plus 省略号伪『小点』。设计稿反向同步圆点着色与流程条提示。
236 lines
8.2 KiB
Vue
236 lines
8.2 KiB
Vue
<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>
|