gaoluyang
2 天以前 025e46e11cb2962fd7692adfa401333758cc779b
src/pages/procurementManagement/procurementInvoiceLedger/detail.vue
@@ -3,53 +3,62 @@
      <!-- 使用通用页面头部组件 -->
      <PageHeader title="编辑来票台账" @back="goBack" />
      <van-form @submit="submitForm" ref="formRef" label-width="120px" 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.purchaseContractNumber" label="采购合同号" readonly />
            <van-field v-model="form.salesContractNo" label="销售合同号" readonly />
            <van-field v-model="form.taxInclusiveUnitPrice" label="含税单价(元)" readonly />
            <van-field v-model="form.createdAt" label="创建时间" readonly />
            <van-field v-model="form.invoiceNumber" label="发票号" placeholder="请输入" readonly />
            <van-field v-model="form.ticketsNum" label="来票数" type="number" placeholder="请输入" required :rules="[{ required: true, message: '请输入来票数' }]" @change="inputTicketsNum"/>
            <van-field v-model="form.ticketsAmount" label="本次来票金额(元)" type="number" placeholder="请输入" required :rules="[{ required: true, message: '请输入本次来票金额' }]" @change="inputTicketsAmount"/>
      <up-form @submit="submitForm" ref="formRef" label-width="120" :model="form">
         <up-form-item label="采购合同号" prop="purchaseContractNumber">
            <up-input v-model="form.purchaseContractNumber" placeholder="自动生成" disabled />
         </up-form-item>
         <up-form-item label="销售合同号" prop="salesContractNo">
            <up-input v-model="form.salesContractNo" placeholder="自动生成" disabled />
         </up-form-item>
         <up-form-item label="含税单价(元)" prop="taxInclusiveUnitPrice">
            <up-input v-model="form.taxInclusiveUnitPrice" placeholder="自动生成" disabled />
         </up-form-item>
         <up-form-item label="创建时间" prop="createdAt">
            <up-input v-model="form.createdAt" placeholder="自动生成" disabled />
         </up-form-item>
         <up-form-item label="发票号" prop="invoiceNumber">
            <up-input v-model="form.invoiceNumber" placeholder="请输入" disabled />
         </up-form-item>
         <up-form-item label="来票数" prop="ticketsNum" required :rules="rules.ticketsNum">
            <up-input
               v-model="form.ticketsNum"
               type="number"
               placeholder="请输入"
               @blur="inputTicketsNum"
            />
         </up-form-item>
         <up-form-item label="本次来票金额(元)" prop="ticketsAmount" required :rules="rules.ticketsAmount">
            <up-input
               v-model="form.ticketsAmount"
               type="number"
               placeholder="请输入"
               @blur="inputTicketsAmount"
            />
         </up-form-item>
            <view class="tip-text">未来票数:{{ formatAmount(form.futureTickets) }} 元</view>
<!--            <van-field v-model="form.invoicePerson" label="未来票数" readonly />-->
         </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>
         <!-- 使用公共底部按钮组件 -->
         <FooterButtons
            show
            cancelText="取消"
            confirmText="保存"
            @cancel="goBack"
            @confirm="onSubmit"
         />
      </up-form>
   </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 { invoiceLedgerSaveOrUpdate } from '@/api/salesManagement/invoiceLedger.js'
