<template>
|
<div>
|
<basicContainer>
|
<avue-crud ref="crudRef" v-model="form" v-model:search="searchForm" :option="option" :data="tableData"
|
:table-loading="loading" :page="page" :row-class-name="tableRowClassName" @on-load="onLoad"
|
@search-change="searchChange" @search-reset="searchReset" @current-change="currentChange"
|
@size-change="sizeChange">
|
<template #businessType-form="{ row, disabled }">
|
<template v-if="disabled">
|
<span>{{ dictFormat(sys_account_business, row?.businessType || form?.businessType) }}</span>
|
</template>
|
</template>
|
<template #currency-form="{ row, disabled }">
|
<template v-if="disabled">
|
<span>{{ dictFormat(sys_currency, row?.currency || form?.currency) }}</span>
|
</template>
|
</template>
|
<template #settlementMethod-form="{ row, disabled }">
|
<template v-if="disabled">
|
<span>{{ dictFormat(sys_aging_settlement, row?.settlementMethod || form?.settlementMethod)
|
}}</span>
|
</template>
|
</template>
|
<template #menu-left>
|
<el-button type="warning" icon="Download" v-hasPermi="['cwgl:receivableBillManagement:export']"
|
style="background-color: #eb9e44; border-color: #eb9e44;" @click="handleExport">导出</el-button>
|
<el-button icon="Back" @click="goReturn">返回</el-button>
|
</template>
|
|
<template #menu="{ row }">
|
<div class="menu-btn-container">
|
<el-link type="primary" icon="Edit" v-hasPermi="['cwgl:receivableBillManagement:edit']"
|
@click="handleEdit(row)">编辑</el-link>
|
|
<el-link type="primary" icon="ChatDotSquare"
|
v-hasPermi="['cwgl:receivableBillManagement:paymentFeedbac']"
|
@click="handleFeedback(row)">回款进度反馈</el-link>
|
|
<el-link type="primary" icon="View" @click="crudRef.rowView(row)">查看</el-link>
|
|
<el-link type="primary" icon="el-icon-document"
|
v-hasPermi="['cwgl:receivableBillManagement:log']" @click="handleFlow(row)">日志</el-link>
|
</div>
|
</template>
|
</avue-crud>
|
</basicContainer>
|
|
<el-dialog v-model="editVisible" title="编辑账龄分析信息" width="800px" append-to-body>
|
<el-form :model="editForm" label-width="100px" border class="edit-form-container">
|
<el-row>
|
<el-col :span="12">
|
<el-form-item label="责任人"><el-input v-model="editForm.responsiblePerson" /></el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="责任领导"><el-input v-model="editForm.responsibleLeader" /></el-form-item>
|
</el-col>
|
</el-row>
|
<el-row>
|
<el-col :span="12">
|
<el-form-item label="结算方式">
|
<el-select v-model="editForm.settlementMethod" placeholder="请选结算方式" clearable>
|
<el-option v-for="dict in sys_aging_settlement" :key="dict.value" :label="dict.label"
|
:value="dict.value" />
|
</el-select>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="业务类型">
|
<el-select v-model="editForm.businessType" placeholder="请选业务类型" clearable>
|
<el-option v-for="dict in sys_account_business" :key="dict.value" :label="dict.label"
|
:value="dict.value" />
|
</el-select>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row>
|
<el-col :span="24">
|
<el-form-item label="备注"><el-input v-model="editForm.accountRemark" /></el-form-item>
|
</el-col>
|
</el-row>
|
<el-row>
|
<el-col :span="24">
|
<el-form-item label="推进要求">
|
<el-input v-model="editForm.promotionRequirement" type="textarea" :rows="4"
|
placeholder="请输入推进要求" />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</el-form>
|
<template #footer>
|
<div class="dialog-footer">
|
<el-button @click="editVisible = false">取消</el-button>
|
<el-button type="primary" @click="submitEdit">确定</el-button>
|
</div>
|
</template>
|
</el-dialog>
|
|
<el-dialog v-model="feedbackVisible" title="回款进度反馈" width="900px" append-to-body>
|
<div class="feedback-section">
|
<el-form ref="feedbackFormRef" :model="feedbackForm" :rules="feedbackRules" label-width="100px">
|
<el-row>
|
<el-col :span="10">
|
<el-form-item label="反馈时间" prop="feedbackTime">
|
<el-date-picker v-model="feedbackForm.feedbackTime" value-format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss" type="datetime" style="width: 100%" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="10">
|
<el-form-item label="反馈人" prop="createBy">
|
<el-input v-model="feedbackForm.createBy" />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<el-row>
|
<el-col :span="20">
|
<el-form-item label="反馈内容" prop="feedbackContent">
|
<el-input v-model="feedbackForm.feedbackContent" type="textarea" :rows="2" />
|
</el-form-item>
|
</el-col>
|
</el-row>
|
<div style="text-align: center; margin-bottom: 20px;">
|
<el-button @click="handleFeedbackClose">取消</el-button>
|
<el-button type="primary" @click="submitFeedback">确定</el-button>
|
</div>
|
</el-form>
|
</div>
|
<div class="history-section">
|
<h3 class="section-title">历史回款反馈</h3>
|
<el-table :data="historyFeedbackList" border stripe>
|
<el-table-column prop="createTime" label="反馈时间" width="180" align="center" />
|
<el-table-column prop="createBy" label="反馈人" width="120" align="center" />
|
<el-table-column prop="feedbackContent" label="反馈内容" header-align="center" />
|
</el-table>
|
</div>
|
</el-dialog>
|
|
<OperationLogModal ref="logModalRef" />
|
</div>
|
</template>
|
|
<script setup name="AgeAnalysis" lang="ts">
|
import { ref, reactive, onMounted, watch } from 'vue';
|
import { useRoute } from 'vue-router';
|
import router from "@/router";
|
import { agingAnalysisList, addPaymentFeedback, listPaymentFeedback, listAgingLog, receivableBillManagementAging } from "@/api/cwgl/analysisManagement";
|
import OperationLogModal from '@/components/OperationLogModal/index.vue';
|
import useCurrentInstance from "@/utils/useCurrentInstance";
|
|
const route = useRoute();
|
const crudRef = ref();
|
const loading = ref(false);
|
const tableData = ref([]);
|
const form = ref({});
|
const searchForm = ref<any>({}); // 搜索表单模型
|
/**
|
* 字典公共转换函数
|
*/
|
const dictFormat = (dict: any, value: any) => {
|
const dictData = Array.isArray(dict) ? dict : (dict?.value || []);
|
if (value === undefined || value === null || value === '') return '';
|
return proxy.selectDictLabel(dictData, value);
|
};
|
// 路由高亮标识
|
const highlightSystemNo = ref(route.query.systemNo || '');
|
const { proxy } = useCurrentInstance();
|
const { sys_aging_settlement, sys_currency,
|
sys_account_business } = proxy.useDict(
|
'sys_aging_settlement', 'sys_account_business', 'sys_currency'
|
);
|
const page = reactive({
|
pageSize: 10,
|
total: 0,
|
currentPage: 1,
|
});
|
|
/**
|
* Avue 配置项
|
*/
|
const option = ref({
|
searchShow: true,
|
searchMenuSpan: 6,
|
border: true,
|
addBtn: false,
|
editBtn: false,
|
delBtn: false,
|
viewBtn: false,
|
dicCache: true,
|
menuWidth: 220,
|
labelWidth: 150,
|
page: true,
|
pagination: true,
|
searchLabelWidth: 120,
|
showSummary: true,
|
// --- 合计行配置 ---
|
// 强制指定合计文案(如果 method 返回空,它作为保底)
|
sumText: '合计',
|
sumColumnList: [
|
// { label: '合计:', name: 'responsiblePerson', type: 'sum', },
|
{ name: 'totalAmount', type: 'sum' },
|
{ name: 'pendingAmount', type: 'sum' },
|
{ name: 'overdueAmount', type: 'sum' },
|
{ name: 'unInvoicedAmount', type: 'sum' },
|
],
|
|
column: [
|
{ label: '责任人', prop: 'responsiblePerson', width: 100 },
|
{ label: '责任领导', prop: 'responsibleLeader', width: 100 },
|
{ label: '客户名称', prop: 'customerName', width: 220, }, // 开启搜索
|
{
|
label: '结算方式', prop: 'settlementMethod', width: 120, dicUrl: '/system/dict/data/type/sys_aging_settlement',
|
formatter: (row: any, value: any) => dictFormat(sys_aging_settlement, value)
|
},
|
{ label: '币种', prop: 'currency', width: 80, dicUrl: '/system/dict/data/type/sys_currency', },
|
// { label: '业务类型', prop: 'businessType', dicUrl: '/system/dict/data/type/sys_account_business', width: 120 },
|
{
|
label: '业务类型',
|
prop: 'businessType',
|
type: 'select',
|
dataType: 'string',
|
dicUrl: '/system/dict/data/type/sys_account_business',
|
display: true,
|
detail: true,
|
formatter: (row: any, value: any) => dictFormat(sys_account_business, value)
|
},
|
{ label: '账单系统编号', prop: 'systemNo', width: 200, search: true },
|
// 金额列
|
{ label: '含暂估应收账款余额', prop: 'totalAmount', width: 160, type: 'number' },
|
{ label: '已确认应收账款金额', prop: 'totalAmount', width: 160, type: 'number' },
|
{ label: '应收账款余额', prop: 'pendingAmount', width: 140, type: 'number' },
|
|
{ label: '30天内到期', prop: 'dueIn30Days', width: 120 },
|
{ label: '逾期金额', prop: 'overdueAmount', width: 120, className: 'text-danger' },
|
{ label: '逾期1~30天', prop: 'overdue1To30Days', width: 120 },
|
{ label: '逾期31~60天', prop: 'overdue31To60Days', width: 120 },
|
{ label: '逾期61~90天', prop: 'overdue61To90Days', width: 120 },
|
{ label: '逾期91~180天', prop: 'overdue91To180Days', width: 130 },
|
{ label: '逾期181~365天', prop: 'overdue181To365Days', width: 140 },
|
{ label: '逾期1年以上', prop: 'overdueOver1Year', width: 130 },
|
|
{ label: '推进要求', prop: 'promotionRequirement', width: 250, overHidden: true },
|
{ label: '备注', prop: 'accountRemark', width: 150 },
|
{
|
label: '逾期金额所属期', width: 200,
|
formatter: (row) => {
|
if (row.billingStartDate && row.billingEndDate) {
|
return `${row.billingStartDate} 至 ${row.billingEndDate}`;
|
}
|
return row.periodType || '-';
|
}
|
},
|
{ label: '开票日期', prop: 'invoiceDate', width: 120 },
|
{ label: '未开票金额', prop: 'pendingAmount', width: 120 }
|
]
|
});
|
|
/**
|
* 核心:数据加载与空值过滤
|
*/
|
/**
|
* 核心:数据加载与空值过滤
|
*/
|
const onLoad = (params = {}) => {
|
loading.value = true;
|
|
// 1. 获取路由参数(仅在 searchForm 为空,即初始化时尝试回显)
|
const routeCustomerName = route.query.customerName;
|
if (routeCustomerName && !searchForm.value.customerName) {
|
searchForm.value.customerName = routeCustomerName;
|
}
|
|
// 2. 构建分页基础参数
|
const query: any = {
|
pageNum: page.currentPage,
|
pageSize: page.pageSize
|
};
|
|
// 3. 合并参数逻辑:
|
// params 是 Avue 内部传出的(包含点击搜索按钮后的参数)
|
// searchForm.value 是双向绑定的(包含路由回显的参数)
|
const combinedParams = { ...searchForm.value, ...params };
|
|
// 4. 过滤并合并到 query
|
Object.keys(combinedParams).forEach(key => {
|
const val = combinedParams[key];
|
if (val !== '' && val !== null && val !== undefined) {
|
query[key] = val;
|
}
|
});
|
|
// 5. 调用接口
|
agingAnalysisList(query).then(res => {
|
tableData.value = res.rows;
|
page.total = res.total;
|
loading.value = false;
|
|
// 高亮滚动逻辑
|
// if (highlightSystemNo.value) {
|
// setTimeout(() => {
|
// const target = document.querySelector('.highlight-row');
|
// target?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
// }, 500);
|
// }
|
}).catch(() => {
|
loading.value = false;
|
});
|
};
|
|
/**
|
* 页面初始化:处理路由带过来的客户名称
|
*/
|
/**
|
* 页面初始化:处理路由带过来的客户名称
|
*/
|
onMounted(() => {
|
const routeCustomerName = route.query.customerName;
|
if (routeCustomerName) {
|
// 1. 赋值给 searchForm,这会同步到 avue-crud 的搜索栏输入框
|
searchForm.value.customerName = routeCustomerName;
|
|
// 2. 立即执行加载,传入该参数
|
// 注意:这里最好重置页码为1
|
page.currentPage = 1;
|
onLoad({ customerName: routeCustomerName });
|
} else {
|
onLoad();
|
}
|
});
|
|
const tableRowClassName = ({ row }: any) => {
|
// return (highlightSystemNo.value && row.systemNo === highlightSystemNo.value) ? 'highlight-row' : '';
|
};
|
|
const searchChange = (params: any, done: Function) => {
|
// 1. 如果输入了账单系统编号,仅进行前端定位高亮
|
if (params.systemNo) {
|
highlightSystemNo.value = params.systemNo;
|
|
// 2. 在当前页数据中查找
|
const exists = tableData.value.some(row => row.systemNo === params.systemNo);
|
onLoad(params);
|
// if (exists) {
|
// setTimeout(() => {
|
// const target = document.querySelector('.highlight-row');
|
// if (target) {
|
// target.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
// }
|
// }, 100);
|
// } else {
|
// }
|
|
// 4. 注意:这里不调用 onLoad(),所以不会请求后端
|
} else {
|
// 如果清空了编号或者搜索其他字段,则执行正常请求
|
page.currentPage = 1;
|
onLoad(params);
|
}
|
|
done(); // 关闭搜索栏加载状态
|
};
|
|
const searchReset = () => {
|
highlightSystemNo.value = '';
|
|
// 1. 先清空所有搜索项
|
searchForm.value = {};
|
|
// 2. 检查路由中是否有初始的客户名称,如果有则还原
|
const routeCustomerName = route.query.customerName;
|
if (routeCustomerName) {
|
searchForm.value.customerName = routeCustomerName;
|
// 3. 执行带初始参数的查询
|
onLoad({ customerName: routeCustomerName });
|
} else {
|
// 如果路由没值,直接执行普通加载
|
onLoad();
|
}
|
};
|
|
const currentChange = (val: number) => {
|
page.currentPage = val;
|
onLoad();
|
};
|
|
const sizeChange = (val: number) => {
|
page.pageSize = val;
|
onLoad();
|
};
|
|
// 修改 goReturn 方法
|
const goReturn = () => router.push('/basic/analysisManagement');
|
// --- 弹窗逻辑 ---
|
const editVisible = ref(false);
|
const editForm = ref<any>({});
|
const handleEdit = (row: any) => {
|
editForm.value.responsiblePerson = row.responsiblePerson
|
editForm.value.id = row.id;
|
editForm.value.responsibleLeader = row.responsibleLeader;
|
editForm.value.settlementMethod = row.settlementMethod;
|
editForm.value.businessType = row.businessType;
|
editForm.value.promotionRequirement = row.promotionRequirement;
|
editForm.value.remark = row.remark;
|
|
|
|
editVisible.value = true;
|
};
|
const submitEdit = () => {
|
console.log("提交编辑:", editForm.value);
|
receivableBillManagementAging(editForm.value).then((res) => {
|
if (res.code == 200) {
|
proxy.$modal.msgSuccess(res.msg);
|
onLoad({ customerName: route.query.customerName });
|
editVisible.value = false;
|
|
}
|
})
|
};
|
|
const feedbackVisible = ref(false);
|
const feedbackForm = ref({ feedbackTime: '', createBy: '', feedbackContent: '' });
|
const feedbackRules = {
|
feedbackTime: [{ required: true, message: '请选择反馈时间', trigger: 'change' }],
|
createBy: [{ required: true, message: '请输入反馈人', trigger: 'blur' }],
|
feedbackContent: [{ required: true, message: '请输入反馈内容', trigger: 'blur' }]
|
};
|
const historyFeedbackList = ref([]);
|
const feedbackFormRef = ref();
|
|
const handleFeedback = (row: any) => {
|
feedbackForm.value = { headId: row.id };
|
feedbackVisible.value = true;
|
getHistory(row.id);
|
};
|
const handleFeedbackClose = () => {
|
feedbackFormRef.value?.resetFields();
|
feedbackVisible.value = false;
|
};
|
const heardIds = ref<string>('')
|
const submitFeedback = () => {
|
feedbackFormRef.value?.validate((valid: boolean) => {
|
if (valid) {
|
addPaymentFeedback(feedbackForm.value).then((res) => {
|
if (res.code == 200) {
|
feedbackForm.value = {
|
headId: feedbackForm.value.headId
|
}
|
proxy.$modal.msgSuccess(res.msg);
|
getHistory(heardIds.value)
|
// feedbackVisible.value = false;
|
}
|
})
|
}
|
});
|
};
|
const getHistory = (id: string) => {
|
heardIds.value = id
|
historyFeedbackList.value = []
|
listPaymentFeedback({ headId: id }).then((res) => {
|
if (res.code == 200) {
|
historyFeedbackList.value = res.rows;
|
}
|
});
|
};
|
|
const logModalRef = ref<any>(null);
|
const handleFlow = (row: any) => {
|
// 这里可以从 row 中直接获取日志,或者调用后端接口查询
|
//
|
listAgingLog({ headId: row.id }).then((res) => {
|
if (res.code == 200) {
|
logModalRef.value.open(res.rows, 'payable');
|
}
|
});
|
}
|
/* 导出按钮 */
|
function handleExport() {
|
proxy.download(
|
'/cwgl/receivableBillManagement/agingAnalysis/export',
|
{
|
...form.value,
|
},
|
`report_${new Date().getTime()}.xlsx`
|
)
|
}
|
</script>
|
|
<style scoped>
|
/* 合计行样式 */
|
:deep(.el-table__footer-wrapper) {
|
font-weight: bold;
|
background-color: #f5f7fa;
|
}
|
|
/* 高亮行逻辑 */
|
:deep(.el-table .highlight-row) {
|
background-color: #fff9c4 !important;
|
}
|
|
:deep(.el-table .highlight-row td) {
|
background-color: transparent !important;
|
}
|
|
/* 逾期金额标红 */
|
:deep(.text-danger) {
|
color: #f56c6c;
|
font-weight: bold;
|
}
|
|
.section-title {
|
margin: 20px 0 10px;
|
padding-left: 10px;
|
border-left: 4px solid #409eff;
|
}
|
|
/* 操作列按钮间距 */
|
.menu-btn-container .el-link {
|
margin-right: 12px;
|
}
|
|
.menu-btn-container .el-link:last-child {
|
margin-right: 0;
|
}
|
|
/* 强制在合计行的第一个非复选框单元格(责任人列)显示文案 */
|
:deep(.el-table__footer-wrapper .el-table__cell.avue-crud__color .cell),
|
:deep(.el-table__footer-wrapper .el-table__cell:nth-child(2) .cell) {
|
visibility: visible !important;
|
}
|
|
/* 如果是多选框列导致的遮挡,通过伪元素强行在第二列注入 */
|
:deep(.el-table__footer-wrapper tr td:nth-child(2) .cell::before) {
|
content: '合计';
|
font-weight: bold;
|
color: #333;
|
}
|
</style>
|