优化合同个管理和产品管理相关功能

This commit is contained in:
2026-09-12 23:29:45 +08:00
parent 2b1874943f
commit 4455b95e37
92 changed files with 5367 additions and 3144 deletions
@@ -0,0 +1,151 @@
<template>
<div class="contract-payback">
<div class="section-head">
<span class="section-title">回款计划</span>
<el-button type="primary" size="small" :icon="Plus" @click="openCreate">新建回款计划</el-button>
</div>
<el-table v-loading="loading" :data="list" border size="small">
<el-table-column label="状态" width="90" align="center">
<template #default="{ row }">
<el-tag :type="paybackStatusTag(row.status)" size="small">{{ paybackStatusText(row.status) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="回款周期" width="90" align="center">
<template #default="{ row }">
<el-tag size="small" effect="plain">{{ paybackCycleText(row.plan_type) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="回款进度" min-width="140" align="center">
<template #default="{ row }">
<el-progress :percentage="paybackPercent(row)" :stroke-width="10" />
</template>
</el-table-column>
<el-table-column label="计划回款总额" width="130" align="right">
<template #default="{ row }">{{ formatMoney(row.total_amount) }}</template>
</el-table-column>
<el-table-column label="已回款" width="120" align="right">
<template #default="{ row }">{{ formatMoney(row.received_amount) }}</template>
</el-table-column>
<el-table-column label="回款方式" width="100" align="center">
<template #default="{ row }">{{ paybackMethodText(row.pay_method) }}</template>
</el-table-column>
<el-table-column label="负责人" width="90">
<template #default="{ row }">{{ row.owner_user_name || "-" }}</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center">
<template #default="{ row }">
<el-button link type="primary" size="small" @click="openDetail(row)">详情</el-button>
</template>
</el-table-column>
<template #empty>
<el-empty description="该合同暂无回款计划,点击右上角新建" :image-size="60" />
</template>
</el-table>
<!-- 新建 / 编辑回款计划(预设当前合同) -->
<PaybackCreate
v-model:visible="createVisible"
:preset-contract="contract"
:edit-data="editRow"
@success="handleSaved"
/>
<!-- 回款计划详情 -->
<PaybackDetail v-model:visible="detailVisible" :payback="currentRow" @edit="handleEdit" />
</div>
</template>
<script setup>
import { ref } from "vue";
import { Plus } from "@element-plus/icons-vue";
import { getPaybackList } from "@/api/crmPayback";
import PaybackCreate from "../../payback/components/create.vue";
import PaybackDetail from "../../payback/components/detail.vue";
import {
paybackCycleText,
paybackMethodText,
paybackStatusText,
paybackStatusTag,
formatMoney,
} from "../../dict";
const props = defineProps({
/** 当前合同详情对象(含 id / contract_name / contract_no / parties / summary) */
contract: { type: Object, default: null },
});
const emit = defineEmits(["count", "saved"]);
const loading = ref(false);
const list = ref([]);
const createVisible = ref(false);
const detailVisible = ref(false);
const currentRow = ref(null);
const editRow = ref(null);
/** 回款进度百分比 */
function paybackPercent(row) {
const total = Number(row.total_amount) || 0;
if (!total) return 0;
return Math.min(100, Math.round((Number(row.received_amount) / total) * 100));
}
function openCreate() {
editRow.value = null;
createVisible.value = true;
}
function openDetail(row) {
currentRow.value = { ...row };
detailVisible.value = true;
}
/** 详情内点击编辑:关闭详情并打开编辑弹窗 */
function handleEdit(row) {
detailVisible.value = false;
editRow.value = { ...row };
createVisible.value = true;
}
function handleSaved() {
reload();
emit("saved");
}
/** 供父组件(detail.vue)在抽屉打开时调用 */
async function reload() {
const contractId = props.contract?.id;
if (!contractId) {
list.value = [];
emit("count", 0);
return;
}
loading.value = true;
try {
const res = await getPaybackList({ contract_id: contractId, page: 1, pageSize: 100 });
list.value = res?.data?.list || [];
} catch (e) {
list.value = [];
} finally {
loading.value = false;
emit("count", list.value.length);
}
}
defineExpose({ reload });
</script>
<style lang="less" scoped>
.section-head {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
.section-title {
font-size: 14px;
font-weight: 600;
}
}
</style>