<template>
|
<view class="invoice-add">
|
<!-- 使用通用页面头部组件 -->
|
<PageHeader title="新增发票" @back="goBack" />
|
|
<!-- 表单内容 -->
|
<u-form @submit="submitForm" ref="formRef" label-width="110" input-align="right" error-message-align="right">
|
<!-- 基本信息 -->
|
<view class="form-section">
|
<u-form-item label="采购合同号" prop="purchaseLedgerNo" required>
|
<u-input v-model="form.purchaseLedgerNo" placeholder="自动填充" disabled />
|
</u-form-item>
|
<u-form-item label="销售合同号" prop="salesContractNo" required>
|
<u-input v-model="form.salesContractNo" placeholder="自动填充" disabled />
|
</u-form-item>
|
<u-form-item label="供应商名称" prop="supplierName" required>
|
<u-input v-model="form.supplierName" placeholder="自动填充" disabled />
|
</u-form-item>
|
<u-form-item label="项目名称" prop="projectName" required>
|
<u-input v-model="form.projectName" placeholder="自动填充" disabled />
|
</u-form-item>
|
<u-form-item label="发票号" prop="invoiceNumber" required>
|
<u-input v-model="form.invoiceNumber" placeholder="请输入" clearable />
|
</u-form-item>
|
<u-form-item label="发票金额(元)" prop="invoiceAmount" required>
|
<u-input v-model="form.invoiceAmount" type="number" placeholder="自动填充" disabled />
|
</u-form-item>
|
<u-form-item label="录入人">
|
<u-input v-model="form.issUer" placeholder="自动填充" disabled />
|
</u-form-item>
|
<u-form-item
|
label="开票日期"
|
prop="entryDate"
|
required
|
@click="showEntryDatePicker = true"
|
>
|
<u-input
|
v-model="form.entryDate"
|
placeholder="请选择开票日期"
|
readonly
|
@click="showEntryDatePicker = true"
|
/>
|
<template #right>
|
<u-icon name="arrow-right" @click="showEntryDatePicker = true"></u-icon>
|
</template>
|
</u-form-item>
|
<u-form-item
|
label="录入日期"
|
prop="enterDate"
|
required
|
@click="showEnterDatePicker = true"
|
>
|
<u-input
|
v-model="form.enterDate"
|
placeholder="请选择录入日期"
|
readonly
|
@click="showEnterDatePicker = true"
|
/>
|
<template #right>
|
<u-icon name="arrow-right" @click="showEnterDatePicker = true"></u-icon>
|
</template>
|
</u-form-item>
|
</view>
|
|
<!-- 产品信息 -->
|
<view class="product-section">
|
<view class="section-header">
|
<view>
|
<text class="section-title">产品信息</text>
|
</view>
|
</view>
|
|
<view v-if="!productData || productData.length === 0" class="empty-state">
|
<view class="empty-text">暂无产品数据</view>
|
</view>
|
|
<view v-else class="product-list">
|
<view
|
v-for="(item, index) in productData"
|
:key="index"
|
class="product-card"
|
>
|
<!-- 产品头部 -->
|
<view class="product-header">
|
<view class="product-title">
|
<view class="document-icon">
|
<u-icon name="file-text" size="16" color="#ffffff"></u-icon>
|
</view>
|
<text class="product-productCategory">产品 {{ index + 1 }}</text>
|
</view>
|
</view>
|
|
<!-- 产品信息表单 -->
|
<view class="product-form">
|
<u-form-item label="产品大类" prop="productCategory" border-bottom>
|
<u-input v-model="item.productCategory" placeholder="" disabled />
|
</u-form-item>
|
<u-form-item label="规格型号" prop="specificationModel" border-bottom>
|
<u-input v-model="item.specificationModel" placeholder="" disabled />
|
</u-form-item>
|
<u-form-item label="单位" prop="unit" border-bottom>
|
<u-input v-model="item.unit" placeholder="" disabled />
|
</u-form-item>
|
<u-form-item label="数量" prop="quantity" border-bottom>
|
<u-input v-model="item.quantity" placeholder="" disabled />
|
</u-form-item>
|
<u-form-item label="税率(%)" prop="taxRate" border-bottom>
|
<u-input v-model="item.taxRate" placeholder="" disabled />
|
</u-form-item>
|
<u-form-item label="录入日期" prop="registerDate" border-bottom>
|
<u-input v-model="item.registerDate" placeholder="" disabled />
|
</u-form-item>
|
<u-form-item label="含税单价(元)" prop="taxInclusiveUnitPrice" border-bottom>
|
<u-input v-model="item.taxInclusiveUnitPrice" placeholder="" disabled />
|
</u-form-item>
|
<u-form-item label="含税总价(元)" prop="taxInclusiveTotalPrice" border-bottom>
|
<u-input v-model="item.taxInclusiveTotalPrice" placeholder="" disabled />
|
</u-form-item>
|
<u-form-item label="不含税总价(元)" prop="taxExclusiveTotalPrice" border-bottom>
|
<u-input v-model="item.taxExclusiveTotalPrice" placeholder="" disabled />
|
</u-form-item>
|
|
<!-- 本次来票信息 -->
|
<u-form-item label="本次来票数" prop="ticketsNum" border-bottom>
|
<u-input
|
v-model="item.ticketsNum"
|
type="number"
|
placeholder="请输入来票数量"
|
@blur="invoiceNumBlur(item)"
|
/>
|
</u-form-item>
|
<u-form-item label="本次来票金额(元)" prop="ticketsAmount" border-bottom>
|
<u-input
|
v-model="item.ticketsAmount"
|
type="number"
|
placeholder="请输入来票金额"
|
@blur="invoiceAmountBlur(item)"
|
/>
|
</u-form-item>
|
|
<!-- 未来票信息 -->
|
<u-form-item label="未来票数" prop="futureTickets" border-bottom>
|
<u-input v-model="item.futureTickets" placeholder="" disabled />
|
</u-form-item>
|
<u-form-item label="未来票金额(元)" prop="futureTicketsAmount" border-bottom>
|
<u-input v-model="item.futureTicketsAmount" placeholder="" disabled />
|
</u-form-item>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<!-- 使用FooterButtons组件 -->
|
<FooterButtons
|
@cancel="goBack"
|
@confirm="submitForm"
|
:loading="submitting"
|
/>
|
|
<!-- 为底部按钮留出空间 -->
|
<view style="height: 80px;"></view>
|
</u-form>
|
|
<!-- 开票日期选择器 -->
|
<u-popup :show="showEntryDatePicker" mode="bottom" @close="showEntryDatePicker = false">
|
<u-datetime-picker
|
:show="true"
|
v-model="entryDateValue"
|
@confirm="onEntryDateConfirm"
|
@cancel="showEntryDatePicker = false"
|
mode="date"
|
/>
|
</u-popup>
|
|
<!-- 录入日期选择器 -->
|
<u-popup :show="showEnterDatePicker" mode="bottom" @close="showEnterDatePicker = false">
|
<u-datetime-picker
|
:show="true"
|
v-model="enterDateValue"
|
@confirm="onEnterDateConfirm"
|
@cancel="showEnterDatePicker = false"
|
mode="date"
|
/>
|
</u-popup>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, onMounted } from 'vue'
|
import FooterButtons from '@/components/FooterButtons.vue'
|
const showToast = (message) => {
|
uni.showToast({
|
title: message,
|
icon: 'none'
|
})
|
}
|
|
const showLoadingToast = (message) => {
|
uni.showLoading({
|
title: message || '加载中...'
|
})
|
}
|
|
const closeToast = () => {
|
uni.hideLoading()
|
}
|
import useUserStore from '@/store/modules/user'
|
import {addOrUpdateRegistration, getPurchaseNoById} from "@/api/procurementManagement/invoiceEntry";
|
import {getInfo} from "@/api/procurementManagement/invoiceEntry.js";
|
import { formatDateToYMD } from '@/utils/ruoyi'
|
|
const userStore = useUserStore()
|
const editData = ref(null);
|
|
// 表单引用
|
const formRef = ref()
|
|
// 表单数据
|
let form = ref({
|
purchaseLedgerNo: '',
|
salesContractNo: '',
|
supplierName: '',
|
projectName: '',
|
issUer: '',
|
entryDate: '',
|
enterDate: '',
|
invoiceAmount: '',
|
invoiceNumber: '',
|
fileIds: []
|
})
|
|
// 产品数据
|
const productData = ref([])
|
|
// 文件上传相关
|
const action = ref('/dev-api/common/upload')
|
const getToken = () => {
|
return userStore.token || ''
|
}
|
|
// 日期选择器状态
|
const showEntryDatePicker = ref(false)
|
const showEnterDatePicker = ref(false)
|
const entryDateValue = ref(Date.now())
|
const enterDateValue = ref(Date.now())
|
|
// 提交状态
|
const submitting = ref(false)
|
|
// 返回上一页
|
const goBack = () => {
|
// 清理本地存储的数据
|
uni.removeStorageSync('editData');
|
uni.navigateBack()
|
}
|
|
// 格式化数字
|
const formatNumber = (value, precision = 2) => {
|
if (!value && value !== 0) return '0.00'
|
return Number(value).toFixed(precision)
|
}
|
|
// 更新未来票数据
|
const updateFutureTicketData = (row) => {
|
const totalQuantity = parseFloat(row.quantity) || 0
|
const currentTicketNum = parseFloat(row.ticketsNum) || 0
|
const totalAmount = parseFloat(row.taxInclusiveTotalPrice) || 0
|
const currentTicketAmount = parseFloat(row.ticketsAmount) || 0
|
|
row.futureTickets = Math.max(0, totalQuantity - currentTicketNum).toFixed(2)
|
row.futureTicketsAmount = Math.max(0, totalAmount - currentTicketAmount).toFixed(2)
|
}
|
|
// 来票数量变化处理
|
const invoiceNumBlur = (row) => {
|
if (!row.ticketsNum || row.ticketsNum === "") {
|
row.ticketsNum = 0;
|
}
|
if (Number(row.ticketsNum) > Number(row.tempFutureTickets)) {
|
showToast("本次开票数不得大于未开票数");
|
row.ticketsNum = 0;
|
return;
|
}
|
// 计算本次来票金额
|
row.ticketsAmount = row.ticketsNum * row.taxInclusiveUnitPrice;
|
// 计算未来票数
|
row.futureTickets = row.tempFutureTickets - row.ticketsNum;
|
// 计算未来票金额
|
row.futureTicketsAmount = row.tempFutureTicketsAmount - row.ticketsAmount;
|
calculateinvoiceAmount();
|
}
|
|
// 来票金额变化处理
|
const invoiceAmountBlur = (row) => {
|
if (!row.ticketsAmount) {
|
row.ticketsAmount = 0;
|
}
|
// 计算是否超过来票总金额
|
if (Number(row.ticketsAmount) > Number(row.taxInclusiveTotalPrice)) {
|
showToast('本次来票金额不得大于含税总金额');
|
row.ticketsAmount = 0;
|
}
|
// 计算本次来票数
|
row.ticketsNum = Number(
|
(row.ticketsAmount / row.taxInclusiveUnitPrice).toFixed(2)
|
);
|
// 计算未来票数和未来票金额
|
updateFutureTicketData(row)
|
calculateinvoiceAmount();
|
}
|
|
const calculateinvoiceAmount = () => {
|
let invoiceAmountTotal = 0;
|
productData.value.forEach((item) => {
|
if (item.ticketsAmount) {
|
invoiceAmountTotal += Number(item.ticketsAmount);
|
}
|
});
|
form.value.invoiceAmount = invoiceAmountTotal.toFixed(2);
|
}
|
|
// 文件上传成功回调
|
const uploadSuccess = (res) => {
|
if (res && res.data && res.data.fileId) {
|
form.value.fileIds.push(res.data.fileId)
|
}
|
}
|
|
// 文件删除回调
|
const removeFile = (index, file) => {
|
if (file.fileId) {
|
const fileIndex = form.value.fileIds.indexOf(file.fileId)
|
if (fileIndex > -1) {
|
form.value.fileIds.splice(fileIndex, 1)
|
}
|
}
|
}
|
|
|
|
// 开票日期确认
|
const onEntryDateConfirm = (e) => {
|
form.value.entryDate = formatDateToYMD(e.value)
|
entryDateValue.value = e.value
|
showEntryDatePicker.value = false;
|
};
|
|
// 录入日期确认
|
const onEnterDateConfirm = (e) => {
|
form.value.enterDate = formatDateToYMD(e.value)
|
enterDateValue.value = e.value
|
showEnterDatePicker.value = false;
|
}
|
|
// 格式化日期
|
const formatDate = (date) => {
|
const year = date.getFullYear()
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
const day = String(date.getDate()).padStart(2, '0')
|
return `${year}-${month}-${day}`
|
}
|
|
// 获取产品列表
|
const getProductList = async () => {
|
try {
|
showLoadingToast('加载中...')
|
const res = await getPurchaseNoById({ id: editData.value.id })
|
form.value.purchaseLedgerNo = res.data.purchaseContractNumber;
|
form.value.invoiceAmount = res.data.invoiceAmount;
|
form.value.invoiceNumber = res.data.invoiceNumber;
|
const data = await getInfo({ id: editData.value.id });
|
productData.value = data.data.productData;
|
form.value.salesContractNo = data.data.salesContractNo;
|
form.value.projectName = data.data.projectName;
|
form.value.supplierName = data.data.supplierName;
|
form.value.productData = data.data.productData;
|
// 设置默认录入人
|
form.value.issUer = userStore.nickName
|
|
// 设置默认日期
|
const today = new Date()
|
form.value.enterDate = formatDate(today)
|
form.value.entryDate = formatDate(today)
|
|
closeToast()
|
} catch (error) {
|
closeToast()
|
showToast('获取产品列表失败')
|
}
|
}
|
|
// 提交表单
|
const submitForm = async () => {
|
try {
|
submitting.value = true
|
|
// 验证发票号是否填写
|
if (!form.value.invoiceNumber) {
|
showToast('请输入发票号')
|
return
|
}
|
|
// 验证产品数据
|
if (productData.value.length === 0) {
|
showToast('请先添加产品信息')
|
return
|
}
|
|
// 验证来票数据
|
const hasInvoiceData = productData.value.some(item => {
|
const num = parseFloat(item.ticketsNum) || 0
|
const amount = parseFloat(item.ticketsAmount) || 0
|
return num > 0 || amount > 0
|
})
|
|
if (!hasInvoiceData) {
|
showToast('请至少输入一个产品的来票信息')
|
return
|
}
|
|
const submitData = {
|
...form.value,
|
productData: productData.value
|
}
|
|
await addOrUpdateRegistration(submitData)
|
showToast('提交成功')
|
|
// 返回上一页
|
setTimeout(() => {
|
goBack()
|
}, 800)
|
|
} catch (error) {
|
showToast('提交失败,请重试')
|
} finally {
|
submitting.value = false
|
}
|
}
|
|
// 页面加载时初始化数据
|
onMounted(() => {
|
// 从页面参数或缓存中获取销售合同信息
|
const contractInfo = uni.getStorageSync('editData')
|
if (contractInfo) {
|
editData.value = JSON.parse(contractInfo);
|
const contract = JSON.parse(contractInfo)
|
form.value.purchaseLedgerNo = contract.purchaseLedgerNo || ''
|
form.value.salesContractNo = contract.salesContractNo || ''
|
form.value.supplierName = contract.supplierName || ''
|
form.value.projectName = contract.projectName || ''
|
form.value.invoiceAmount = contract.invoiceAmount || ''
|
form.value.invoiceNumber = contract.invoiceNumber || ''
|
form.value.purchaseLedgerId = contract.id || ''
|
|
// 获取产品列表
|
getProductList()
|
}
|
})
|
</script>
|
|
<style scoped lang="scss">
|
@import '@/static/scss/form-common.scss';
|
|
</style>
|