<template>
|
<el-dialog
|
:model-value="dialogFormVisible"
|
@update:model-value="$emit('update:dialogFormVisible', $event)"
|
:title="title"
|
width="1200px"
|
:close-on-click-modal="false"
|
@close="handleClose"
|
>
|
<el-form
|
ref="formRef"
|
:model="formData"
|
:rules="rules"
|
label-width="120px"
|
class="invoice-form"
|
>
|
<!-- 购买方信息 -->
|
<el-card class="buyer-card" shadow="never">
|
<template #header>
|
<div class="card-header">
|
<span>购买方信息</span>
|
</div>
|
</template>
|
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="购买方名称" prop="buyerName">
|
<el-input
|
v-model="formData.buyerName"
|
placeholder="请输入购买方名称"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="纳税人识别号" prop="buyerTaxNo">
|
<el-input
|
v-model="formData.buyerTaxNo"
|
placeholder="请输入纳税人识别号"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="地址电话" prop="buyerAddress">
|
<el-input
|
v-model="formData.buyerAddress"
|
placeholder="请输入地址电话"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="开户行及账号" prop="buyerBankAccount">
|
<el-input
|
v-model="formData.buyerBankAccount"
|
placeholder="请输入开户行及账号"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</el-card>
|
|
<!-- 销售方信息 -->
|
<el-card class="seller-card" shadow="never">
|
<template #header>
|
<div class="card-header">
|
<span>销售方信息</span>
|
</div>
|
</template>
|
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="销售方名称" prop="sellerName">
|
<el-input
|
v-model="formData.sellerName"
|
placeholder="请输入销售方名称"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="纳税人识别号" prop="sellerTaxNo">
|
<el-input
|
v-model="formData.sellerTaxNo"
|
placeholder="请输入纳税人识别号"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
|
<el-row :gutter="20">
|
<el-col :span="12">
|
<el-form-item label="地址电话" prop="sellerAddress">
|
<el-input
|
v-model="formData.sellerAddress"
|
placeholder="请输入地址电话"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="12">
|
<el-form-item label="开户行及账号" prop="sellerBankAccount">
|
<el-input
|
v-model="formData.sellerBankAccount"
|
placeholder="请输入开户行及账号"
|
style="width: 100%"
|
/>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</el-card>
|
|
<!-- 商品明细 -->
|
<el-card class="items-card" shadow="never">
|
<template #header>
|
<div class="card-header">
|
<span>商品明细</span>
|
<el-button type="primary" size="small" @click="addItem">
|
添加商品
|
</el-button>
|
</div>
|
</template>
|
|
<el-table :data="formData.items" border style="width: 100%">
|
<el-table-column label="商品名称" width="200">
|
<template #default="scope">
|
<el-input
|
v-model="scope.row.name"
|
placeholder="商品名称"
|
style="width: 100%"
|
/>
|
</template>
|
</el-table-column>
|
<el-table-column label="规格型号" width="150">
|
<template #default="scope">
|
<el-input
|
v-model="scope.row.specification"
|
placeholder="规格型号"
|
style="width: 100%"
|
/>
|
</template>
|
</el-table-column>
|
<el-table-column label="单位" width="100">
|
<template #default="scope">
|
<el-input
|
v-model="scope.row.unit"
|
placeholder="单位"
|
style="width: 100%"
|
/>
|
</template>
|
</el-table-column>
|
<el-table-column label="数量" width="120">
|
<template #default="scope">
|
<el-input
|
v-model.number="scope.row.quantity"
|
placeholder="数量"
|
type="number"
|
@input="calculateItemAmount(scope.$index)"
|
style="width: 100%"
|
/>
|
</template>
|
</el-table-column>
|
<el-table-column label="单价" width="120">
|
<template #default="scope">
|
<el-input
|
v-model.number="scope.row.unitPrice"
|
placeholder="单价"
|
type="number"
|
@input="calculateItemAmount(scope.$index)"
|
style="width: 100%"
|
>
|
<template v-slot:suffix>
|
<span>元</span>
|
</template>
|
</el-input>
|
</template>
|
</el-table-column>
|
<el-table-column label="金额" width="120">
|
<template #default="scope">
|
<span>{{ (scope.row.amount || 0).toFixed(2) }} 元</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="税率" width="120">
|
<template #default="scope">
|
<el-select
|
v-model="scope.row.taxRate"
|
placeholder="选择税率"
|
@change="calculateItemAmount(scope.$index)"
|
style="width: 100%"
|
>
|
<el-option label="0%" value="0" />
|
<el-option label="1%" value="0.01" />
|
<el-option label="3%" value="0.03" />
|
<el-option label="6%" value="0.06" />
|
<el-option label="9%" value="0.09" />
|
<el-option label="13%" value="0.13" />
|
</el-select>
|
</template>
|
</el-table-column>
|
<el-table-column label="税额" width="120">
|
<template #default="scope">
|
<span>{{ (scope.row.taxAmount || 0).toFixed(2) }} 元</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="价税合计" width="120">
|
<template #default="scope">
|
<span>{{ (scope.row.totalAmount || 0).toFixed(2) }} 元</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="操作" width="80">
|
<template #default="scope">
|
<el-button
|
type="danger"
|
size="small"
|
@click="removeItem(scope.$index)"
|
>
|
删除
|
</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
|
<!-- 合计信息 -->
|
<div class="summary-info">
|
<el-row :gutter="20">
|
<el-col :span="6">
|
<span class="summary-label">金额合计:</span>
|
<span class="summary-value">{{ totalAmount.toFixed(2) }} 元</span>
|
</el-col>
|
<el-col :span="6">
|
<span class="summary-label">税额合计:</span>
|
<span class="summary-value">{{ totalTaxAmount.toFixed(2) }} 元</span>
|
</el-col>
|
<el-col :span="6">
|
<span class="summary-label">价税合计:</span>
|
<span class="summary-value">{{ totalTotalAmount.toFixed(2) }} 元</span>
|
</el-col>
|
</el-row>
|
</div>
|
</el-card>
|
|
<!-- 备注信息 -->
|
<el-form-item label="备注" prop="remark">
|
<el-input
|
v-model="formData.remark"
|
type="textarea"
|
:rows="3"
|
placeholder="请输入备注信息"
|
/>
|
</el-form-item>
|
</el-form>
|
|
<template #footer>
|
<div class="dialog-footer">
|
<el-button @click="handleClose">取消</el-button>
|
<el-button type="primary" @click="handleSubmit" :loading="submitLoading">
|
提交
|
</el-button>
|
</div>
|
</template>
|
</el-dialog>
|
</template>
|
|
<script setup>
|
import { ref, reactive, watch, computed } from "vue";
|
import { ElMessage } from "element-plus";
|
|
// Props
|
const props = defineProps({
|
dialogFormVisible: {
|
type: Boolean,
|
default: false
|
},
|
form: {
|
type: Object,
|
default: () => ({})
|
},
|
title: {
|
type: String,
|
default: ""
|
},
|
isEdit: {
|
type: Boolean,
|
default: false
|
}
|
});
|
|
// Emits
|
const emit = defineEmits(['update:dialogFormVisible', 'update:form', 'submit', 'success']);
|
|
// 响应式数据
|
const formRef = ref(null);
|
const submitLoading = ref(false);
|
|
// 表单数据
|
const formData = reactive({
|
buyerName: "",
|
buyerTaxNo: "",
|
buyerAddress: "",
|
buyerBankAccount: "",
|
sellerName: "本公司",
|
sellerTaxNo: "123456789012345678",
|
sellerAddress: "公司地址",
|
sellerBankAccount: "银行账户",
|
items: [],
|
remark: ""
|
});
|
|
// 表单验证规则
|
const rules = {
|
buyerName: [
|
{ required: true, message: "请输入购买方名称", trigger: "blur" }
|
],
|
buyerTaxNo: [
|
{ required: true, message: "请输入纳税人识别号", trigger: "blur" }
|
],
|
items: [
|
{
|
type: "array",
|
required: true,
|
message: "请至少添加一个商品",
|
trigger: "change",
|
validator: (rule, value, callback) => {
|
if (!value || value.length === 0) {
|
callback(new Error("请至少添加一个商品"));
|
} else {
|
callback();
|
}
|
}
|
}
|
]
|
};
|
|
// 计算属性
|
const totalAmount = computed(() => {
|
return formData.items.reduce((sum, item) => sum + (item.amount || 0), 0);
|
});
|
|
const totalTaxAmount = computed(() => {
|
return formData.items.reduce((sum, item) => sum + (item.taxAmount || 0), 0);
|
});
|
|
const totalTotalAmount = computed(() => {
|
return formData.items.reduce((sum, item) => sum + (item.totalAmount || 0), 0);
|
});
|
|
// 监听表单数据变化
|
watch(() => props.form, (newVal) => {
|
Object.assign(formData, newVal);
|
if (!formData.items || formData.items.length === 0) {
|
formData.items = [];
|
}
|
}, { deep: true, immediate: true });
|
|
// 添加商品
|
const addItem = () => {
|
formData.items.push({
|
name: "",
|
specification: "",
|
unit: "",
|
quantity: 0,
|
unitPrice: 0,
|
amount: 0,
|
taxRate: "0.13",
|
taxAmount: 0,
|
totalAmount: 0
|
});
|
};
|
|
// 删除商品
|
const removeItem = (index) => {
|
formData.items.splice(index, 1);
|
};
|
|
// 计算商品金额
|
const calculateItemAmount = (index) => {
|
const item = formData.items[index];
|
if (item.quantity && item.unitPrice) {
|
item.amount = item.quantity * item.unitPrice;
|
item.taxAmount = item.amount * parseFloat(item.taxRate);
|
item.totalAmount = item.amount + item.taxAmount;
|
}
|
};
|
|
// 关闭对话框
|
const handleClose = () => {
|
emit('update:dialogFormVisible', false);
|
formRef.value?.resetFields();
|
};
|
|
// 提交表单
|
const handleSubmit = async () => {
|
if (!formRef.value) return;
|
|
try {
|
await formRef.value.validate();
|
|
// 验证商品信息
|
if (formData.items.length === 0) {
|
ElMessage.warning("请至少添加一个商品");
|
return;
|
}
|
|
for (let item of formData.items) {
|
if (!item.name) {
|
ElMessage.warning("请输入商品名称");
|
return;
|
}
|
if (!item.quantity || item.quantity <= 0) {
|
ElMessage.warning("请输入有效的商品数量");
|
return;
|
}
|
if (!item.unitPrice || item.unitPrice <= 0) {
|
ElMessage.warning("请输入有效的商品单价");
|
return;
|
}
|
}
|
|
submitLoading.value = true;
|
|
// 模拟提交
|
setTimeout(() => {
|
submitLoading.value = false;
|
ElMessage.success("提交成功");
|
emit('submit', { ...formData });
|
handleClose();
|
}, 1000);
|
|
} catch (error) {
|
console.error('表单验证失败:', error);
|
}
|
};
|
</script>
|
|
<style scoped>
|
.invoice-form {
|
padding: 20px 0;
|
}
|
|
.buyer-card,
|
.seller-card,
|
.items-card {
|
margin-bottom: 20px;
|
}
|
|
.card-header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
font-weight: bold;
|
}
|
|
.summary-info {
|
margin-top: 15px;
|
padding: 15px;
|
background-color: #f5f7fa;
|
border-radius: 4px;
|
}
|
|
.summary-label {
|
font-weight: bold;
|
margin-right: 10px;
|
}
|
|
.summary-value {
|
color: #409eff;
|
font-size: 16px;
|
font-weight: bold;
|
}
|
|
.dialog-footer {
|
text-align: right;
|
}
|
|
.el-table {
|
margin-top: 10px;
|
}
|
</style>
|