<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 />
|
<van-field v-model="form.customerName" label="客户名称" readonly />
|
<van-field v-model="form.invoiceNo" label="发票号" placeholder="请输入" required :rules="[{ required: true, message: '请输入发票号' }]" />
|
<van-field v-model="form.invoiceTotal" label="发票金额(元)" type="number" placeholder="请输入" required :rules="[{ required: true, message: '请输入发票金额' }]" />
|
<view class="tip-text" v-if="form.taxInclusiveTotalPrice">合同总额:{{ formatAmount(form.taxInclusiveTotalPrice) }} 元</view>
|
<van-field v-model="form.invoicePerson" label="开票人" readonly />
|
<van-field v-model="form.invoiceDate" label="开票日期" readonly placeholder="请选择" @click="showInvoiceDatePicker = true" required :rules="[{ required: true, message: '请选择开票日期' }]" />
|
</van-cell-group>
|
|
<van-cell-group title="附件材料(仅支持 pdf)" inset>
|
<van-uploader
|
accept=".pdf"
|
multiple
|
:after-read="afterReadUpload"
|
:before-read="beforeReadPdf"
|
>
|
<van-button class="upload-btn" icon="plus" type="primary" block>上传文件</van-button>
|
</van-uploader>
|
<view class="uploaded-list" v-if="fileList.length">
|
<view class="uploaded-item" v-for="(f, idx) in fileList" :key="idx">
|
<text class="file-name">{{ f.name || getFileNameFromUrl(f.url) }}</text>
|
<van-button size="mini" type="danger" plain @click="removeUploaded(idx)">移除</van-button>
|
</view>
|
</view>
|
</van-cell-group>
|
|
<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="showInvoiceDatePicker" position="bottom">
|
<van-date-picker
|
v-model="currentInvoiceDate"
|
title="选择开票日期"
|
@confirm="onInvoiceDateConfirm"
|
@cancel="showInvoiceDatePicker = false"
|
/>
|
</van-popup>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, onMounted } from 'vue'
|
import { showToast, showLoadingToast, closeToast } from 'vant'
|
import dayjs from 'dayjs'
|
import useUserStore from '@/store/modules/user'
|
import { getToken } from '@/utils/auth'
|
import { invoiceLedgerProductInfo, invoiceLedgerSaveOrUpdate } from '@/api/salesManagement/invoiceLedger.js'
|
import config from '@/config.js'
|
|
const userStore = useUserStore()
|
|
const formRef = ref()
|
let form = ref({
|
salesLedgerId: '',
|
customerId: '',
|
invoiceNo: '',
|
invoiceTotal: '',
|
taxRate: '',
|
invoicePerson: '',
|
invoiceDate: '',
|
customerName: '',
|
fileList: [],
|
createTime: '',
|
taxInclusiveTotalPrice: ''
|
})
|
const fileList = ref([])
|
const currentId = ref('')
|
|
// 日期选择
|
const showInvoiceDatePicker = ref(false)
|
const currentInvoiceDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()])
|
|
const goBack = () => {
|
uni.removeStorageSync('invoiceLedgerEditRow');
|
uni.navigateBack()
|
}
|
|
const formatAmount = (val) => {
|
if (val === undefined || val === null || val === '') return '0.00'
|
const num = Number(val)
|
if (Number.isNaN(num)) return '0.00'
|
return num.toFixed(2)
|
}
|
|
const onInvoiceDateConfirm = ({ selectedValues }) => {
|
form.value.invoiceDate = selectedValues.join('-')
|
currentInvoiceDate.value = selectedValues
|
showInvoiceDatePicker.value = false
|
}
|
|
// 上传前校验(兼容 Vant Uploader 的 file/fileList 结构)
|
const beforeReadPdf = (file) => {
|
const items = Array.isArray(file) ? file : [file]
|
for (const it of items) {
|
const raw = it?.file || it
|
const fileName = raw?.name || it?.name || ''
|
const ext = fileName.split('.').pop()?.toLowerCase()
|
const sizeOk = (raw?.size || 0) <= 10 * 1024 * 1024
|
if (ext !== 'pdf') {
|
showToast('仅支持pdf文件')
|
return false
|
}
|
if (!sizeOk) {
|
showToast('上传文件大小不能超过10MB')
|
return false
|
}
|
}
|
return true
|
}
|
|
const uploadSingleFile = async (fileObj) => {
|
return new Promise((resolve, reject) => {
|
showLoadingToast({ message: '正在上传...' })
|
const baseUrl = config.baseUrl + '/invoiceLedger/uploadFile'
|
|
const filePath = fileObj?.url || fileObj?.tempFilePath || fileObj?.file?.path
|
if (filePath) {
|
uni.uploadFile({
|
url: baseUrl,
|
filePath,
|
name: 'file',
|
header: { Authorization: 'Bearer ' + getToken() },
|
success: (res) => {
|
closeToast()
|
try {
|
const data = JSON.parse(res.data || '{}')
|
if (data.code === 200) {
|
resolve(data.data)
|
} else {
|
reject(new Error(data.msg || '上传失败'))
|
}
|
} catch (err) {
|
reject(err)
|
}
|
},
|
fail: (err) => {
|
closeToast()
|
reject(err)
|
}
|
})
|
return
|
}
|
|
// H5: 使用原始 File(input 选择)
|
const rawFile = fileObj?.file
|
if (rawFile) {
|
// uni.uploadFile 在 H5 不支持原生 File 对象,这里用 fetch 发送 FormData
|
const formData = new FormData()
|
formData.append('file', rawFile, rawFile.name || 'file.pdf')
|
formData.append('salesLedgerId', form.value.salesLedgerId || currentId.value || '')
|
fetch(baseUrl, {
|
method: 'POST',
|
headers: { Authorization: 'Bearer ' + getToken() },
|
body: formData
|
}).then(async (res) => {
|
closeToast()
|
const data = await res.json()
|
if (data.code === 200) {
|
resolve(data.data)
|
} else {
|
reject(new Error(data.msg || '上传失败'))
|
}
|
}).catch((err) => {
|
closeToast()
|
reject(err)
|
})
|
return
|
}
|
|
closeToast()
|
reject(new Error('未找到可上传的文件'))
|
})
|
}
|
|
const afterReadUpload = async (file) => {
|
try {
|
const files = Array.isArray(file) ? file : file?.file ? [file] : [file]
|
for (const f of files) {
|
const uploaded = await uploadSingleFile(f)
|
fileList.value.push(uploaded)
|
}
|
showToast('上传成功')
|
} catch (e) {
|
showToast('上传失败')
|
}
|
}
|
|
const removeUploaded = (index) => {
|
fileList.value.splice(index, 1)
|
}
|
|
const getFileNameFromUrl = (url) => {
|
try { if (!url) return ''; return decodeURIComponent(url.split('/').pop()) } catch (e) { return url }
|
}
|
|
const loadDetail = async (id) => {
|
try {
|
showLoadingToast({ message: '加载中...' })
|
const res = await invoiceLedgerProductInfo({ id })
|
const data = res?.data || res
|
form.value = { ...data }
|
fileList.value = data?.fileList || []
|
if (!form.value.invoicePerson) {
|
form.value.invoicePerson = userStore.nickName
|
}
|
if (!form.value.invoiceDate) {
|
form.value.invoiceDate = dayjs().format('YYYY-MM-DD')
|
}
|
closeToast()
|
} catch (e) {
|
closeToast()
|
showToast('加载失败')
|
}
|
}
|
|
const submitForm = async () => {
|
try {
|
if (!form.value.invoiceNo) { showToast('请输入发票号'); return }
|
if (!form.value.invoiceTotal) { showToast('请输入发票金额'); return }
|
if (!form.value.invoiceDate) { showToast('请选择开票日期'); return }
|
showLoadingToast({ message: '提交中...' })
|
form.value.fileList = fileList.value
|
await invoiceLedgerSaveOrUpdate(form.value)
|
closeToast()
|
showToast('提交成功')
|
setTimeout(() => { uni.navigateBack() }, 800)
|
} catch (e) {
|
closeToast()
|
showToast('提交失败,请重试')
|
}
|
}
|
|
onMounted(() => {
|
const rowStr = uni.getStorageSync('invoiceLedgerEditRow')
|
if (rowStr) {
|
try {
|
const row = JSON.parse(rowStr)
|
currentId.value = row.id
|
loadDetail(currentId.value)
|
} catch (e) {
|
// ignore
|
}
|
}
|
})
|
</script>
|
|
<style scoped lang="scss">
|
.account-detail {
|
min-height: 100vh;
|
background: #f8f9fa;
|
padding-bottom: 5rem;
|
}
|
.uploaded-list { padding: 8px 16px 0 16px; }
|
.uploaded-item { display: flex; align-items: center; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #f5f5f5; }
|
.file-name { font-size: 12px; color: #333; margin-right: 8px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
.tip-text { padding: 4px 16px 0 16px; font-size: 12px; color: #888; }
|
.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; }
|
</style>
|