zhangwencui
5 天以前 b973bcee308e99b5fd8a69640f11069e810346f4
src/pages/procurementManagement/procurementInvoiceLedger/detail.vue
@@ -1,292 +1,271 @@
<template>
   <view class="account-detail">
      <!-- 使用通用页面头部组件 -->
      <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"/>
            <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>
   </view>
  <view class="account-detail">
    <!-- 使用通用页面头部组件 -->
    <PageHeader title="编辑来票台账"
                @back="goBack" />
    <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>
      <!-- 使用公共底部按钮组件 -->
      <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 { ref, onMounted } from "vue";
  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()
  const userStore = useUserStore();
const formRef = ref()
let form = ref({
   salesLedgerId: '',
   customerId: '',
   invoiceNo: '',
   invoiceTotal: '',
   taxRate: '',
   invoicePerson: '',
   invoiceDate: '',
   customerName: '',
   fileList: [],
   createTime: '',
   taxInclusiveTotalPrice: '',
   taxInclusiveUnitPrice: ''
})
const fileList = ref([])
const currentId = ref('')
const temFutureTickets = ref(0)
  const formRef = ref();
  let form = ref({
    salesLedgerId: "",
    customerId: "",
    invoiceNo: "",
    invoiceTotal: "",
    taxRate: "",
    invoicePerson: "",
    invoiceDate: "",
    customerName: "",
    fileList: [],
    createTime: "",
    taxInclusiveTotalPrice: "",
    taxInclusiveUnitPrice: "",
  });
  const fileList = ref([]);
  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) => {
   // 确保含税单价存在且不为零
   if (!form.value.taxInclusiveUnitPrice || Number(form.value.taxInclusiveUnitPrice) === 0) {
      showToast("含税单价不能为零或未定义");
      return;
   }
   if (Number(form.value.ticketsNum) > Number(temFutureTickets.value)) {
      showToast("来票数不得大于未来票数");
      form.value.ticketsNum = temFutureTickets.value
   }
   // 确保所有数值都转换为数字类型进行计算
   const ticketsAmount = Number(form.value.ticketsNum) * Number(form.value.taxInclusiveUnitPrice);
   const futureTickets = Number(temFutureTickets.value) - Number(form.value.ticketsNum);
   form.value.futureTickets = Number(futureTickets.toFixed(2));
   form.value.ticketsAmount = Number(ticketsAmount.toFixed(2));
};
const inputTicketsAmount = (val) => {
   // 确保含税单价存在且不为零
   if (!form.value.taxInclusiveUnitPrice || Number(form.value.taxInclusiveUnitPrice) === 0) {
      showToast("含税单价不能为零或未定义");
      return;
   }
   if (Number(val) > Number(form.value.futureTickets*form.value.taxInclusiveUnitPrice)) {
      showToast("本次来票金额不得大于总金额");
      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))
      return;
   }
   // 确保所有数值都转换为数字类型进行计算
   const ticketsNum = Number(val) / Number(form.value.taxInclusiveUnitPrice);
   form.value.ticketsNum = Number(ticketsNum.toFixed(2));
};
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 goBack = () => {
    uni.removeStorageSync("invoiceLedgerEditRow");
    uni.navigateBack();
  };
  const inputTicketsNum = () => {
    // 确保含税单价存在且不为零
    if (
      !form.value.taxInclusiveUnitPrice ||
      Number(form.value.taxInclusiveUnitPrice) === 0
    ) {
      uni.showToast({
        title: "含税单价不能为零或未定义",
        icon: "none",
      });
      return;
    }
    if (Number(form.value.ticketsNum) > Number(temFutureTickets.value)) {
      uni.showToast({
        title: "来票数不得大于未来票数",
        icon: "none",
      });
      form.value.ticketsNum = temFutureTickets.value;
    }
// 上传前校验(兼容 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 ticketsAmount =
      Number(form.value.ticketsNum) * Number(form.value.taxInclusiveUnitPrice);
    const futureTickets =
      Number(temFutureTickets.value) - Number(form.value.ticketsNum);
    form.value.futureTickets = Number(futureTickets.toFixed(2));
    form.value.ticketsAmount = Number(ticketsAmount.toFixed(2));
  };
  const inputTicketsAmount = () => {
    // 确保含税单价存在且不为零
    if (
      !form.value.taxInclusiveUnitPrice ||
      Number(form.value.taxInclusiveUnitPrice) === 0
    ) {
      uni.showToast({
        title: "含税单价不能为零或未定义",
        icon: "none",
      });
      return;
    }
const uploadSingleFile = async (fileObj) => {
   return new Promise((resolve, reject) => {
      showLoadingToast({ message: '正在上传...' })
      const baseUrl = config.baseUrl + '/invoiceLedger/uploadFile'
    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));
      return;
    }
      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
      }
    // 确保所有数值都转换为数字类型进行计算
    const ticketsNum =
      Number(form.value.ticketsAmount) / Number(form.value.taxInclusiveUnitPrice);
    form.value.ticketsNum = Number(ticketsNum.toFixed(2));
  };
  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);
  };
      // 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
      }
  const loadDetail = async (id, purchaseLedgerId, productModelId) => {
    try {
      uni.showLoading({
        title: "加载中...",
      });
      const res = await getProductRecordById({
        id: id,
        purchaseLedgerId: purchaseLedgerId,
        productModelId: productModelId,
      });
      const data = res?.data || res;
      form.value = { ...data };
      temFutureTickets.value = data.futureTickets;
      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");
      }
      uni.hideLoading();
    } catch (e) {
      uni.hideLoading();
      uni.showToast({
        title: "加载失败",
        icon: "none",
      });
    }
  };
      closeToast()
      reject(new Error('未找到可上传的文件'))
   })
}
  const submitForm = async () => {
    try {
      // 提交表单的具体逻辑
      await updateRegistration(form.value);
      uni.showToast({
        title: "提交成功",
        icon: "success",
      });
      setTimeout(() => {
        goBack();
      }, 800);
    } catch (e) {
      uni.showToast({
        title: "提交失败,请重试",
        icon: "none",
      });
    }
  };
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 onSubmit = () => {
    formRef.value
      .validate()
      .then(() => {
        // 表单验证通过,提交表单
        submitForm();
      })
      .catch(error => {
        // 表单验证失败
        console.log("表单验证失败", error);
      });
  };
  const purchaseLedgerId = ref("");
  const productModelId = ref({});
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 getProductRecordById({ id })
      const data = res?.data || res
      form.value = { ...data }
      temFutureTickets.value = data.futureTickets;
      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 {
      showLoadingToast({ message: '提交中...' })
      await updateRegistration(form.value)
      closeToast()
      showToast('提交成功')
      setTimeout(() => { goBack() }, 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
      }
   }
})
  onMounted(() => {
    const rowStr = uni.getStorageSync("invoiceLedgerEditRow");
    if (rowStr) {
      try {
        const row = JSON.parse(rowStr);
        currentId.value = row.id;
        purchaseLedgerId.value = row.purchaseLedgerId;
        productModelId.value = row.productModelId;
        loadDetail(currentId.value, purchaseLedgerId.value, productModelId.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; }
  @import "@/static/scss/form-common.scss";
</style>