gaoluyang
昨天 f1bcbcb10006807787247219df78e0408742604b
1.采购台账开发联调
已修改3个文件
已添加13个文件
2070 ■■■■■ 文件已修改
src/api/basicData/customerFile.js 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/basicData/product.js 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/basicData/supplierManageFile.js 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/invoiceEntry.js 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/paymentEntry.js 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/paymentLedger.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/procurementInvoiceLedger.js 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/procurementLedger.js 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/projectProfit.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/procurementManagement/taxComparison.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages.json 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/index.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/procurementManagement/procurementLedger/detail.vue 862 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/procurementManagement/procurementLedger/index.vue 342 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/procurementManagement/procurementLedger/view.vue 287 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/sales/salesAccount/detail.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/basicData/customerFile.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
// å®¢æˆ·æ¡£æ¡ˆé¡µé¢æŽ¥å£
import request from '@/utils/request'
// åˆ†é¡µæŸ¥è¯¢
export function listCustomer(query) {
    return request({
        url: '/basic/customer/list',
        method: 'get',
        params: query
    })
}
// æŸ¥è¯¢å®¢æˆ·æ¡£æ¡ˆè¯¦ç»†
export function getCustomer(id) {
    return request({
        url: '/basic/customer/' + id,
        method: 'get'
    })
}
// æ–°å¢žå®¢æˆ·æ¡£æ¡ˆ
export function addCustomer(data) {
    return request({
        url: '/basic/customer/addCustomer',
        method: 'post',
        data: data
    })
}
// ä¿®æ”¹å®¢æˆ·æ¡£æ¡ˆ
export function updateCustomer(data) {
    return request({
        url: '/basic/customer/updateCustomer',
        method: 'post',
        data: data
    })
}
// å¯¼å‡ºå®¢æˆ·æ¡£æ¡ˆ
export function exportCustomer(query) {
    return request({
        url: '/basic/customer/export',
        method: 'get',
        params: query,
        responseType: 'blob'
    })
}
// åˆ é™¤å®¢æˆ·æ¡£æ¡ˆ
export function delCustomer(ids) {
    return request({
        url: '/basic/customer/delCustomer',
        method: 'delete',
        data: ids
    })
}
src/api/basicData/product.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
// äº§å“ç»´æŠ¤é¡µé¢æŽ¥å£
import request from '@/utils/request'
// äº§å“æ ‘查询
export function productTreeList(query) {
    return request({
        url: '/basic/product/list',
        method: 'get',
        params: query
    })
}
// äº§å“æ ‘新增修改
export function addOrEditProduct(query) {
    return request({
        url: '/basic/product/addOrEditProduct',
        method: 'post',
        data: query
    })
}
// è§„格型号新增修改
export function addOrEditProductModel(query) {
    return request({
        url: '/basic/product/addOrEditProductModel',
        method: 'post',
        data: query
    })
}
// äº§å“æ ‘删除
export function delProduct(query) {
    return request({
        url: '/basic/product/delProduct',
        method: 'delete',
        data: query
    })
}
// è§„格型号删除
export function delProductModel(query) {
    return request({
        url: '/basic/product/delProductModel',
        method: 'delete',
        data: query
    })
}
// è§„格型号查询
export function modelList(query) {
    return request({
        url: '/basic/product/modelList',
        method: 'get',
        params: query
    })
}
export function modelListPage(query) {
    return request({
        url: '/basic/product/modelListPage',
        method: 'get',
        params: query
    })
}
src/api/basicData/supplierManageFile.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
// ä¾›åº”商档案页面接口
import request from '@/utils/request'
// åˆ†é¡µæŸ¥è¯¢
export function listSupplier(query) {
    return request({
        url: '/system/supplier/listPage',
        method: 'get',
        params: query
    })
}
// æŸ¥è¯¢ä¾›åº”商信息详细
export function getSupplier(id) {
    return request({
        url: '/system/supplier/' + id,
        method: 'get'
    })
}
// æ–°å¢žä¾›åº”商信息
export function addSupplier(data) {
    return request({
        url: '/system/supplier/add',
        method: 'post',
        data: data
    })
}
// ä¿®æ”¹ä¾›åº”商信息
export function updateSupplier(data) {
    return request({
        url: '/system/supplier/update',
        method: 'post',
        data: data
    })
}
// å¯¼å‡ºä¾›åº”商信息
export function exportSupplier(query) {
    return request({
        url: '/system/supplier/export',
        method: 'get',
        params: query,
        responseType: 'blob'
    })
}
// åˆ é™¤ä¾›åº”商信息
export function delSupplier(ids) {
    return request({
        url: '/system/supplier/del',
        method: 'delete',
        data: ids
    })
}
src/api/procurementManagement/invoiceEntry.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
// é‡‡è´­-来票登记接口
import request from "@/utils/request";
// æŸ¥è¯¢é‡‡è´­åˆåŒå·
export function getProduct(query) {
  return request({
    url: "/purchase/ledger/getProduct",
    method: "get",
    params: query,
  });
}
// æŸ¥è¯¢id采购合同号
export function getPurchaseNoById(query) {
  return request({
    url: "/purchase/ledger/getPurchaseNoById",
    method: "get",
    params: query,
  });
}
// æ ¹æ®é‡‡è´­åˆåŒå·æŸ¥è¯¢è¯¦ç»†ä¿¡æ¯
export function getInfo(query) {
  return request({
    url: "/purchase/ledger/getInfo",
    method: "get",
    params: query,
  });
}
// ä¸»åˆ—表查询
export function gePurchaseList(query) {
  return request({
    url: "/purchase/ledger/list",
    method: "get",
    params: query,
  });
}
// ä¸»åˆ—表查询
export function getRegistrationById(query) {
  return request({
    url: "/purchase/registration/getRegistrationById",
    method: "get",
    params: query,
  });
}
// æ–°å¢žç¼–辑来票登记
export function addOrUpdateRegistration(query) {
  return request({
    url: "/purchase/registration/addOrUpdateRegistration",
    method: "post",
    data: query,
  });
}
// åˆ é™¤æ¥ç¥¨ç™»è®°
export function delRegistration(query) {
  return request({
    url: "/purchase/registration/delRegistration",
    method: "delete",
    data: query,
  });
}
// ä¸»åˆ—表查询
export function gePurchaseListPage(query) {
  return request({
    url: "/purchase/ledger/listPage",
    method: "get",
    params: query,
  });
}
src/api/procurementManagement/paymentEntry.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,81 @@
// é‡‡è´­ä»˜æ¬¾ç™»è®°é¡µé¢æŽ¥å£
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
export function registrationList(query) {
  return request({
    url: "/purchase/paymentRegistration/list",
    method: "get",
    params: query,
  });
}
// æŸ¥è¯¢è¯¦æƒ…
export function registrationInfo(query) {
  return request({
    url: "/purchase/paymentRegistration/" + query,
    method: "get",
  });
}
// æ ¹æ®é‡‡è´­åˆåŒå·æŸ¥è¯¢è¯¦æƒ…
export function byPurchaseId(query) {
  return request({
    url: "/purchase/paymentRegistration/byPurchaseId/" + query,
    method: "get",
  });
}
// æŸ¥è¯¢é‡‡è´­åˆåŒå·
export function getPurchaseNo() {
  return request({
    url: "/purchase/ledger/getPurchaseNo",
    method: "get",
  });
}
// æ–°å¢ž
export function paymentRegistrationAdd(query) {
  return request({
    url: "/purchase/paymentRegistration",
    method: "post",
    data: query,
  });
}
// ä¿®æ”¹
export function paymentRegistrationEdit(query) {
  return request({
    url: "/purchase/paymentRegistration",
    method: "put",
    data: query,
  });
}
// åˆ é™¤
export function paymentRegistrationDel(query) {
  return request({
    url: "/purchase/paymentRegistration/delete",
    method: "delete",
    data: query,
  });
}
// èŽ·å–å‘ç¥¨å·å’Œå‘ç¥¨é‡‘é¢
export function getTicketNo(query) {
  return request({
    url: "/purchase/registration/getTicketNo",
    method: "get",
    params: query,
  });
}
// åˆ†é¡µæŸ¥è¯¢
export function paymentHistoryList(query) {
  return request({
    url: "/purchase/paymentRegistration/paymentHistoryList",
    method: "get",
    params: query,
  });
}
// åˆ†é¡µæŸ¥è¯¢
export function paymentHistoryListPage(query) {
  return request({
    url: "/purchase/paymentRegistration/paymentHistoryListPage",
    method: "get",
    params: query,
  });
}
src/api/procurementManagement/paymentLedger.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
// é‡‡è´­å°è´¦é¡µé¢æŽ¥å£
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
export function paymentLedgerList(query) {
  return request({
    url: "/purchase/paymentRegistration/paymentLedgerList",
    method: "get",
    params: query,
  });
}
// åˆ†é¡µæŸ¥è¯¢
export function paymentRecordList(supplierId) {
  return request({
    url: "/purchase/paymentRegistration/getPaymentRecordList/" + supplierId,
    method: "get",
  });
}
src/api/procurementManagement/procurementInvoiceLedger.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,125 @@
// é‡‡è´­-来票台账接口
import request from "@/utils/request";
// æŸ¥è¯¢åˆ—表
export function invoiceList(query) {
  return request({
    url: "/purchase/registration/list",
    method: "get",
    params: query,
  });
}
// æŸ¥è¯¢è¯¦æƒ…
// export function getInvoiceById(query) {
//   return request({
//     url: "/purchase/registration/getRegistrationById",
//     method: "get",
//     params: query,
//   });
// }
// æ–°å¢žã€ç¼–辑
export function addOrUpdateInvoice(query) {
  return request({
    url: "/purchase/invoice/addOrUpdateInvoice",
    method: "post",
    data: query,
  });
}
// åˆ é™¤
export function delInvoice(query) {
  return request({
    url: "/purchase/invoice/delInvoice",
    method: "delete",
    data: query,
  });
}
// åˆ é™¤æ¥ç¥¨å°è´¦
export function delRegistration(query) {
  return request({
    url: "/purchase/registration/delRegistration",
    method: "delete",
    data: query,
  });
}
// åˆ é™¤é™„ä»¶
export function delCommonFile(query) {
  return request({
    url: "/commonFile/delCommonFile",
    method: "delete",
    data: query,
  });
}
// å­è¡¨æ ¼æŸ¥è¯¢
export function productRecordList(query) {
  return request({
    url: "/purchase/registration/productRecordList",
    method: "get",
    params: query,
  });
}
// æŸ¥è¯¢åˆ—表
export function invoiceListPage(query) {
  return request({
    url: "/purchase/registration/listPage",
    method: "get",
    params: query,
  });
}
export function productRecordPage(query) {
  return request({
    url: "/purchase/registration/productRecordPage",
    method: "get",
    params: query,
  });
}
export function productUploadFile(data) {
  return request({
    url: "/file/uploadFile",
    method: "post",
    data: data,
  });
}
export function getProductRecordById(params) {
  return request({
    url: "/purchase/registration/getProductRecordById",
    method: "get",
    params: params,
  });
}
export function updateRegistration(data) {
  return request({
    url: "/purchase/registration/updateRegistration",
    method: "post",
    data: data,
  });
}
// æŸ¥è¯¢ä»˜æ¬¾ç™»è®°å­åˆ—表
export function registrationListPageGetById(query) {
  return request({
    url: "/purchase/registration/getById",
    method: "get",
    params: query,
  });
}
// ä¿®æ”¹ä»˜æ¬¾æµæ°´
export function updatePaymentRegistration(query) {
  return request({
    url: "/purchase/registration/updatePaymentRegistration",
    method: "put",
    data: query,
  });
}
// åˆ é™¤ä»˜æ¬¾æµæ°´
export function delPaymentRegistration(query) {
  return request({
    url: "/purchase/registration/delPaymentRegistration",
    method: "delete",
    data: query,
  });
}
src/api/procurementManagement/procurementLedger.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,74 @@
// é‡‡è´­å°è´¦é¡µé¢æŽ¥å£
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
export function purchaseList(query) {
  return request({
    url: "/purchase/ledger/list",
    method: "get",
    params: query,
  });
}
// æŸ¥è¯¢åˆåŒå·
export function getSalesNo(query) {
  return request({
    url: "/purchase/ledger/getSalesNo",
    method: "get",
    params: query,
  });
}
// å­è¡¨æ ¼æŸ¥è¯¢
export function productList(query) {
  return request({
    url: "/sales/product/list",
    method: "get",
    params: query,
  });
}
// æ–°å¢žã€ç¼–辑
export function addOrEditPurchase(query) {
  return request({
    url: "/purchase/ledger/addOrEditPurchase",
    method: "post",
    data: query,
  });
}
// åˆ é™¤
export function delPurchase(query) {
  return request({
    url: "/purchase/ledger/delPurchase",
    method: "delete",
    data: query,
  });
}
// æŸ¥è¯¢è¯¦æƒ…
export function getPurchaseById(query) {
  return request({
    url: "/purchase/ledger/getPurchaseById",
    method: "get",
    params: query,
  });
}
// æŸ¥è¯¢è¯¦æƒ…
export function getOptions(query) {
  return request({
    url: "/system/supplier/getOptions",
    method: "get",
    params: query,
  });
}
export function purchaseListPage(query) {
  return request({
    url: "/purchase/ledger/listPage",
    method: "get",
    params: query,
  });
}
export function createPurchaseNo() {
  return request({
    url: "/purchase/ledger/createPurchaseNo",
    method: "get",
  });
}
src/api/procurementManagement/projectProfit.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,10 @@
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
export function getPurchaseList(query) {
  return request({
    url: "/purchase/report/list",
    method: "get",
    params: query,
  });
}
src/api/procurementManagement/taxComparison.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,10 @@
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
export function getTaxList(query) {
  return request({
    url: "/purchase/report/listVat",
    method: "get",
    params: query,
  });
}
src/pages.json
@@ -141,6 +141,27 @@
      }
    },
    {
      "path": "pages/procurementManagement/procurementLedger/index",
      "style": {
        "navigationBarTitleText": "采购台账",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/procurementManagement/procurementLedger/detail",
      "style": {
        "navigationBarTitleText": "修改台账",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/procurementManagement/procurementLedger/view",
      "style": {
        "navigationBarTitleText": "台账详情",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/common/webview/index",
      "style": {
        "navigationBarTitleText": "浏览网页"
src/pages/index.vue
@@ -318,6 +318,11 @@
                url: '/pages/sales/receiptPaymentLedger/index'
            });
            break;
        case '采购台账':
            uni.navigateTo({
                url: '/pages/procurementManagement/procurementLedger/index'
            });
            break;
        case '协同审批':
            uni.navigateTo({
                url: '/pages/cooperativeOffice/collaborativeApproval/index'
src/pages/procurementManagement/procurementLedger/detail.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,862 @@
<template>
  <view class="account-detail">
    <!-- ä½¿ç”¨é€šç”¨é¡µé¢å¤´éƒ¨ç»„ä»¶ -->
        <PageHeader title="台账详情" @back="goBack" />
         <!-- è¡¨å•区域 -->
        <van-form @submit="onSubmit" label-width="110px" input-align="right" style="margin-top: 10px" error-message-align="right" scroll-to-error scroll-to-error-position="center">
            <van-field label="采购合同号" name="purchaseContractNumber" borderBottom="true" v-model="form.purchaseContractNumber" placeholder="自动生成" :rules="[{ required: true, message: '请输入' }]">
            </van-field>
            <van-field
                v-model="form.salesContractNo"
                is-link
                readonly
                name="salesContractNo"
                label="销售合同号"
                required
                placeholder="点击选择销售合同号"
                :rules="[{ required: true, message: '请选择销售合同号' }]"
                @click="showPicker = true"
            />
            <van-field
                v-model="form.supplierName"
                is-link
                readonly
                required
                name="supplierName"
                label="供应商名称"
                placeholder="点击选择供应商"
                :rules="[{ required: true, message: '请选择供应商' }]"
                @click="showCustomerPicker = true"
            />
            <van-field label="项目名称" name="projectName" borderBottom="true" v-model="form.projectName" placeholder="请输入项目名称" :rules="[{ required: true, message: '项目名称不能为空' }]" required>
            </van-field>
            <van-field label="付款方式" name="paymentMethod" borderBottom="true" v-model="form.paymentMethod" placeholder="请输入付款方式">
            </van-field>
            <van-field label="录入人" name="recorderName" borderBottom="true" v-model="form.recorderName" placeholder="请输入" disabled>
            </van-field>
            <van-field label="录入日期" name="entryDate" borderBottom="true" v-model="form.entryDate" placeholder="请输入" disabled>
            </van-field>
            <van-popup v-model:show="showPicker" destroy-on-close position="bottom">
                <van-picker
                    :columns="salesContractList"
                    v-model="pickerValue"
                    @confirm="onConfirm"
                    @cancel="showPicker = false"
                />
            </van-popup>
            <van-popup v-model:show="showCustomerPicker" destroy-on-close position="bottom">
                <van-picker
                    :columns="supplierList"
                    v-model="pickerCustomerValue"
                    @confirm="onCustomerConfirm"
                    @cancel="showCustomerPicker = false"
                />
            </van-popup>
            <!-- äº§å“å¤§ç±»é€‰æ‹©å™¨ -->
            <van-popup v-model:show="showCategoryPicker" destroy-on-close position="bottom">
                <!-- å¤´éƒ¨æŒ‰é’®åŒºåŸŸ -->
                <view class="popup-header">
                    <view @click="showCategoryPicker = false" class="cancelButton">取消</view>
                    <view @click="confirmCategorySelection" class="confirmButton">确定</view>
                </view>
                <up-tree
                    :data="productOptions"
                    :props="defaultProps"
                    show-checkbox
                    default-expand-all
                    check-strictly
                    @check-change="onCategoryConfirm"
                />
            </van-popup>
            <!-- è§„格型号选择器 -->
            <van-popup v-model:show="showSpecificationPicker" destroy-on-close position="bottom">
                <van-picker
                    :columns="modelOptions"
                    v-model="pickerSpecificationValue"
                    @confirm="onSpecificationConfirm"
                    @cancel="showSpecificationPicker = false"
                />
            </van-popup>
            <!-- ç¨ŽçŽ‡é€‰æ‹©å™¨ -->
            <van-popup v-model:show="showTaxRatePicker" destroy-on-close position="bottom">
                <van-picker
                    :columns="taxRateOptions"
                    v-model="pickerTaxRateValue"
                    @confirm="onTaxRateConfirm"
                    @cancel="showTaxRatePicker = false"
                />
            </van-popup>
            <!-- å‘票类型选择器 -->
            <van-popup v-model:show="showInvoiceTypePicker" destroy-on-close position="bottom">
                <van-picker
                    :columns="invoiceTypeOptions"
                    v-model="pickerInvoiceTypeValue"
                    @confirm="onInvoiceTypeConfirm"
                    @cancel="showInvoiceTypePicker = false"
                />
            </van-popup>
            <!-- äº§å“ä¿¡æ¯ -->
            <view class="product-section">
                <view class="section-header">
                    <text class="section-title">产品信息</text>
                    <van-button type="primary" size="small" @click="addProduct" class="add-btn" icon="plus"  v-if="operationType !== 'view'">新增</van-button>
                </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 class="product-actions"  v-if="operationType !== 'view'">
                            <van-button type="danger" size="mini" @click="removeProduct(idx)" class="del-btn" icon="delete">删除</van-button>
                        </view>
                    </view>
                    <!-- äº§å“ä¿¡æ¯è¡¨å• -->
                    <view class="product-form">
                        <!-- äº§å“å¤§ç±» -->
                        <van-field
                            v-model="product.productCategory"
                            is-link
                            readonly
                            name="productCategory"
                            label="产品大类"
                            required
                            placeholder="请选择"
                            :rules="[{ required: true, message: '请选择' }]"
                            @click="openCategoryPicker(idx)"
                        />
                        <!-- è§„格型号 -->
                        <van-field
                            v-model="product.specificationModel"
                            is-link
                            readonly
                            name="specificationModel"
                            label="规格型号"
                            required
                            :rules="[{ required: true, message: '请选择' }]"
                            placeholder="请选择"
                            @click="openSpecificationPicker(idx)"
                        />
                        <!-- å•位 -->
                        <van-field
                            v-model="product.unit"
                            name="unit"
                            label="单位"
                            required
                            :rules="[{ required: true, message: '请输入' }]"
                            placeholder="请输入"
                        />
                        <!-- ç¨Žçއ -->
                        <van-field
                            v-model="product.taxRate"
                            is-link
                            readonly
                            name="taxRate"
                            label="税率(%)"
                            required
                            :rules="[{ required: true, message: '请选择' }]"
                            placeholder="请选择"
                            @click="openTaxRatePicker(idx)"
                        />
                        <!-- å«ç¨Žå•ä»· -->
                        <van-field
                            v-model="product.taxInclusiveUnitPrice"
                            name="taxInclusiveUnitPrice"
                            label="含税单价(元)"
                            type="number"
                            required
                            :rules="[{ required: true, message: '请输入' }]"
                            placeholder="请输入"
                            @blur="formatTaxPrice(idx)"
                        />
                        <!-- æ•°é‡ -->
                        <van-field
                            v-model="product.quantity"
                            name="quantity"
                            label="数量"
                            type="number"
                            :rules="[{ required: true, message: '请输入' }]"
                            required
                            placeholder="请输入"
                            @blur="formatAmount(idx)"
                        />
                        <!-- å«ç¨Žæ€»ä»· -->
                        <van-field
                            v-model="product.taxInclusiveTotalPrice"
                            name="taxInclusiveTotalPrice"
                            label="含税总价(元)"
                            type="number"
                            :rules="[{ required: true, message: '请输入' }]"
                            required
                            placeholder="请输入"
                            @blur="formatTaxTotal(idx)"
                        />
                        <!-- ä¸å«ç¨Žæ€»ä»· -->
                        <van-field
                            v-model="product.taxExclusiveTotalPrice"
                            name="taxExclusiveTotalPrice"
                            label="不含税总价(元)"
                            type="number"
                            required
                            :rules="[{ required: true, message: '请输入' }]"
                            placeholder="请输入"
                            @blur="formatNoTaxTotal(idx)"
                        />
                        <!-- å‘票类型 -->
                        <van-field
                            v-model="product.invoiceType"
                            is-link
                            readonly
                            name="invoiceType"
                            label="发票类型"
                            :rules="[{ required: true, message: '请选择' }]"
                            required
                            placeholder="请选择"
                            @click="openInvoiceTypePicker(idx)"
                        />
                    </view>
                </view>
            </view>
            <view class="footer-btns" v-if="operationType !== 'view'">
                <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>
</template>
<script setup>
import {onMounted, ref} from 'vue';
import { modelList, productTreeList } from "@/api/basicData/product.js";
import useUserStore from "@/store/modules/user";
import {calculateTaxExclusiveTotalPrice} from "@/utils/summarizeTable";
import {
    addOrEditPurchase, createPurchaseNo,
    getOptions,
    getPurchaseById,
    getSalesNo
} from "@/api/procurementManagement/procurementLedger";
// èŽ·å–é¡µé¢å‚æ•°
const operationType = ref('');
const editData = ref(null);
const userStore = useUserStore()
const form = ref({
    id: '',
  salesContractNo: '',
    purchaseContractNumber: '',
    supplierId: '',
    supplierName: '',
    projectName: '',
  paymentMethod: '',
    recorderId: '',
    recorderName: '',
    entryDate: '',
});
const pickerValue = ref(['']);
const pickerDateValue = ref([]);
const showPicker = ref(false);
const pickerCustomerValue = ref(['']);
const showCustomerPicker = ref(false);
const salesContractList = ref([]);
const supplierList = ref([]);
const productData = ref([]);
// é€‰æ‹©å™¨ç›¸å…³å˜é‡
const showCategoryPicker = ref(false);
const showSpecificationPicker = ref(false);
const showTaxRatePicker = ref(false);
const showInvoiceTypePicker = ref(false);
const pickerSpecificationValue = ref(['']);
const pickerTaxRateValue = ref(['']);
const pickerInvoiceTypeValue = ref(['']);
const currentProductIndex = ref(0);
// é€‰é¡¹æ•°æ®
const productOptions = ref([]);
const selectedCategoryNode = ref(null);
const defaultProps = ref({
    children: 'children',
    label: 'label',
    nodeKey: 'id'
});
const modelOptions = ref([]);
// é˜²æ­¢å¾ªçŽ¯è®¡ç®—çš„æ ‡å¿—
const taxRateOptions = ref([
  { text: '1', value: '1' },
  { text: '6', value: '6' },
  { text: '13', value: '13' },
]);
const invoiceTypeOptions = ref([
  { text: '增普票', value: '增普票' },
  { text: '增专票', value: '增专票' },
]);
const addProduct = () => {
    if (productData.value === null) {
        productData.value = []
    }
    productData.value.push({
    productCategory: '',
    specificationModel: '',
        productModelId: '',
    unit: '',
    taxRate: '',
    taxInclusiveUnitPrice: '',
    quantity: '',
    taxInclusiveTotalPrice: '',
    taxExclusiveTotalPrice: '',
    invoiceType: ''
  });
};
const onConfirm = ({ selectedValues, selectedOptions }) => {
    form.value.salesContractNo = selectedOptions[0]?.text;
    form.value.salesLedgerId = selectedOptions[0]?.value;
    pickerValue.value = [selectedValues[0]];
    showPicker.value = false;
};
const onCustomerConfirm = ({ selectedValues, selectedOptions }) => {
    form.value.supplierName = selectedOptions[0]?.text;
    form.value.supplierId = selectedOptions[0]?.value;
    pickerCustomerValue.value = [selectedValues[0]];
    showCustomerPicker.value = false;
};
const removeProduct = (idx) => {
    productData.value.splice(idx, 1);
};
// æ˜¾ç¤ºé€‰æ‹©å™¨
const openCategoryPicker = (idx) => {
  currentProductIndex.value = idx;
  showCategoryPicker.value = true;
};
const openSpecificationPicker = (idx) => {
  currentProductIndex.value = idx;
  showSpecificationPicker.value = true;
};
const openTaxRatePicker = (idx) => {
  currentProductIndex.value = idx;
  showTaxRatePicker.value = true;
};
const openInvoiceTypePicker = (idx) => {
  currentProductIndex.value = idx;
  showInvoiceTypePicker.value = true;
};
// é€‰æ‹©å™¨ç¡®è®¤äº‹ä»¶
const onCategoryConfirm = (node) => {
    // èŽ·å–é€‰ä¸­çš„èŠ‚ç‚¹ä¿¡æ¯
    console.log('selected node---', node);
    // å­˜å‚¨é€‰ä¸­çš„节点,用于确认时获取数据
    selectedCategoryNode.value = node;
};
// ç¡®è®¤äº§å“å¤§ç±»é€‰æ‹©
const confirmCategorySelection = () => {
    if (selectedCategoryNode.value) {
        // è®¾ç½®é€‰ä¸­çš„产品大类
        productData.value[currentProductIndex.value].productCategory = selectedCategoryNode.value.label;
        const id = selectedCategoryNode.value.id
        // é‡ç½®é€‰ä¸­çš„节点
        selectedCategoryNode.value = null;
        productData.value[currentProductIndex.value].specificationModel = ''
        productData.value[currentProductIndex.value].productModelId = ''
        productData.value[currentProductIndex.value].pickerSpecificationValue = ['']
        getModels(id)
    }
    showCategoryPicker.value = false;
};
// èŽ·å–è§„æ ¼åž‹å·
const getModels = (value) => {
    modelList({ id: value }).then((res) => {
        modelOptions.value = res.map(user => ({
            text: user.model,
            value: user.id,
            unit: user.unit,
        }));
    });
};
// é€‰æ‹©è§„格型号
const onSpecificationConfirm = ({ selectedValues, selectedOptions }) => {
    productData.value[currentProductIndex.value].specificationModel = selectedOptions[0]?.text;
    productData.value[currentProductIndex.value].productModelId = selectedOptions[0]?.value;
    productData.value[currentProductIndex.value].unit = selectedOptions[0]?.unit;
  pickerSpecificationValue.value = [selectedValues[0]];
  showSpecificationPicker.value = false;
};
// é€‰æ‹©ç¨Žçއ
const onTaxRateConfirm = ({ selectedValues, selectedOptions }) => {
    productData.value[currentProductIndex.value].taxRate = selectedOptions[0]?.value;
  pickerTaxRateValue.value = [selectedValues[0]];
  showTaxRatePicker.value = false;
    // if (isCalculating.value) return;
    const inclusiveTotalPrice = parseFloat(productData.value[currentProductIndex.value].taxInclusiveTotalPrice);
    const taxRate = parseFloat(productData.value[currentProductIndex.value].taxRate);
    if (!inclusiveTotalPrice || !taxRate) {
        return;
    }
    // isCalculating.value = true;
    // è®¡ç®—不含税总价
    productData.value[currentProductIndex.value].taxExclusiveTotalPrice =
        calculateTaxExclusiveTotalPrice(
            inclusiveTotalPrice,
            taxRate
        );
    // isCalculating.value = false;
};
const onInvoiceTypeConfirm = ({ selectedValues, selectedOptions }) => {
    productData.value[currentProductIndex.value].invoiceType = selectedOptions[0]?.text;
  pickerInvoiceTypeValue.value = [selectedValues[0]];
  showInvoiceTypePicker.value = false;
};
// æ ¼å¼åŒ–函数 - å›ºå®šä¸¤ä½å°æ•°
const formatTaxPrice = (idx) => {
  if (productData.value[idx].taxInclusiveUnitPrice) {
    const value = parseFloat(productData.value[idx].taxInclusiveUnitPrice);
    if (!isNaN(value)) {
            productData.value[idx].taxInclusiveUnitPrice = value.toFixed(2);
    }
  }
    if (!productData.value[currentProductIndex.value].taxRate) {
        uni.showToast({
            title: '请先选择税率',
            icon: 'none'
        });
        return;
    }
    const quantity = parseFloat(productData.value[currentProductIndex.value].quantity);
    const unitPrice = parseFloat(productData.value[currentProductIndex.value].taxInclusiveUnitPrice);
    if (!quantity || quantity <= 0 || !unitPrice) {
        return;
    }
    // è®¡ç®—含税总价
    productData.value[currentProductIndex.value].taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
    // å¦‚果有税率,计算不含税总价
    if (productData.value[currentProductIndex.value].taxRate) {
        productData.value[currentProductIndex.value].taxExclusiveTotalPrice =
            calculateTaxExclusiveTotalPrice(
                productData.value[currentProductIndex.value].taxInclusiveTotalPrice,
                productData.value[currentProductIndex.value].taxRate
            );
    }
};
// æ•°é‡è¾“入框失焦
const formatAmount = (idx) => {
  if (productData.value[idx].quantity) {
    const value = parseFloat(productData.value[idx].quantity);
    if (!isNaN(value)) {
            productData.value[idx].quantity = value.toFixed(2);
    }
  }
    if (!productData.value[currentProductIndex.value].taxRate) {
        uni.showToast({
            title: '请先选择税率',
            icon: 'none'
        });
        return;
    }
    const quantity = parseFloat(productData.value[currentProductIndex.value].quantity);
    const unitPrice = parseFloat(productData.value[currentProductIndex.value].taxInclusiveUnitPrice);
    if (!quantity || quantity <= 0 || !unitPrice) {
        return;
    }
    // è®¡ç®—含税总价
    productData.value[currentProductIndex.value].taxInclusiveTotalPrice = (unitPrice * quantity).toFixed(2);
    // å¦‚果有税率,计算不含税总价
    if (productData.value[currentProductIndex.value].taxRate) {
        productData.value[currentProductIndex.value].taxExclusiveTotalPrice =
            calculateTaxExclusiveTotalPrice(
                productData.value[currentProductIndex.value].taxInclusiveTotalPrice,
                productData.value[currentProductIndex.value].taxRate
            );
    }
};
// å«ç¨Žæ€»ä»·å¤±ç„¦ï¼Œæ ¹æ®å«ç¨Žæ€»ä»·è®¡ç®—含税单价和数量
const formatTaxTotal = (idx) => {
  if (productData.value[idx].taxInclusiveTotalPrice) {
    const value = parseFloat(productData.value[idx].taxInclusiveTotalPrice);
    if (!isNaN(value)) {
            productData.value[idx].taxInclusiveTotalPrice = value.toFixed(2);
    }
  }
    const totalPrice = parseFloat(productData.value[currentProductIndex.value].taxInclusiveTotalPrice);
    const quantity = parseFloat(productData.value[currentProductIndex.value].quantity);
    if (!totalPrice || !quantity || quantity <= 0) {
        return;
    }
    // è®¡ç®—含税单价 = å«ç¨Žæ€»ä»· / æ•°é‡
    productData.value[currentProductIndex.value].taxInclusiveUnitPrice = (totalPrice / quantity).toFixed(2);
    // å¦‚果有税率,计算不含税总价
    if (productData.value[currentProductIndex.value].taxRate) {
        productData.value[currentProductIndex.value].taxExclusiveTotalPrice =
            calculateTaxExclusiveTotalPrice(
                totalPrice,
                productData.value[currentProductIndex.value].taxRate
            );
    }
};
// ä¸å«ç¨Žæ€»ä»·å¤±ç„¦, æ ¹æ®ä¸å«ç¨Žæ€»ä»·è®¡ç®—含税单价和数量
const formatNoTaxTotal = (idx) => {
  if (productData.value[idx].taxExclusiveTotalPrice) {
    const value = parseFloat(productData.value[idx].taxExclusiveTotalPrice);
    if (!isNaN(value)) {
            productData.value[idx].taxExclusiveTotalPrice = value.toFixed(2);
    }
  }
    if (!productData.value[currentProductIndex.value].taxRate) {
        uni.showToast({
            title: '请先选择税率',
            icon: 'none'
        });
        return;
    }
    const exclusiveTotalPrice = parseFloat(productData.value[currentProductIndex.value].taxExclusiveTotalPrice);
    const quantity = parseFloat(productData.value[currentProductIndex.value].quantity);
    const taxRate = parseFloat(productData.value[currentProductIndex.value].taxRate);
    if (!exclusiveTotalPrice || !quantity || quantity <= 0 || !taxRate) {
        return;
    }
    // å…ˆè®¡ç®—含税总价 = ä¸å«ç¨Žæ€»ä»· / (1 - ç¨Žçއ/100)
    const taxRateDecimal = taxRate / 100;
    const inclusiveTotalPrice = exclusiveTotalPrice / (1 - taxRateDecimal);
    productData.value[currentProductIndex.value].taxInclusiveTotalPrice = inclusiveTotalPrice.toFixed(2);
    // è®¡ç®—含税单价 = å«ç¨Žæ€»ä»· / æ•°é‡
    productData.value[currentProductIndex.value].taxInclusiveUnitPrice = (inclusiveTotalPrice / quantity).toFixed(2);
};
const goBack = () => {
    // æ¸…理本地存储的数据
    uni.removeStorageSync('operationType');
    uni.removeStorageSync('editData');
    uni.navigateBack();
};
const onSubmit = () => {
    if (productData.value !== null && productData.value.length > 0) {
        form.value.productData = JSON.parse(JSON.stringify(productData.value));
    } else {
        uni.showToast({
            title: '请添加产品信息',
            icon: 'none'
        });
        return
    }
    form.value.type = 2;
    addOrEditPurchase(form.value).then((res) => {
        uni.showToast({
            title: '提交成功',
            icon: 'success',
        });
        goBack();
    });
};
const setUserInfo = () => {
    form.value.recorderId = userStore.id;
    form.value.recorderName = userStore.nickName;
    // è®¾ç½®å½“天日期
    const today = new Date()
    const year = today.getFullYear()
    const month = String(today.getMonth() + 1).padStart(2, '0')
    const day = String(today.getDate()).padStart(2, '0')
    form.value.entryDate = `${year}-${month}-${day}`
    pickerDateValue.value = [year.toString(), month.toString(), day.toString()]
}
// å¡«å……表单数据(编辑模式)
const fillFormData = () => {
  if (!editData.value) return;
    getPurchaseById({ id: editData.value.id, type: 2 }).then((res) => {
        productData.value = res.productData;
    });
    console.log(editData.value)
  // å¡«å……基本信息
  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.paymentMethod = editData.value.paymentMethod || '';
  form.value.salesLedgerId = editData.value.salesLedgerId || '';
  form.value.recorderId = editData.value.recorderId || '';
  form.value.recorderName = editData.value.recorderName || '';
  form.value.entryDate = editData.value.entryDate || '';
  form.value.id = editData.value.id || '';
  form.value.supplierId = editData.value.supplierId || '';
  // è®¾ç½®é”€å”®åˆåŒå·é€‰æ‹©å™¨çš„值
  if (editData.value.salesContractNo) {
    const salesmanIndex = salesContractList.value.findIndex(user => user.text === editData.value.salesContractNo);
    if (salesmanIndex !== -1) {
      pickerValue.value = [salesContractList.value[salesmanIndex].value];
    }
  }
  // è®¾ç½®ä¾›åº”商选择器的值
  if (editData.value.supplierName) {
    const customerIndex = supplierList.value.findIndex(customer => customer.text === editData.value.supplierName);
    if (customerIndex !== -1) {
      pickerCustomerValue.value = [supplierList.value[customerIndex].value]
    }
  }
  // è®¾ç½®æ—¥æœŸé€‰æ‹©å™¨çš„值
  if (editData.value.executionDate) {
        pickerDateValue.value = editData.value.executionDate.split('-').map(num => parseInt(num, 10))
        console.log(pickerDateValue.value)
    }
};
const getSalesNoList = () => {
    getSalesNo().then((res) => {
        // å°†ç”¨æˆ·æ•°æ®ç»„装成 picker éœ€è¦çš„æ ¼å¼
        salesContractList.value = res.map(user => ({
            text: user.salesContractNo,
            value: user.id
        }));
    })
}
const getOptionsLIst = () => {
    getOptions().then((res) => {
        // å°†ç”¨æˆ·æ•°æ®ç»„装成 picker éœ€è¦çš„æ ¼å¼
        supplierList.value = res.data.map(item => ({
            text: item.supplierName,
            value: item.id
        }));
    })
}
const convertIdToValue = (data) => {
    // å¦‚果传入的不是数组,则返回空数组
    if (!Array.isArray(data)) {
        return [];
    }
    // é€’归映射函数
    return data.map(item => {
        // åˆ›å»ºæ–°å¯¹è±¡ï¼Œæ˜ å°„字段
        const mappedItem = {
            label: item.label, // å…³é”®ï¼šå°† label æ˜ å°„为 text
            id: item.id,       // ä¿ç•™ id
        };
        // å¦‚果存在 children æ•°ç»„,则递归处理
        if (item.children && Array.isArray(item.children) && item.children.length > 0) {
            mappedItem.children = convertIdToValue(item.children);
        }
        return mappedItem;
    });
};
// èŽ·å–äº§å“å¤§ç±»tree数据
const getProductOptions = () => {
    productTreeList().then((res) => {
        productOptions.value = convertIdToValue(res);
    });
};
onMounted(() => {
    // èŽ·å–é¡µé¢å‚æ•°
    operationType.value = uni.getStorageSync('operationType') || '';
    // èŽ·å–é”€å”®åˆåŒå·åˆ—è¡¨
    getSalesNoList()
    // èŽ·å–ä¾›åº”å•†åˆ—è¡¨
    getOptionsLIst()
    // èŽ·å–äº§å“å¤§ç±»tree数据
    getProductOptions()
    // èµ‹å€¼é»˜è®¤ä¿¡æ¯
    if (operationType.value === 'add') {
        setUserInfo()
        createPurchaseNo().then((res) => {
            form.value.purchaseContractNumber = res.data;
        });
    }
    // èŽ·å–ç¼–è¾‘æ•°æ®å¹¶å¡«å……è¡¨å•
    const editDataStr = uni.getStorageSync('editData');
    if (editDataStr) {
        try {
            editData.value = JSON.parse(editDataStr);
            // å¦‚果是编辑模式,等待数据加载完成后填充表单数据
            if (operationType.value !== 'add' && editData.value) {
                // ä½¿ç”¨ nextTick ç¡®ä¿æ•°æ®åŠ è½½å®ŒæˆåŽå†å¡«å……
                setTimeout(() => {
                    fillFormData();
                }, 100);
            }
        } catch (error) {
            console.error('解析编辑数据失败:', error);
        }
    }
});
</script>
<style scoped lang="scss">
.account-detail {
  min-height: 100vh;
  background: #f8f9fa;
  padding-bottom: 5rem;
}
.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;
}
.form-section {
    margin-top: 1rem;
}
.van-field {
    height: 3.4rem;
}
.van-cell {
    align-items: center;
}
.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-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;
}
.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-productCategory {
  margin-left: 0.5rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: #333;
}
.info-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
  margin-bottom: 1rem;
}
.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;
}
.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;
}
.popup-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1rem;
    background: #fff;
    position: sticky;
    top: 0;
    z-index: 10;
}
.cancelButton {
    color: #969799
}
.confirmButton {
    color: #1989FA
}
.u-tree {
    height: 13rem;
}
</style>
src/pages/procurementManagement/procurementLedger/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,342 @@
<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" @click="handleInfo('edit', 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 class="item-tag">-->
                        <!--                                <text class="tag-text">{{ item.recorder }}</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">{{ item.paymentMethod }}</text>
                        </view>
                        <view class="detail-row">
                            <text class="detail-label">合同金额(元)</text>
                            <text class="detail-value highlight">{{ item.contractAmount }}</text>
                        </view>
                        <up-divider></up-divider>
                        <view class="detail-info">
                            <view class="detail-row">
                                <text class="detail-label">录入人</text>
                                <text class="detail-value">{{ item.recorderName }}</text>
                            </view>
                            <view class="detail-row">
                                <text class="detail-label">录入日期</text>
                                <text class="detail-value">{{ item.entryDate }}</text>
                            </view>
                        </view>
                    </view>
                </view>
            </view>
        </view>
        <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>
<script setup>
import { ref } from 'vue';
import { onShow } from '@dcloudio/uni-app';
import useUserStore from "@/store/modules/user";
import PageHeader from "@/components/PageHeader.vue";
import {purchaseListPage} from "@/api/procurementManagement/procurementLedger";
const userStore = useUserStore()
// æœç´¢å…³é”®è¯
const searchKeyword = ref('');
// é‡‡è´­å°è´¦æ•°æ®
const ledgerList = ref([]);
// è¿”回上一页
const goBack = () => {
    uni.navigateBack();
};
// æŸ¥è¯¢åˆ—表
const getList = () => {
    const page = {
        current: -1,
        size: -1
    }
    purchaseListPage({...page}).then((res) => {
        ledgerList.value = res.data.records;
    }).catch(() => {
            // tableLoading.value = false;
    });
};
// å¤„理台账信息操作(查看/编辑/新增)
const handleInfo = (type, row) => {
  try {
    // è®¾ç½®æ“ä½œç±»åž‹
    uni.setStorageSync('operationType', type);
    // å¦‚果是查看或编辑操作
    if (type !== 'add') {
      // éªŒè¯è¡Œæ•°æ®æ˜¯å¦å­˜åœ¨
      if (!row) {
        uni.showToast({
          title: '数据不存在',
          icon: 'error'
        });
        return;
      }
      // æ£€æŸ¥æƒé™ï¼šåªæœ‰å½•入人才能编辑
      if (row.recorderName != userStore.nickName) {
        // éžå½•入人跳转到只读详情页面
        uni.setStorageSync('editData', JSON.stringify(row));
        uni.navigateTo({
          url: '/pages/procurementManagement/procurementLedger/view'
        });
        return;
      }
      // å½•入人编辑:存储数据并跳转到编辑页面
      uni.setStorageSync('editData', JSON.stringify(row));
      uni.navigateTo({
        url: '/pages/procurementManagement/procurementLedger/detail'
      });
      return;
    }
    // æ–°å¢žæ“ä½œï¼šç›´æŽ¥è·³è½¬åˆ°ç¼–辑页面
    uni.navigateTo({
      url: '/pages/procurementManagement/procurementLedger/detail'
    });
  } 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;
}
.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;
}
.no-data {
    padding: 40px 0;
    text-align: center;
    color: #999;
}
.fab-button {
    position: fixed;
    bottom: calc(30px + env(safe-area-inset-bottom));
    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/procurementLedger/view.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,287 @@
<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">{{ form.paymentMethod }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">录入人</text>
          <text class="info-value">{{ form.recorderName }}</text>
        </view>
        <view class="info-item">
          <text class="info-label">录入日期</text>
          <text class="info-value">{{ form.entryDate }}</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.taxInclusiveUnitPrice }}</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.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">{{ product.invoiceType }}</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: '',
  salesContractNo: '',
  customerContractNo: '',
  customerId: '',
  customerName: '',
  projectName: '',
  executionDate: '',
  paymentMethod: '',
  entryPerson: '',
  entryPersonName: '',
  entryDate: '',
  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 = {...res}
  });
};
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;
}
.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/salesAccount/detail.vue
@@ -425,7 +425,8 @@
    modelList({ id: value }).then((res) => {
        modelOptions.value = res.map(user => ({
            text: user.model,
            value: user.id
            value: user.id,
            unit: user.unit,
        }));
    });
};