import config from '@/config.js'
import {getProductRecordById, updateRegistration} from "@/api/procurementManagement/procurementInvoiceLedger";
import PageHeader from '@/components/PageHeader.vue';
import FooterButtons from '@/components/FooterButtons.vue';
const userStore = useUserStore()
@@ -72,21 +81,34 @@
const currentId = ref('')
const temFutureTickets = ref(0)
// 日期选择
const currentInvoiceDate = ref([new Date().getFullYear(), new Date().getMonth() + 1, new Date().getDate()])
// 表单校验规则
const rules = {
   ticketsNum: [
      { required: true, message: '请输入来票数', trigger: 'blur' }
   ],
   ticketsAmount: [
      { required: true, message: '请输入本次来票金额', trigger: 'blur' }
   ]
};
const goBack = () => {
   uni.removeStorageSync('invoiceLedgerEditRow');
   uni.navigateBack()
}
const inputTicketsNum = (val) => {
const inputTicketsNum = () => {
   // 确保含税单价存在且不为零
   if (!form.value.taxInclusiveUnitPrice || Number(form.value.taxInclusiveUnitPrice) === 0) {
      showToast("含税单价不能为零或未定义");
      uni.showToast({
         title: "含税单价不能为零或未定义",
         icon: 'none'
      });
      return;
   }
   if (Number(form.value.ticketsNum) > Number(temFutureTickets.value)) {
      showToast("来票数不得大于未来票数");
      uni.showToast({
         title: "来票数不得大于未来票数",
         icon: 'none'
      });
      form.value.ticketsNum = temFutureTickets.value
   }
   
@@ -96,15 +118,21 @@
   form.value.futureTickets = Number(futureTickets.toFixed(2));
   form.value.ticketsAmount = Number(ticketsAmount.toFixed(2));
};
const inputTicketsAmount = (val) => {
const inputTicketsAmount = () => {
   // 确保含税单价存在且不为零
   if (!form.value.taxInclusiveUnitPrice || Number(form.value.taxInclusiveUnitPrice) === 0) {
      showToast("含税单价不能为零或未定义");
      uni.showToast({
         title: "含税单价不能为零或未定义",
         icon: 'none'
      });
      return;
   }
   
   if (Number(val) > Number(form.value.futureTickets*form.value.taxInclusiveUnitPrice)) {
      showToast("本次来票金额不得大于总金额");
   if (Number(form.value.ticketsAmount) > Number(form.value.futureTickets*form.value.taxInclusiveUnitPrice)) {
      uni.showToast({
         title: "本次来票金额不得大于总金额",
         icon: 'none'
      });
      form.value.ticketsAmount = (form.value.futureTickets*form.value.taxInclusiveUnitPrice).toFixed(2)
      const ticketsNum = Number(form.value.ticketsAmount) / Number(form.value.taxInclusiveUnitPrice);
      form.value.ticketsNum = Number(ticketsNum.toFixed(2))
@@ -112,7 +140,7 @@
   }
   
   // 确保所有数值都转换为数字类型进行计算
   const ticketsNum = Number(val) / Number(form.value.taxInclusiveUnitPrice);
   const ticketsNum = Number(form.value.ticketsAmount) / Number(form.value.taxInclusiveUnitPrice);
   form.value.ticketsNum = Number(ticketsNum.toFixed(2));
};
const formatAmount = (val) => {
@@ -122,114 +150,11 @@
   return num.toFixed(2)
}
// 上传前校验(兼容 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: '加载中...' })
      uni.showLoading({
         title: '加载中...'
      });
      const res = await getProductRecordById({ id })
      const data = res?.data || res
      form.value = { ...data }
@@ -241,25 +166,43 @@
      if (!form.value.invoiceDate) {
         form.value.invoiceDate = dayjs().format('YYYY-MM-DD')
      }
      closeToast()
      uni.hideLoading();
   } catch (e) {
      closeToast()
      showToast('加载失败')
      uni.hideLoading();
      uni.showToast({
         title: '加载失败',
         icon: 'none'
      });
   }
}
const submitForm = async () => {
   try {
      showLoadingToast({ message: '提交中...' })
      // 提交表单的具体逻辑
      await updateRegistration(form.value)
      closeToast()
      showToast('提交成功')
      uni.showToast({
         title: '提交成功',
         icon: 'success'
      });
      setTimeout(() => { goBack() }, 800)
   } catch (e) {
      closeToast()
      showToast('提交失败,请重试')
      uni.showToast({
         title: '提交失败,请重试',
         icon: 'none'
      });
   }
}
// 表单提交
const onSubmit = () => {
   formRef.value.validate().then(() => {
      // 表单验证通过,提交表单
      submitForm();
   }).catch(error => {
      // 表单验证失败
      console.log('表单验证失败', error);
   });
};
onMounted(() => {
   const rowStr = uni.getStorageSync('invoiceLedgerEditRow')
@@ -272,21 +215,10 @@
         // 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; }
@import '@/static/scss/form-common.scss';
</style>