<template>
|
<el-dialog v-model="visible" :title="dialogTitle" width="1200px" destroy-on-close>
|
<el-descriptions title="账单基本信息" :column="3" border class="mb-5">
|
<el-descriptions-item label="系统编号">{{ billInfo.systemNo }}</el-descriptions-item>
|
<el-descriptions-item label="账单名称">{{ billInfo.billName }}</el-descriptions-item>
|
<el-descriptions-item :label="type.includes('应收') ? '客户名称' : '供应商名称'">
|
{{ type.includes('应收') ? billInfo.customerName : billInfo.supplierName }}
|
</el-descriptions-item>
|
<el-descriptions-item label="单据数量">{{ billInfo.documentCount }}</el-descriptions-item>
|
<el-descriptions-item label="应结算金额"><span class="text-bold">{{ billInfo.totalAmount
|
}}</span></el-descriptions-item>
|
<el-descriptions-item label="币制">{{ billInfo.currency }}</el-descriptions-item>
|
<el-descriptions-item label="减免金额">{{ billInfo.discountAmount }}</el-descriptions-item>
|
<el-descriptions-item :label="type.includes('应收') ? '已收金额' : '已付金额'">{{ billInfo.receivedAmount
|
}}</el-descriptions-item>
|
<el-descriptions-item :label="type.includes('应收') ? '待收金额' : '待付金额'">
|
<span class="text-danger">{{ billInfo.pendingAmount }}</span>
|
</el-descriptions-item>
|
<el-descriptions-item label="周期类型">{{ billInfo.periodType }}</el-descriptions-item>
|
<el-descriptions-item label="业务期间">{{ billInfo.businessStartDate }} ~ {{ billInfo.businessEndDate
|
}}</el-descriptions-item>
|
<el-descriptions-item label="账单周期">{{ billInfo.billingStartDate }} ~ {{ billInfo.billingEndDate
|
}}</el-descriptions-item>
|
</el-descriptions>
|
|
<div v-if="InvoiceDetails !== '开票明细'" class="section-header">
|
<h3 class="section-title">发票商品明细</h3>
|
<el-button type="primary" size="small" icon="Plus" @click="addItemRow">新增</el-button>
|
</div>
|
|
<el-table v-if="InvoiceDetails !== '开票明细'" :data="form.invoiceBillDetails" border class="mb-5">
|
<el-table-column label="商品名称" prop="goodsName">
|
<template #default="{ row }">
|
<el-input v-if="!row.isSaved && InvoiceDetails !== '开票明细'" v-model="row.goodsName" placeholder="商品名称" />
|
<span v-else>{{ row.goodsName }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="单价" prop="price" width="200">
|
<template #default="{ row }">
|
<el-input-number v-if="!row.isSaved && InvoiceDetails !== '开票明细'" v-model="row.price" :precision="2"
|
style="width:100%" />
|
<span v-else>{{ row.price }}</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="含税标志" prop="withTaxFlag" width="200">
|
<template #default="{ row }">
|
<el-radio-group v-model="row.withTaxFlag" :disabled="row.isSaved || InvoiceDetails === '开票明细'">
|
<el-radio :label="0">含税</el-radio>
|
<el-radio :label="1">不含税</el-radio>
|
</el-radio-group>
|
</template>
|
</el-table-column>
|
<el-table-column v-if="InvoiceDetails !== '开票明细'" label="操作" width="150" align="center">
|
<template #default="{ row, $index }">
|
<el-button v-if="!row.isSaved" type="primary" link @click="saveItemRow(row)">保存</el-button>
|
<el-button v-else type="primary" link @click="row.isSaved = false">修改</el-button>
|
<el-button type="danger" link @click="form.invoiceBillDetails.splice($index, 1)">删除</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<template v-if="InvoiceDetails !== '开票明细'">
|
<h3 class="section-title">本次开票信息</h3>
|
<el-form ref="formRef" :model="form" :rules="formRules" label-width="130px">
|
<el-row :gutter="20">
|
<el-col :span="8">
|
<!-- <el-form-item label="抬头公司" prop="invoiceCompanyName">
|
<el-input v-model="form.invoiceCompanyName" placeholder="请输入" />
|
</el-form-item> -->
|
<el-form-item label="抬头公司" prop="invoiceCompanyName">
|
<el-input v-model="form.invoiceCompanyName" @click="openBankDialog" readonly
|
placeholder="请输入抬头公司">
|
<template #append>
|
<el-button icon="Search" @click="openBankDialog" />
|
</template>
|
</el-input>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="社会信用代码" prop="invoiceCreditCode">
|
<el-input v-model="form.invoiceCreditCode" placeholder="请输入" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="8">
|
<el-form-item label="发票类型" prop="invoiceType">
|
<el-select v-model="form.invoiceType" placeholder="请选择" style="width:100%">
|
<el-option v-for="d in sys_invoice_type" :key="d.value" :label="d.label" :value="d.value" />
|
</el-select>
|
</el-form-item>
|
</el-col>
|
<el-col :span="8"><el-form-item label="开户银行"><el-input
|
v-model="form.invoiceBankName" /></el-form-item></el-col>
|
<el-col :span="8"><el-form-item label="基本账号"><el-input v-model="form.invoiceBankNo" /></el-form-item></el-col>
|
<el-col :span="8"><el-form-item label="已开票金额"><el-input v-model="billInfo.invoicedAmount"
|
disabled /></el-form-item></el-col>
|
<el-col :span="8">
|
<el-form-item label="本次开票金额" prop="currentInvoicedAmount">
|
<el-input-number v-model="form.currentInvoicedAmount" disabled :precision="2" style="width:100%" />
|
</el-form-item>
|
</el-col>
|
<el-col :span="16"><el-form-item label="发票备注"><el-input v-model="form.invoiceRemark"
|
type="textarea" /></el-form-item></el-col>
|
</el-row>
|
</el-form>
|
</template>
|
|
<template v-else>
|
<h3 class="section-title">开票明细记录</h3>
|
<el-table v-loading="loading" :data="recordList" border stripe>
|
<el-table-column label="发票类型" prop="invoiceType" align="center" />
|
<el-table-column label="发票编号" prop="invoiceNo" align="center" />
|
<el-table-column label="开票状态" prop="status" align="center" />
|
<el-table-column label="开票日期" prop="invoiceDate" align="center" />
|
<el-table-column label="发票金额" prop="invoiceAmount" align="center" />
|
<el-table-column label="购买方名称" prop="buyerName" align="center" />
|
<el-table-column label="销售方名称" prop="sellerName" align="center" />
|
</el-table>
|
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
|
v-model:limit="queryParams.pageSize" @pagination="fetchRecords" />
|
</template>
|
|
<template #footer>
|
<el-button v-if="InvoiceDetails !== '开票明细'" type="primary" @click="handleConfirm">确定开票</el-button>
|
<el-button @click="openIshpw">关闭</el-button>
|
</template>
|
</el-dialog>
|
|
<invoiceManagementDialog v-model:visible="supplierBankIsible" :default-selected-id="form.invoiceInfoId"
|
@confirm="handleBankCardfirm" />
|
</template>
|
|
<script setup lang="ts">
|
import { ref, reactive, computed, watch } from 'vue'
|
import useCurrentInstance from "@/utils/useCurrentInstance";
|
import invoiceManagementDialog from "../invoiceManagementDialog/index.vue";
|
|
const { proxy } = useCurrentInstance();
|
const { sys_invoice_type } = proxy.useDict('sys_invoice_type');
|
|
const props = defineProps<{
|
type: string, // '应收账单开票' 等
|
InvoiceDetails: string // '本次开票信息' 或 '开票明细'
|
}>()
|
|
const visible = ref(false)
|
const loading = ref(false)
|
const billInfo = ref<any>({})
|
const recordList = ref([])
|
const total = ref(0)
|
const formRef = ref()
|
|
const dialogTitle = computed(() => {
|
return props.InvoiceDetails === '开票明细' ? `${props.type}记录` : props.type;
|
})
|
|
const form = reactive({
|
invoiceCompanyName: '',
|
invoiceCreditCode: '',
|
invoiceType: '',
|
currentInvoicedAmount: 0,
|
invoiceBillDetails: [] as any[]
|
})
|
|
const formRules = {
|
invoiceCompanyName: [{ required: true, message: '请输入抬头', trigger: 'blur' }],
|
invoiceType: [{ required: true, message: '请选择类型', trigger: 'change' }],
|
// currentInvoicedAmount: [{ required: true, message: '请输入金额', trigger: 'blur' }]
|
}
|
|
const queryParams = reactive({ pageNum: 1, pageSize: 10, billId: '' })
|
|
// 模拟获取开票记录
|
const fetchRecords = async () => {
|
loading.value = true;
|
// 这里根据 props.type 调用对应的 API (应收/应付记录)
|
// const res = await listInvoiceRecords(queryParams);
|
// recordList.value = res.rows;
|
// total.value = res.total;
|
loading.value = false;
|
}
|
|
const open = (row: any) => {
|
visible.value = true;
|
billInfo.value = row;
|
recordList.value = row.recordList || [];
|
queryParams.billId = row.id;
|
|
if (props.InvoiceDetails === '开票明细') {
|
fetchRecords();
|
} else {
|
// 初始化空行
|
form.invoiceBillDetails = [{ goodsName: '', price: 0, withTaxFlag: 0, isSaved: false }];
|
}
|
}
|
|
// 5. 新增行初始化
|
const addItemRow = () => {
|
form.invoiceBillDetails.push({
|
goodsName: '',
|
price: 0,
|
withTaxFlag: 0,
|
isSaved: false
|
});
|
}
|
const emit = defineEmits(['success']);
|
const handleConfirm = async () => {
|
if (!formRef.value) return;
|
|
// 校验一:商品明细是否全部保存,且是否有未填项
|
if (form.invoiceBillDetails.length === 0) {
|
proxy.$modal.msgError("请至少添加一条商品明细");
|
return;
|
}
|
|
const hasUnsaved = form.invoiceBillDetails.some(item => !item.isSaved);
|
if (hasUnsaved) {
|
proxy.$modal.msgError("请先保存所有商品明细项");
|
return;
|
}
|
|
// 校验二:表单必填项校验
|
await formRef.value.validate((valid: boolean) => {
|
if (valid) {
|
// 构建提交数据,过滤掉 UI 使用的 isSaved 属性
|
const submitData = {
|
...form,
|
id: billInfo.value.id,
|
invoiceBillDetails: form.invoiceBillDetails.map(({ isSaved, ...rest }) => rest)
|
};
|
console.log("提交数据:", submitData);
|
emit('success', submitData);
|
// proxy.$modal.msgSuccess("开票成功");
|
// visible.value = false;
|
}
|
});
|
}
|
// 2. 计算本次开票金额:商品明细所有单价的和
|
const calculatedTotalAmount = computed(() => {
|
return form.invoiceBillDetails.reduce((sum, item) => {
|
return sum + (Number(item.price) || 0);
|
}, 0);
|
});
|
|
// 3. 监听计算金额的变化,同步到 form 对象中(以便提交给后端)
|
watch(calculatedTotalAmount, (newVal) => {
|
form.currentInvoicedAmount = newVal;
|
});
|
// 4. 保存单行时的校验逻辑
|
const saveItemRow = (row: any) => {
|
if (!row.goodsName || row.goodsName.trim() === '') {
|
proxy.$modal.msgError("商品名称不能为空");
|
return;
|
}
|
row.isSaved = true;
|
};
|
const openIshpw = () => {
|
visible.value = false;
|
}
|
const supplierBankIsible = ref(false);
|
|
const openBankDialog = () => {
|
supplierBankIsible.value = true;
|
}
|
const handleBankCardfirm = (data) => {
|
form.invoiceCompanyName = data.invoiceCompanyName;
|
form.invoiceCreditCode = data.invoiceCreditCode;
|
form.invoiceType = data.invoiceType;
|
form.invoiceBankName = data.invoiceBankName;
|
form.invoiceBankNo = data.invoiceBankNo;
|
form.invoiceInfoId = data.id; // 建议在 formData 增加此 ID 字段用于回显
|
};
|
// 弹窗显示状态
|
defineExpose({ open, openIshpw })
|
</script>
|
|
<style scoped>
|
.mb-5 {
|
margin-bottom: 20px;
|
}
|
|
.section-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
margin-bottom: 10px;
|
}
|
|
.section-title {
|
font-size: 16px;
|
font-weight: bold;
|
border-left: 4px solid #409eff;
|
padding-left: 10px;
|
margin: 15px 0;
|
}
|
|
.text-bold {
|
font-weight: bold;
|
}
|
|
.text-danger {
|
color: #f56c6c;
|
}
|
</style>
|