gaoluyang
昨天 5d51aeded717c667a22096174168e4e5e59bde39
1.来票登记开发联调
已修改4个文件
已添加3个文件
1332 ■■■■■ 文件已修改
src/components/PageHeader.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/index.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/procurementManagement/invoiceEntry/add.vue 542 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/procurementManagement/invoiceEntry/index.vue 419 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/procurementManagement/invoiceEntry/view.vue 311 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/sales/invoicingRegistration/index.vue 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/PageHeader.vue
@@ -48,7 +48,7 @@
        props.customBack();
    } else {
        emit('back');
        uni.navigateBack();
        // uni.navigateBack();
    }
};
</script>
src/pages.json
@@ -162,6 +162,27 @@
      }
    },
    {
      "path": "pages/procurementManagement/invoiceEntry/index",
      "style": {
        "navigationBarTitleText": "来票登记",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/procurementManagement/invoiceEntry/add",
      "style": {
        "navigationBarTitleText": "新增来票登记",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/procurementManagement/invoiceEntry/view",
      "style": {
        "navigationBarTitleText": "来票登记详情",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/common/webview/index",
      "style": {
        "navigationBarTitleText": "浏览网页"
src/pages/index.vue
@@ -323,6 +323,11 @@
                url: '/pages/procurementManagement/procurementLedger/index'
            });
            break;
        case '来票登记':
            uni.navigateTo({
                url: '/pages/procurementManagement/invoiceEntry/index'
            });
            break;
        case '协同审批':
            uni.navigateTo({
                url: '/pages/cooperativeOffice/collaborativeApproval/index'
src/pages/procurementManagement/invoiceEntry/add.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,542 @@
<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.purchaseLedgerNo"
          label="采购合同号"
          readonly
          placeholder="自动填充"
        />
        <van-field
          v-model="form.salesContractNo"
          label="销售合同号"
          readonly
          placeholder="自动填充"
        />
        <van-field
          v-model="form.supplierName"
          label="供应商名称"
          readonly
          placeholder="自动填充"
        />
        <van-field
          v-model="form.projectName"
          label="项目名称"
          readonly
          placeholder="自动填充"
        />
        <van-field
          v-model="form.issUer"
          label="录入人"
                    readonly
          placeholder="请输入录入人"
        />
                <van-field
                    v-model="form.enterDate"
                    label="录入日期"
                    readonly
                    placeholder="请选择录入日期"
                    @click="showCreateTimePicker = true"
                />
                <van-field
                    v-model="form.invoiceNumber"
                    label="发票号码"
                    required
                    placeholder="请输入发票号码"
                    :rules="[{ required: true, message: '请输入发票号码' }]"
                />
                <van-field
                    v-model="form.invoiceAmount"
                    label="发票金额(元)"
                    required
                    readonly
                    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.ticketsNum"
                label="本次来票数"
                type="number"
                placeholder="请输入来票数量"
                @blur="invoiceNumBlur(item)"
              />
              <van-field
                v-model="item.ticketsAmount"
                label="本次来票金额(元)"
                type="number"
                placeholder="请输入来票金额"
                @blur="invoiceAmountBlur(item)"
              />
              <!-- æœªæ¥ç¥¨ä¿¡æ¯ -->
              <van-field
                v-model="item.futureTickets"
                label="未来票数"
                readonly
              />
              <van-field
                v-model="item.futureTicketsAmount"
                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, onMounted } from 'vue'
import { showToast, showLoadingToast, closeToast } from 'vant'
import useUserStore from '@/store/modules/user'
import {addOrUpdateRegistration, getPurchaseNoById} from "@/api/procurementManagement/invoiceEntry";
import {getInfo} from "@/api/procurementManagement/invoiceEntry.js";
const userStore = useUserStore()
const editData = ref(null);
// è¡¨å•引用
const formRef = ref()
// è¡¨å•数据
let form = ref({
  purchaseLedgerNo: '',
  salesContractNo: '',
  supplierName: '',
  projectName: '',
  issUer: '',
  issueDate: '',
  enterDate: '',
    invoiceAmount: '',
  invoiceNumber: ''
})
// äº§å“æ•°æ®
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.ticketsNum || row.ticketsNum === "") {
        row.ticketsNum = 0;
    }
    if (Number(row.ticketsNum) > Number(row.tempFutureTickets)) {
        showToast('本次来票数不得大于未来票数');
        row.ticketsNum = 0;
        return;
    }
    // è®¡ç®—本次来票金额
    row.ticketsAmount = (row.ticketsNum * row.taxInclusiveUnitPrice).toFixed(2)
    // è®¡ç®—未来票数
    row.futureTickets = (row.tempFutureTickets - row.ticketsNum).toFixed(2)
    // è®¡ç®—未来票金额
    row.futureTicketsAmount = (row.tempFutureTicketsAmount - row.ticketsAmount).toFixed(2)
    calculateinvoiceAmount();
}
// æ¥ç¥¨é‡‘额变化处理
const invoiceAmountBlur = (row) => {
    if (!row.ticketsAmount) {
        row.ticketsAmount = 0;
    }
    // è®¡ç®—是否超过来票总金额
    if (row.ticketsAmount > row.tempFutureTicketsAmount) {
        showToast('本次来票金额不得大于未来票金额');
        row.ticketsAmount = 0;
    }
    // è®¡ç®—本次来票数
    row.ticketsNum = Number(
        (row.ticketsAmount / row.taxInclusiveUnitPrice).toFixed(2)
    );
    // è®¡ç®—未来票数
    row.futureTickets = (row.tempFutureTickets - row.ticketsNum).toFixed(2)
    // è®¡ç®—未来票金额
    row.futureTicketsAmount = (row.tempFutureTicketsAmount - row.ticketsAmount).toFixed(2)
    calculateinvoiceAmount();
}
const calculateinvoiceAmount = () => {
    let invoiceAmountTotal = 0;
    productData.value.forEach((item) => {
        if (item.ticketsAmount) {
            invoiceAmountTotal += Number(item.ticketsAmount);
        }
    });
    form.value.invoiceAmount = invoiceAmountTotal.toFixed(2);
};
// æ¥ç¥¨æ—¥æœŸç¡®è®¤
const onIssueDateConfirm = ({ 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.enterDate = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
    form.value.issueDate = `${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 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.issueDate = 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.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(() => {
      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.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">
.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>
src/pages/procurementManagement/invoiceEntry/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,419 @@
<template>
    <view class="sales-account">
        <!-- ä½¿ç”¨é€šç”¨é¡µé¢å¤´éƒ¨ç»„ä»¶ -->
        <PageHeader title="来票登记" @back="goBack" />
        <!-- æœç´¢å’Œç­›é€‰åŒºåŸŸ -->
        <view class="search-filter-section">
            <view class="search-bar">
                <view class="search-input">
                    <input
                        class="search-text"
                        placeholder="请输入采购合同号/供应商名称"
                        v-model="searchKeyword"
                    />
                </view>
                <view class="filter-button" @click="getList">
                    <up-icon name="search" size="24" color="#999"></up-icon>
                </view>
            </view>
        </view>
        <!-- é‡‡è´­å°è´¦ç€‘布流 -->
        <view class="ledger-list" v-if="ledgerList.length > 0">
            <view v-for="(item, index) in ledgerList" :key="index">
                <view class="ledger-item">
                    <view class="item-header">
                        <view class="item-left">
                            <view class="document-icon">
                                <up-icon name="file-text" size="16" color="#ffffff"></up-icon>
                            </view>
                            <text class="item-id">{{ item.purchaseContractNumber }}</text>
                        </view>
                    </view>
                    <up-divider></up-divider>
                    <view class="item-details">
                        <view class="detail-row">
                            <text class="detail-label">销售合同号</text>
                            <text class="detail-value">{{ item.salesContractNo }}</text>
                        </view>
                        <view class="detail-row">
                            <text class="detail-label">供应商名称</text>
                            <text class="detail-value">{{ item.supplierName }}</text>
                        </view>
                        <view class="detail-row">
                            <text class="detail-label">项目名称</text>
                            <text class="detail-value">{{ item.projectName }}</text>
                        </view>
                        <view class="detail-row">
                            <text class="detail-label">合同金额(元)</text>
                            <text class="detail-value highlight">{{ item.contractAmount }}</text>
                        </view>
                        <view class="detail-row">
                            <text class="detail-label">已开票金额(元)</text>
                            <text class="detail-value highlight">{{ item.receiptPaymentAmount }}</text>
                        </view>
                        <view class="detail-row">
                            <text class="detail-label">待开票金额(元)</text>
                            <text class="detail-value redlight">{{ item.unReceiptPaymentAmount }}</text>
                        </view>
                    </view>
                    <!-- æ“ä½œæŒ‰é’®åŒºåŸŸ -->
                    <view class="action-buttons">
                        <van-button
                            type="primary"
                            size="small"
                            @click="handleAddInvoice(item)"
                            class="action-btn"
                            :disabled="item.unReceiptPaymentAmount == 0"
                        >
                            æ–°å¢žå¼€ç¥¨
                        </van-button>
                        <van-button
                            type="default"
                            size="small"
                            @click="handleViewDetail(item)"
                            class="action-btn"
                        >
                            æŸ¥çœ‹è¯¦æƒ…
                        </van-button>
                    </view>
                </view>
            </view>
        </view>
        <view v-else class="no-data">
            <text>暂无采购台账数据</text>
        </view>
    </view>
</template>
<script setup>
import { ref } from 'vue';
import { onShow } from '@dcloudio/uni-app';
import useUserStore from "@/store/modules/user";
import {gePurchaseListPage} from "@/api/procurementManagement/invoiceEntry";
const userStore = useUserStore()
// æœç´¢å…³é”®è¯
const searchKeyword = ref('');
// é‡‡è´­å°è´¦æ•°æ®
const ledgerList = ref([]);
// è¿”回上一页
const goBack = () => {
    uni.navigateBack();
};
// æŸ¥è¯¢åˆ—表
const getList = () => {
    const page = {
        current: -1,
        size: -1
    }
    gePurchaseListPage({...page}).then((res) => {
        ledgerList.value = res.data.records;
    }).catch(() => {
        // tableLoading.value = false;
    });
};
// å¤„理新增来票
const handleAddInvoice = (item) => {
    try {
        // å­˜å‚¨é€‰ä¸­çš„合同信息
        uni.setStorageSync('editData', JSON.stringify(item));
        // è·³è½¬åˆ°æ–°å¢žæ¥ç¥¨é¡µé¢
        uni.navigateTo({
            url: '/pages/procurementManagement/invoiceEntry/add'
        });
    } catch (error) {
        console.error('处理新增来票失败:', error);
        uni.showToast({
            title: '操作失败,请重试',
            icon: 'error'
        });
    }
};
// å¤„理查看详情
const handleViewDetail = (item) => {
    try {
        // å­˜å‚¨æ•°æ®
        uni.setStorageSync('editData', JSON.stringify(item));
        // è·³è½¬åˆ°è¯¦æƒ…页面
        uni.navigateTo({
            url: '/pages/procurementManagement/invoiceEntry/view'
        });
    } catch (error) {
        console.error('处理查看详情失败:', error);
        uni.showToast({
            title: '操作失败,请重试',
            icon: 'error'
        });
    }
};
onShow(() => {
    // é¡µé¢æ˜¾ç¤ºæ—¶åˆ·æ–°åˆ—表
    getList();
});
</script>
<style scoped lang="scss">
.u-divider {
    margin: 0 !important;
}
.sales-account {
    min-height: 100vh;
    background: #f8f9fa;
    position: relative;
}
.page-header {
    background: #ffffff;
    padding: 16px 20px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    border-bottom: 1px solid #f0f0f0;
    position: sticky;
    /* å…¼å®¹ iOS åˆ˜æµ·/灵动岛安全区 */
    padding-top: env(safe-area-inset-top);
    top: 0;
    z-index: 100;
}
.header-left {
    display: flex;
    align-items: center;
    gap: 8px;
}
.nav-icon {
    width: 24px;
    height: 24px;
    background: #2979ff;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.nav-text {
    font-size: 14px;
    color: #2979ff;
    font-weight: 500;
}
.header-center {
    flex: 1;
    display: flex;
    justify-content: center;
    align-items: center;
    position: absolute;
    left: 0;
    right: 0;
    pointer-events: none;
}
.page-title {
    font-size: 18px;
    font-weight: 600;
    color: #333;
    pointer-events: auto;
}
.header-right {
    display: flex;
    align-items: center;
}
.status-bar {
    display: flex;
    align-items: center;
    gap: 4px;
}
.signal, .wifi, .battery {
    width: 16px;
    height: 8px;
    background: #333;
    border-radius: 2px;
}
.search-filter-section {
    padding: 10px 20px;
    background: #ffffff;
}
.search-bar {
    display: flex;
    align-items: center;
    gap: 12px;
}
.search-input {
    flex: 1;
    background: #f5f5f5;
    border-radius: 24px;
    padding: 10px 16px;
    display: flex;
    align-items: center;
    gap: 8px;
}
.search-text {
    flex: 1;
    font-size: 14px;
    color: #333;
    background: transparent;
    border: none;
    outline: none;
}
.search-text::placeholder {
    color: #999;
}
.filter-button {
    width: 40px;
    height: 40px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.ledger-list {
    padding: 20px;
}
.ledger-item {
    background: #ffffff;
    border-radius: 12px;
    margin-bottom: 16px;
    overflow: hidden;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
    padding: 0 16px;
}
.item-header {
    padding: 16px 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.item-left {
    display: flex;
    align-items: center;
    gap: 8px;
}
.document-icon {
    width: 24px;
    height: 24px;
    background: #2979ff;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.item-id {
    font-size: 14px;
    color: #333;
    font-weight: 500;
}
.item-tag {
    background: #4caf50;
    border-radius: 4px;
    padding: 2px 4px;
}
.tag-text {
    font-size: 11px;
    color: #ffffff;
    font-weight: 500;
}
.item-details {
    padding: 16px 0;
}
.detail-row {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    margin-bottom: 8px;
    &:last-child {
        margin-bottom: 0;
    }
}
.detail-info {
    margin-top: 10px;
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
}
.detail-label {
    font-size: 12px;
    color: #777777;
    min-width: 60px;
}
.detail-value {
    font-size: 12px;
    color: #000000;
    text-align: right;
    flex: 1;
    margin-left: 16px;
}
.detail-value.highlight {
    color: #2979ff;
    font-weight: 500;
}
.detail-value.redlight {
    color: red;
    font-weight: 500;
}
.action-buttons {
    display: flex;
    gap: 12px;
    padding: 0 0 16px 0;
    justify-content: space-between;
}
.action-btn {
    flex: 1;
}
.no-data {
    padding: 40px 0;
    text-align: center;
    color: #999;
}
.fab-button {
    position: fixed;
    bottom: 30px;
    right: 30px;
    width: 56px;
    height: 56px;
    background: #2979ff;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 4px 16px rgba(41, 121, 255, 0.3);
    z-index: 1000;
}
</style>
src/pages/procurementManagement/invoiceEntry/view.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,311 @@
<template>
  <view class="account-view">
    <!-- ä½¿ç”¨é€šç”¨é¡µé¢å¤´éƒ¨ç»„ä»¶ -->
        <PageHeader title="来票登记详情" @back="goBack" />
    <!-- åŸºæœ¬ä¿¡æ¯å±•示 -->
    <view class="info-section">
      <view class="section-title">基本信息</view>
      <view class="info-grid">
        <view class="info-item">
          <text class="info-label">采购合同号</text>
          <text class="info-value">{{ form.purchaseContractNumber }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">销售合同号</text>
          <text class="info-value">{{ form.salesContractNo }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">供应商名称</text>
          <text class="info-value">{{ form.supplierName }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">项目名称</text>
          <text class="info-value">{{ form.projectName }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">合同金额(元)</text>
          <text class="info-value highlight">{{ form.contractAmount }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">已来票金额(元)</text>
          <text class="info-value highlight">{{ form.receiptPaymentAmount }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">未来票金额(元)</text>
          <text class="info-value redlight">{{ form.unReceiptPaymentAmount }}</text>
        </view>
      </view>
    </view>
    <!-- äº§å“ä¿¡æ¯å±•示 -->
    <view class="product-section" v-if="productData && productData.length > 0">
      <view class="section-title">产品信息</view>
      <view class="product-card" v-for="(product, idx) in productData" :key="idx">
        <view class="product-header">
          <view class="product-title">
            <van-icon name="description" color="#2979ff" size="15" />
            <text class="product-productCategory">产品 {{ idx + 1 }}</text>
          </view>
        </view>
        <view class="product-info">
          <view class="info-grid">
            <view class="info-item">
              <text class="info-label">产品大类</text>
              <text class="info-value">{{ product.productCategory }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">规格型号</text>
              <text class="info-value">{{ product.specificationModel }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">单位</text>
              <text class="info-value">{{ product.unit }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">税率(%)</text>
              <text class="info-value">{{ product.taxRate }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">数量</text>
              <text class="info-value highlight">{{ product.quantity }}</text>
            </view>
                        <view class="info-item">
                            <text class="info-label">含税单价(元)</text>
                            <text class="info-value highlight">{{ product.taxInclusiveUnitPrice }}</text>
                        </view>
            <view class="info-item">
              <text class="info-label">含税总价(元)</text>
              <text class="info-value highlight">{{ product.taxInclusiveTotalPrice }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">不含税总价(元)</text>
              <text class="info-value highlight">{{ product.taxExclusiveTotalPrice }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">来票数</text>
              <text class="info-value highlight">{{ product.ticketsNum }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">来票金额(元)</text>
              <text class="info-value highlight">{{ product.ticketsAmount }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">未来票数</text>
              <text class="info-value highlight">{{ product.futureTickets }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">未来票金额(元)</text>
              <text class="info-value redlight">{{ product.futureTicketsAmount }}</text>
            </view>
          </view>
        </view>
      </view>
    </view>
    <!-- æ— äº§å“ä¿¡æ¯æç¤º -->
    <view class="no-product" v-else>
      <text>暂无产品信息</text>
    </view>
  </view>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import {getPurchaseById} from "@/api/procurementManagement/procurementLedger";
// è¡¨å•数据
const form = ref({
  id: '',
  purchaseContractNumber: '',
  salesContractNo: '',
  customerId: '',
  supplierName: '',
  projectName: '',
  executionDate: '',
    contractAmount: '',
  receiptPaymentAmount: '',
  unReceiptPaymentAmount: '',
  salesman: ''
});
// äº§å“æ•°æ®
const productData = ref([]);
// ç¼–辑数据
const editData = ref(null);
// è¿”回上一页
const goBack = () => {
  // æ¸…理本地存储的数据
  uni.removeStorageSync('editData');
  uni.navigateBack();
};
// å¡«å……表单数据
const fillFormData = () => {
  if (!editData.value) return;
  // èŽ·å–å®Œæ•´çš„äº§å“ä¿¡æ¯
    getPurchaseById({ id: editData.value.id, type: 2 }).then((res) => {
    productData.value = res.productData || [];
        // å¡«å……基本信息
        form.value.purchaseContractNumber = editData.value.purchaseContractNumber || '';
        form.value.salesContractNo = editData.value.salesContractNo || '';
        form.value.supplierName = editData.value.supplierName || '';
        form.value.projectName = editData.value.projectName || '';
        form.value.executionDate = editData.value.executionDate || '';
        form.value.contractAmount = editData.value.contractAmount || 0;
        form.value.salesman = editData.value.salesman || '';
        form.value.receiptPaymentAmount = editData.value.receiptPaymentAmount || 0;
        form.value.unReceiptPaymentAmount = editData.value.unReceiptPaymentAmount || 0;
  });
};
onMounted(() => {
  // èŽ·å–ç¼–è¾‘æ•°æ®å¹¶å¡«å……è¡¨å•
  const editDataStr = uni.getStorageSync('editData');
  if (editDataStr) {
    try {
      editData.value = JSON.parse(editDataStr);
      // ä½¿ç”¨ nextTick ç¡®ä¿æ•°æ®åŠ è½½å®ŒæˆåŽå†å¡«å……
      setTimeout(() => {
        fillFormData();
      }, 100);
    } catch (error) {
      console.error('解析编辑数据失败:', error);
    }
  }
});
</script>
<style scoped lang="scss">
.account-view {
  min-height: 100vh;
  background: #f8f9fa;
  padding-bottom: 2rem;
}
.header {
  display: flex;
  align-items: center;
  background: #fff;
  padding: 1rem 1.25rem;
  border-bottom: 0.0625rem solid #f0f0f0;
  position: sticky;
  top: 0;
  z-index: 100;
  /* å…¼å®¹ iOS åˆ˜æµ·/灵动岛安全区 */
  padding-top: env(safe-area-inset-top);
}
.title {
  flex: 1;
  text-align: center;
  font-size: 1.125rem;
  font-weight: 600;
  color: #333;
}
.info-section {
  background: #fff;
  margin: 1rem;
  padding: 1rem;
  border-radius: 0.5rem;
  box-shadow: 0 0.125rem 0.5rem rgba(0,0,0,0.04);
}
.section-title {
  font-size: 1rem;
  font-weight: 600;
  color: #333;
  margin-bottom: 1rem;
  padding-bottom: 1rem;
  border-bottom: 0.0625rem solid #e8e8e8;
}
.info-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
}
.info-item {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.info-label {
  font-size: 0.75rem;
  color: #666;
  font-weight: 400;
}
.info-value {
  font-size: 0.875rem;
  color: #333;
  font-weight: 500;
}
.info-value.highlight {
  color: #2979ff;
  font-weight: 600;
}
.info-value.redlight {
  color: red;
  font-weight: 600;
}
.product-section {
  background: #fff;
  margin: 1rem;
  padding: 1rem;
  border-radius: 0.5rem;
  box-shadow: 0 0.125rem 0.5rem rgba(0,0,0,0.04);
}
.product-card {
  background: #f8f9fa;
  border-radius: 0.5rem;
  padding: 1rem;
  margin-bottom: 1rem;
}
.product-card:last-child {
  margin-bottom: 0;
}
.product-header {
  display: flex;
  align-items: center;
  padding-bottom: 0.75rem;
  border-bottom: 0.0625rem solid #e8e8e8;
  margin-bottom: 1rem;
}
.product-title {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.product-productCategory {
  font-size: 0.875rem;
  font-weight: 500;
  color: #333;
}
.product-info .info-grid {
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
}
.no-product {
  text-align: center;
  padding: 2rem;
  color: #999;
  font-size: 0.875rem;
}
</style>
src/pages/sales/invoicingRegistration/index.vue
@@ -30,9 +30,6 @@
                            </view>
                            <text class="item-id">{{ item.salesContractNo }}</text>
                        </view>
                        <!--                            <view class="item-tag">-->
                        <!--                                <text class="tag-text">{{ item.recorder }}</text>-->
                        <!--                            </view>-->
                    </view>
                    <up-divider></up-divider>
                    
@@ -71,16 +68,16 @@
                    <view class="action-buttons">
                        <van-button
                            type="primary"
                            size="small"
                            size="small"
                            @click="handleAddInvoice(item)"
                            class="action-btn"
                            :disabled="item.entryPerson != userStore.id || item.noInvoiceAmountTotal == 0"
                            :disabled="item.noInvoiceAmountTotal == 0"
                        >
                            æ–°å¢žå¼€ç¥¨
                        </van-button>
                        <van-button
                            type="default"
                            size="small"
                        <van-button
                            type="default"
                            size="small"
                            @click="handleViewDetail(item)"
                            class="action-btn"
                        >
@@ -93,11 +90,6 @@
        <view v-else class="no-data">
            <text>暂无销售台账数据</text>
        </view>
        <!-- æµ®åŠ¨æ“ä½œæŒ‰é’® -->
        <!-- <view class="fab-button" @click="handleInfo('add')">
            <up-icon name="plus" size="24" color="#ffffff"></up-icon>
        </view> -->
    </view>
</template>
@@ -114,11 +106,6 @@
// é”€å”®å°è´¦æ•°æ®
const ledgerList = ref([]);
const total = ref(0);
// åˆåŒé€‰æ‹©å™¨ç›¸å…³
const contractList = ref([]);
const contractLoading = ref(false);
const contractFinished = ref(false);
// è¿”回上一页
const goBack = () => {
@@ -141,15 +128,6 @@
// å¤„理新增开票
const handleAddInvoice = (item) => {
    try {
        // æ£€æŸ¥æƒé™ï¼šåªæœ‰å½•入人才能新增开票
        if (item.entryPerson != userStore.id) {
            uni.showToast({
                title: '只有录入人才能新增开票',
                icon: 'none'
            });
            return;
        }
        // å­˜å‚¨é€‰ä¸­çš„合同信息
        uni.setStorageSync('editData', JSON.stringify(item));