<template>
|
<view class="account-detail">
|
<!-- 使用通用页面头部组件 -->
|
<PageHeader title="新增开票登记" @back="goBack" />
|
|
<!-- 表单内容 -->
|
<van-form @submit="submitForm" ref="formRef" label-width="110px" input-align="right" error-message-align="right" scroll-to-error scroll-to-error-position="center">
|
<!-- 基本信息 -->
|
<van-cell-group title="基本信息" inset>
|
<van-field
|
v-model="form.salesContractNo"
|
label="销售合同号"
|
readonly
|
placeholder="自动填充"
|
/>
|
<van-field
|
v-model="form.customerName"
|
label="客户名称"
|
readonly
|
placeholder="自动填充"
|
/>
|
<van-field
|
v-model="form.salesman"
|
label="业务员"
|
readonly
|
placeholder="自动填充"
|
/>
|
<van-field
|
v-model="form.projectName"
|
label="项目名称"
|
readonly
|
placeholder="自动填充"
|
/>
|
<van-field
|
v-model="form.createUer"
|
label="录入人"
|
readonly
|
placeholder="请输入录入人"
|
/>
|
<van-field
|
v-model="form.createTime"
|
label="录入日期"
|
readonly
|
placeholder="请选择录入日期"
|
@click="showCreateTimePicker = true"
|
/>
|
<van-field
|
v-model="form.invoiceNo"
|
label="发票号码"
|
required
|
placeholder="请输入发票号码"
|
:rules="[{ required: true, message: '请输入发票号码' }]"
|
/>
|
<van-field
|
v-model="form.issueDate"
|
label="开票日期"
|
readonly
|
placeholder="请选择开票日期"
|
required
|
@click="showIssueDatePicker = true"
|
:rules="[{ required: true, message: '请选择开票日期' }]"
|
/>
|
</van-cell-group>
|
|
<!-- 产品信息 -->
|
<view class="product-section">
|
<view class="section-header">
|
<text class="section-title">产品信息</text>
|
</view>
|
|
<view v-if="productData.length === 0" class="empty-state">
|
<van-empty description="暂无产品数据" />
|
</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">
|
<van-icon name="description" color="#2979ff" size="15" />
|
<text class="product-productCategory">产品 {{ index + 1 }}</text>
|
</view>
|
</view>
|
|
<!-- 产品信息表单 -->
|
<view class="product-form">
|
<van-field
|
v-model="item.productCategory"
|
label="产品大类"
|
readonly
|
/>
|
<van-field
|
v-model="item.specificationModel"
|
label="规格型号"
|
readonly
|
/>
|
<van-field
|
v-model="item.unit"
|
label="单位"
|
readonly
|
/>
|
<van-field
|
v-model="item.quantity"
|
label="数量"
|
readonly
|
/>
|
<van-field
|
v-model="item.taxRate"
|
label="税率(%)"
|
readonly
|
/>
|
<van-field
|
v-model="item.taxInclusiveUnitPrice"
|
label="含税单价(元)"
|
readonly
|
/>
|
<van-field
|
v-model="item.taxInclusiveTotalPrice"
|
label="含税总价(元)"
|
readonly
|
/>
|
<van-field
|
v-model="item.taxExclusiveTotalPrice"
|
label="不含税总价(元)"
|
readonly
|
/>
|
|
<!-- 本次开票信息 -->
|
<van-field
|
v-model="item.currentInvoiceNum"
|
label="本次开票数"
|
type="number"
|
placeholder="请输入开票数量"
|
@blur="invoiceNumBlur(item)"
|
/>
|
<van-field
|
v-model="item.currentInvoiceAmount"
|
label="本次开票金额(元)"
|
type="number"
|
placeholder="请输入开票金额"
|
@blur="invoiceAmountBlur(item)"
|
/>
|
|
<!-- 未开票信息 -->
|
<van-field
|
v-model="item.noInvoiceNum"
|
label="未开票数"
|
readonly
|
/>
|
<van-field
|
v-model="item.noInvoiceAmount"
|
label="未开票金额(元)"
|
readonly
|
/>
|
</view>
|
</view>
|
</view>
|
</view>
|
|
<!-- 提交按钮 -->
|
<view class="footer-btns">
|
<van-button class="cancel-btn" @click="goBack">取消</van-button>
|
<van-button class="save-btn" native-type="submit" form-type="submit">保存</van-button>
|
</view>
|
</van-form>
|
|
<!-- 日期选择器 -->
|
<van-popup v-model:show="showIssueDatePicker" position="bottom">
|
<van-date-picker
|
v-model="currentIssueDate"
|
title="选择开票日期"
|
@confirm="onIssueDateConfirm"
|
@cancel="showIssueDatePicker = false"
|
/>
|
</van-popup>
|
|
<van-popup v-model:show="showCreateTimePicker" position="bottom">
|
<van-date-picker
|
v-model="currentCreateTime"
|
title="选择录入日期"
|
@confirm="onCreateTimeConfirm"
|
@cancel="showCreateTimePicker = false"
|
/>
|
</van-popup>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, reactive, onMounted } from 'vue'
|
import { showToast, showLoadingToast, closeToast } from 'vant'
|
import { invoiceRegistrationSave } from '@/api/salesManagement/invoiceRegistration'
|
import useUserStore from '@/store/modules/user'
|
import {getSalesLedgerWithProducts} from "@/api/salesManagement/salesLedger";
|
|
const userStore = useUserStore()
|
const editData = ref(null);
|
|
// 表单引用
|
const formRef = ref()
|
|
// 表单数据
|
let form = ref({
|
salesContractNo: '',
|
customerName: '',
|
salesman: '',
|
projectName: '',
|
createUer: '',
|
issueDate: '',
|
createTime: '',
|
invoiceNo: ''
|
})
|
|
// 产品数据
|
const productData = ref([])
|
|
// 日期选择器状态
|
const showIssueDatePicker = ref(false)
|
const showCreateTimePicker = ref(false)
|
const currentIssueDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()])
|
const currentCreateTime = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()])
|
|
// 提交状态
|
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 invoiceNumBlur = (row) => {
|
if (!row.currentInvoiceNum) {
|
row.currentInvoiceNum = 0;
|
}
|
if (row.currentInvoiceNum > row.tempNoInvoiceNum) {
|
showToast('本次开票数不得大于未开票数')
|
row.currentInvoiceNum = 0;
|
}
|
// 计算本次开票金额
|
row.currentInvoiceAmount = (
|
row.currentInvoiceNum * row.taxInclusiveUnitPrice
|
).toFixed(2);
|
// 计算未开票数
|
row.noInvoiceNum = (row.originalNoInvoiceNum - row.currentInvoiceNum).toFixed(
|
2
|
);
|
// 计算未开票金额
|
row.noInvoiceAmount = (
|
row.tempnoInvoiceAmount - row.currentInvoiceAmount
|
).toFixed(2);
|
}
|
|
// 开票金额变化处理
|
const invoiceAmountBlur = (row) => {
|
if (!row.currentInvoiceAmount) {
|
row.currentInvoiceAmount = 0;
|
}
|
// 计算是否超过开票总金额
|
if (row.currentInvoiceAmount > row.tempnoInvoiceAmount) {
|
showToast('本次开票金额不得大于未开票金额')
|
row.currentInvoiceAmount = 0;
|
}
|
// 计算本次开票数
|
row.currentInvoiceNum = (
|
row.currentInvoiceAmount / row.taxInclusiveUnitPrice
|
).toFixed(2);
|
// 计算未开票数
|
row.noInvoiceNum = (row.originalNoInvoiceNum - row.currentInvoiceNum).toFixed(
|
2
|
);
|
// 计算未开票金额
|
row.noInvoiceAmount = (
|
row.tempnoInvoiceAmount - row.currentInvoiceAmount
|
).toFixed(2);
|
}
|
|
// 更新未开票数据
|
const updateNoInvoiceData = (row) => {
|
const totalQuantity = parseFloat(row.quantity) || 0
|
const currentInvoiceNum = parseFloat(row.currentInvoiceNum) || 0
|
const totalAmount = parseFloat(row.taxInclusiveTotalPrice) || 0
|
const currentInvoiceAmount = parseFloat(row.currentInvoiceAmount) || 0
|
|
row.noInvoiceNum = Math.max(0, totalQuantity - currentInvoiceNum).toFixed(2)
|
row.noInvoiceAmount = Math.max(0, totalAmount - currentInvoiceAmount).toFixed(2)
|
}
|
|
// 开票日期确认
|
const onIssueDateConfirm = ({ selectedValues }) => {
|
console.log('selectedValues--', selectedValues)
|
form.value.issueDate = selectedValues.join('-');
|
currentIssueDate.value = selectedValues;
|
showIssueDatePicker.value = false;
|
};
|
|
// 录入日期确认
|
const onCreateTimeConfirm = (value) => {
|
try {
|
// 处理不同的值格式
|
let year, month, day;
|
|
if (Array.isArray(value)) {
|
// 数组格式 [year, month, day]
|
[year, month, day] = value;
|
} else if (value && typeof value === 'object') {
|
// Date对象格式
|
year = value.getFullYear();
|
month = value.getMonth() + 1;
|
day = value.getDate();
|
} else {
|
// 其他格式,使用当前日期
|
const now = new Date();
|
year = now.getFullYear();
|
month = now.getMonth() + 1;
|
day = now.getDate();
|
}
|
|
form.value.createTime = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
|
showCreateTimePicker.value = false;
|
} catch (error) {
|
console.error('日期处理错误:', error);
|
showToast('日期选择失败,请重试');
|
}
|
}
|
|
// 格式化日期
|
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 getSalesLedgerWithProducts({ id: editData.value.id, type: 1 })
|
productData.value = res.productData;
|
form.value = { ...res };
|
// 设置默认录入人
|
form.value.createUer = userStore.nickName || ''
|
|
// 设置默认日期
|
const today = new Date()
|
form.value.createTime = formatDate(today)
|
|
closeToast()
|
} catch (error) {
|
closeToast()
|
showToast('获取产品列表失败')
|
}
|
}
|
|
// 提交表单
|
const submitForm = async () => {
|
try {
|
submitting.value = true
|
|
// 验证产品数据
|
if (productData.value.length === 0) {
|
showToast('请先添加产品信息')
|
return
|
}
|
|
// 验证开票数据
|
const hasInvoiceData = productData.value.some(item => {
|
const num = parseFloat(item.currentInvoiceNum) || 0
|
const amount = parseFloat(item.currentInvoiceAmount) || 0
|
return num > 0 || amount > 0
|
})
|
|
if (!hasInvoiceData) {
|
showToast('请至少输入一个产品的开票信息')
|
return
|
}
|
|
const submitData = {
|
...form.value,
|
productList: productData.value
|
}
|
|
await invoiceRegistrationSave(submitData)
|
showToast('提交成功')
|
|
// 返回上一页
|
setTimeout(() => {
|
uni.navigateBack()
|
}, 1500)
|
|
} 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.salesContractNo = contract.salesContractNo || ''
|
form.value.customerName = contract.customerName || ''
|
form.value.salesman = contract.salesman || ''
|
form.value.projectName = contract.projectName || ''
|
|
// 获取产品列表
|
getProductList()
|
}
|
})
|
</script>
|
|
<style scoped lang="scss">
|
.account-detail {
|
min-height: 100vh;
|
background: #f8f9fa;
|
padding-bottom: 5rem;
|
}
|
|
.empty-state {
|
padding: 40px 0;
|
}
|
|
.product-section {
|
background: #fff;
|
margin-top: 1rem;
|
padding: 1rem;
|
box-shadow: 0 0.125rem 0.5rem rgba(0,0,0,0.04);
|
}
|
|
.section-header {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
margin-bottom: 1rem;
|
}
|
|
.section-title {
|
font-size: 1rem;
|
font-weight: 600;
|
color: #333;
|
}
|
|
.product-list {
|
.product-card {
|
background: #FFFFFF;
|
box-shadow: 0 0 1.25rem 0 rgba(0,57,117,0.08);
|
border-radius: 0.5rem 0.5rem 0.5rem 0.5rem;
|
padding: 1rem 0.5rem 0 0.5rem;
|
position: relative;
|
margin-bottom: 1rem;
|
}
|
}
|
|
.product-header {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
padding: 0 0.5rem 0.75rem 0.5rem;
|
border-bottom: 0.0625rem solid #e8e8e8;
|
}
|
|
.product-title {
|
display: flex;
|
align-items: center;
|
}
|
|
.product-productCategory {
|
margin-left: 0.5rem;
|
font-size: 0.875rem;
|
font-weight: 500;
|
color: #333;
|
}
|
|
.product-form {
|
margin-bottom: 1rem;
|
}
|
.footer-btns {
|
position: fixed;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
background: #fff;
|
display: flex;
|
justify-content: space-around;
|
align-items: center;
|
padding: 0.75rem 0;
|
box-shadow: 0 -0.125rem 0.5rem rgba(0,0,0,0.05);
|
z-index: 1000;
|
}
|
.cancel-btn {
|
font-weight: 400;
|
font-size: 1rem;
|
color: #FFFFFF;
|
width: 6.375rem;
|
background: #C7C9CC;
|
box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2);
|
border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
|
}
|
.save-btn {
|
font-weight: 400;
|
font-size: 1rem;
|
color: #FFFFFF;
|
width: 14rem;
|
background: linear-gradient( 140deg, #00BAFF 0%, #006CFB 100%);
|
box-shadow: 0 0.25rem 0.625rem 0 rgba(3,88,185,0.2);
|
border-radius: 2.5rem 2.5rem 2.5rem 2.5rem;
|
}
|
// 响应式调整
|
@media (max-width: 768px) {
|
.submit-section {
|
padding: 12px;
|
}
|
}
|
</style>
|