Files
writeOff/frontend/src/views/modules/FinancePage.vue
T
2026-05-20 18:21:39 +08:00

145 lines
6.1 KiB
Vue

<template>
<PageContainer title="财务管理">
<QueryToolbar>
<el-input-number v-model="form.projectId" :min="1" placeholder="项目ID" />
<el-input-number v-model="form.meetingId" :min="1" placeholder="会议ID" />
<el-input-number v-model="form.amountCent" :min="1" placeholder="金额(分)" />
<el-input v-model="form.paymentVoucherOssKey" placeholder="凭证OSS Key" class="w-input-lg" />
<el-input-number v-model="form.expectedAmountCent" :min="0" placeholder="对账应收(分)" />
<el-input v-model="form.lockReason" placeholder="锁账原因" class="w-input-md" />
<el-button v-if="canConfirm" type="primary" @click="handleConfirm">支付确认</el-button>
<el-button @click="load">刷新</el-button>
<el-button v-if="canExportLedger" @click="handleExportLedger">导出台账</el-button>
<el-button v-if="canReconcile" @click="handleReconcile">发起对账</el-button>
<el-button v-if="canLock" @click="handleLock">锁账</el-button>
<el-button v-if="canUnlock" @click="handleUnlock">解锁</el-button>
</QueryToolbar>
<el-table :data="rows" class="mt-md" empty-text="暂无数据">
<el-table-column prop="projectId" label="项目名称" min-width="160" :formatter="projectNameFormatter" />
<el-table-column prop="meetingId" label="会议名称" min-width="160" :formatter="meetingNameFormatter" />
<el-table-column prop="amountCent" label="支付金额(分)" />
<el-table-column prop="status" label="支付状态" width="140" :formatter="statusFormatter" />
</el-table>
<el-divider />
<el-row :gutter="12">
<el-col :span="12">
<SectionTitle>对账记录</SectionTitle>
<el-table :data="reconciliationRows" empty-text="暂无数据">
<el-table-column prop="projectId" label="项目名称" min-width="140" :formatter="projectNameFormatter" />
<el-table-column prop="expectedAmountCent" label="应收(分)" />
<el-table-column prop="actualAmountCent" label="实收(分)" />
<el-table-column prop="diffAmountCent" label="差额(分)" />
<el-table-column prop="resultStatus" label="结果" width="90" :formatter="statusFormatter" />
</el-table>
</el-col>
<el-col :span="12">
<SectionTitle>锁账记录</SectionTitle>
<el-table :data="lockLogRows" empty-text="暂无数据">
<el-table-column prop="projectId" label="项目名称" min-width="140" :formatter="projectNameFormatter" />
<el-table-column prop="lockStatus" label="状态" width="100" :formatter="statusFormatter" />
<el-table-column prop="reason" label="原因" />
<el-table-column prop="createdAt" label="时间" width="170" />
</el-table>
</el-col>
</el-row>
</PageContainer>
</template>
<script setup lang="ts">
import { computed, ref, onMounted } from "vue";
import PageContainer from "../../components/PageContainer.vue";
import QueryToolbar from "../../components/QueryToolbar.vue";
import SectionTitle from "../../components/SectionTitle.vue";
import { ElMessage } from "element-plus";
import {
confirmPayment,
exportFinanceLedger,
fetchFinanceProjects,
fetchFinanceReconciliationList,
lockFinance,
reconcileFinance,
unlockFinance,
} from "../../api/modules";
import { PERMS } from "../../constants/permissions";
import { useAuthStore } from "../../stores/auth";
import { toZhStatus } from "../../utils/status";
const authStore = useAuthStore();
const rows = ref<any[]>([]);
const canConfirm = computed(() => authStore.hasPermission(PERMS.finance.paymentConfirm));
const canExportLedger = computed(() => authStore.hasPermission(PERMS.finance.ledgerExport));
const canReconcile = computed(() => authStore.hasPermission(PERMS.finance.reconciliation));
const canLock = computed(() => authStore.hasPermission(PERMS.finance.lock));
const canUnlock = computed(() => authStore.hasPermission(PERMS.finance.unlock));
const reconciliationRows = ref<any[]>([]);
const lockLogRows = ref<any[]>([]);
const form = ref({
projectId: 1001,
meetingId: 2001,
amountCent: 100000,
paymentVoucherOssKey: "1/1001/2001/finance/202603/voucher.pdf",
expectedAmountCent: 100000,
lockReason: "月结锁账",
});
const statusFormatter = (_row: any, _column: any, value: unknown) => toZhStatus(value);
const projectNameFormatter = (row: any) => row?.projectName || row?.project || `项目#${row?.projectId || "-"}`;
const meetingNameFormatter = (row: any) => row?.meetingTopic || row?.meetingName || `会议#${row?.meetingId || "-"}`;
const load = async () => {
const resp = await fetchFinanceProjects();
rows.value = resp?.data?.list || [];
if (canReconcile.value || canLock.value || canUnlock.value) {
const recResp = await fetchFinanceReconciliationList({ projectId: form.value.projectId });
reconciliationRows.value = recResp?.data?.reconciliation || [];
lockLogRows.value = recResp?.data?.lockLogs || [];
}
};
const handleConfirm = async () => {
await confirmPayment({
...form.value,
idempotencyKey: `pay-${Date.now()}-${form.value.meetingId}`,
});
ElMessage.success("支付确认成功");
await load();
};
const handleExportLedger = async () => {
const resp = await exportFinanceLedger();
ElMessage.success(`导出成功,共 ${resp?.data?.total || 0} 条`);
};
const handleReconcile = async () => {
await reconcileFinance({
idempotencyKey: `recon-${Date.now()}-${form.value.projectId}`,
projectId: form.value.projectId,
expectedAmountCent: form.value.expectedAmountCent,
});
ElMessage.success("对账完成");
await load();
};
const handleLock = async () => {
await lockFinance({
idempotencyKey: `lock-${Date.now()}-${form.value.projectId}`,
projectId: form.value.projectId,
reason: form.value.lockReason || "锁账",
});
ElMessage.success("锁账成功");
await load();
};
const handleUnlock = async () => {
await unlockFinance({
idempotencyKey: `unlock-${Date.now()}-${form.value.projectId}`,
projectId: form.value.projectId,
reason: form.value.lockReason || "解锁",
});
ElMessage.success("解锁成功");
await load();
};
onMounted(load);
</script>