<template>
|
<el-dialog v-model="visible" title="生成应收账单" width="1000px" destroy-on-close @closed="handleClosed">
|
<div class="dialog-content">
|
<h3 class="section-title">是否根据以下数据生成应收账单</h3>
|
|
<el-form :model="formData" ref="formRef" label-position="left" label-width="80px">
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="账单名称" prop="billName"
|
:rules="[{ required: true, message: '请输入账单名称', trigger: 'blur' }]">
|
<el-input v-model="formData.billName" placeholder="请输入账单名称" />
|
</el-form-item>
|
</el-col>
|
|
<el-col :span="12">
|
<el-form-item label="账单类型" prop="billType"
|
:rules="[{ required: true, message: '请选择账单类型', trigger: 'change' }]">
|
<el-radio-group v-model="formData.billType">
|
<el-radio :label="0">人民币账单</el-radio>
|
<el-radio :label="1">港币账单</el-radio>
|
</el-radio-group>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</el-form>
|
<el-descriptions :column="3" border class="mb-6">
|
<el-descriptions-item label="单据数量">
|
{{ statistics.documentCount }}
|
</el-descriptions-item>
|
<el-descriptions-item label="汇率 (港币兑人民币)">
|
{{ statistics.rate }}
|
</el-descriptions-item>
|
<!-- <el-descriptions-item label="汇率 (人民币兑港币)">
|
{{ statistics.rateInverse || '-' }}
|
</el-descriptions-item> -->
|
<el-descriptions-item label="应收金额">
|
<span class="text-bold">{{ statistics.totalReceivableAmount }}</span>
|
</el-descriptions-item>
|
<el-descriptions-item label="应收金额 (以人民币计)">
|
<span class="text-primary">{{ statistics.totalAmountRmb }}</span>
|
</el-descriptions-item>
|
<el-descriptions-item label="应收金额 (以港币计)">
|
<span class="text-success">{{ statistics.totalAmountHkd }}</span>
|
</el-descriptions-item>
|
</el-descriptions>
|
|
<div class="table-header">
|
<h3 class="section-title">账单明细</h3>
|
</div>
|
<el-table :data="detailList" border stripe height="300px" style="width: 100%">
|
<el-table-column prop="systemNo" align="center" label="系统编号" width="140" />
|
<el-table-column prop="sourceSystem" align="center" label="来源系统" width="120">
|
<template #default="scope">
|
{{ dictFormat(sys_system, scope.row.sourceSystem) }}
|
</template>
|
</el-table-column><!-- sys_system -->
|
<el-table-column prop="businessSector" align="center" label="业务板块" width="120">
|
<template #default="scope">
|
{{ dictFormat(sys_business, scope.row.businessSector) }}
|
</template>
|
</el-table-column>
|
<!-- sys_business -->
|
<el-table-column prop="documentType" align="center" label="单据类型" width="120">
|
<template #default="scope">
|
{{ dictFormat(sys_receipts, scope.row.documentType) }}
|
</template>
|
</el-table-column><!-- sys_receipts -->
|
<el-table-column prop="documentNo" align="center" label="单据编号" width="150" />
|
<el-table-column prop="customerName" align="center" label="客户名称" width="150" />
|
<el-table-column prop="projectName" align="center" label="项目名称" width="150" />
|
<el-table-column prop="receivableAmount" align="center" label="应收金额" />
|
<el-table-column prop="currency" align="center" label="币制" width="100">
|
<template #default="scope">
|
{{ dictFormat(sys_currency, scope.row.currency) }}
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
|
<template #footer>
|
<el-button @click="cancel">取消</el-button>
|
<el-button type="primary" @click="handleConfirm">确认生成</el-button>
|
</template>
|
</el-dialog>
|
</template>
|
|
<script setup lang="ts">
|
import { ref, reactive } from 'vue';
|
import { ElMessage } from 'element-plus';
|
import CustomerSelectDialog from '../CustomerSelectDialog/index';
|
import useCurrentInstance from "@/utils/useCurrentInstance";
|
|
const { proxy } = useCurrentInstance()
|
const { sys_system, sys_business, sys_receipts, sys_supplier, sys_whether_type, sys_currency } = proxy.useDict(
|
'sys_system',
|
'sys_business',
|
'sys_receipts',
|
'sys_supplier',
|
'sys_whether_type', 'sys_currency'
|
)
|
const dictFormat = (dict: any, value: any) => {
|
return proxy.selectDictLabel(dict, value);
|
}
|
// 定义接口结构
|
interface StatisticsData {
|
documentCount: number;
|
rate: number;
|
rateInverse?: number; // 转换后的反向汇率
|
totalReceivableAmount: number;
|
totalAmountRmb: number;
|
totalAmountHkd: number;
|
}
|
const formRef = ref();
|
const visible = ref(false);
|
const loading = ref(false);
|
const detailList = ref<any[]>([]);
|
const statistics = ref<Partial<StatisticsData>>({});
|
|
const formData = reactive({
|
billType: 0,
|
billName: ''
|
});
|
|
const emit = defineEmits(['confirm']);
|
|
// 暴露给父组件的打开方法
|
const open = (data: any, selectionList: any[]) => {
|
visible.value = true;
|
if (data) {
|
Object.assign(statistics.value, data);
|
|
if (selectionList && selectionList.length > 0) {
|
detailList.value = selectionList.flatMap(item => {
|
if (!item.receivableAmountStr) return [item];
|
|
// 1. 拆分多个币种字符串
|
const amountParts = item.receivableAmountStr.trim().split(/\s+/);
|
|
return amountParts.map(part => {
|
// 2. 正则解析提取数值和币种名称
|
// ([\d.]+) 匹配数字和小数点
|
// ([\u4e00-\u9fa5]+) 匹配中文字符(币种)
|
const match = part.match(/([\d.]+)([\u4e00-\u9fa5]+)/);
|
|
let amount = item.receivableAmount; // 默认值
|
let currencyValue = item.currency; // 默认值
|
|
if (match) {
|
amount = parseFloat(match[1]); // 提取的数字
|
const currencyName = match[2]; // 提取的币种文字,如 "港币"
|
|
// 3. 根据提取的文字匹配字典中的 Value
|
// 假设字典 sys_currency.value: 0 是人民币, 1 是港币 (请根据您实际字典值调整)
|
if (currencyName.includes('人民币')) {
|
currencyValue = 'RMB'; // 对应字典的人民币value
|
} else if (currencyName.includes('港币')) {
|
currencyValue = 'HKD'; // 对应字典的港币value
|
}
|
}
|
|
// 4. 返回新对象,覆盖金额和币制
|
return {
|
...item,
|
receivableAmount: amount, // 赋值提取的数字
|
currency: currencyValue, // 赋值匹配到的字典ID
|
receivableAmountStr: part // 保持拆分后的文本
|
};
|
});
|
});
|
} else {
|
detailList.value = [];
|
}
|
}
|
};
|
// 3. 确认生成按钮逻辑
|
const handleConfirm = async () => {
|
if (!formRef.value) return;
|
|
// 手动触发表单校验
|
await formRef.value.validate((valid: boolean) => {
|
if (valid) {
|
loading.value = true;
|
emit('confirm', statistics.value, formData);
|
|
// 注意:通常在这里不直接关闭,而是等父组件请求成功后由父组件调用 cancel()
|
} else {
|
console.log('校验失败');
|
}
|
});
|
};
|
|
const handleClosed = () => {
|
// 重置数据
|
detailList.value = [];
|
statistics.value = {};
|
formData.billType = 0;
|
formData.billName = ''; // 记得重置名称
|
if (formRef.value) formRef.value.resetFields(); // 清除校验残余
|
};
|
const cancel = () => {
|
|
visible.value = false;
|
handleClosed()
|
};
|
// 暴露方法
|
defineExpose({ open, cancel });
|
</script>
|
|
<style scoped>
|
.section-title {
|
font-size: 16px;
|
font-weight: bold;
|
margin-bottom: 20px;
|
color: #303133;
|
}
|
|
.mb-6 {
|
margin-bottom: 24px;
|
}
|
|
.table-header {
|
margin-top: 20px;
|
border-top: 1px solid #ebeef5;
|
padding-top: 20px;
|
}
|
|
.text-bold {
|
font-weight: bold;
|
}
|
|
.text-primary {
|
color: #409eff;
|
font-weight: bold;
|
}
|
|
.text-success {
|
color: #67c23a;
|
font-weight: bold;
|
}
|
|
:deep(.el-descriptions__label) {
|
width: 180px;
|
background-color: #f5f7fa;
|
}
|
</style>
|