gaoluyang
2026-04-27 36c8ae70cae3de90e642b080553abe70d3345c74
src/pages/sales/salesQuotation/detail.vue
@@ -6,142 +6,109 @@
      <view class="section">
        <view class="section-title">基础信息</view>
        <view class="info-list">
          <view class="info-item">
            <text class="info-label">报价单号</text>
            <text class="info-value">{{ detailData.quotationNo || "-" }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">客户名称</text>
            <text class="info-value">{{ detailData.customer || "-" }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">业务员</text>
            <text class="info-value">{{ detailData.salesperson || "-" }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">报价日期</text>
            <text class="info-value">{{ detailData.quotationDate || "-" }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">有效期至</text>
            <text class="info-value">{{ detailData.validDate || "-" }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">付款方式</text>
            <text class="info-value">{{ detailData.paymentMethod || "-" }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">审批状态</text>
            <text class="info-value">{{ detailData.status || "-" }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">报价总额</text>
            <text class="info-value highlight">{{ formatAmount(detailData.totalAmount) }}</text>
          </view>
          <view class="info-item">
            <text class="info-label">备注</text>
            <text class="info-value">{{ detailData.remark || "-" }}</text>
          </view>
        </view>
      </view>
      <view class="section">
        <view class="section-title">审批节点</view>
        <view v-if="approverNames.length" class="info-list">
          <view v-for="(name, index) in approverNames" :key="index" class="info-item">
            <text class="info-label">审批节点 {{ index + 1 }}</text>
            <text class="info-value">{{ name }}</text>
          </view>
        </view>
        <view v-else class="empty-box">
          <text>暂无审批节点</text>
          <view class="info-item"><text class="info-label">报价单号</text><text class="info-value">{{ detailData.quotationNo || "-" }}</text></view>
          <view class="info-item"><text class="info-label">客户</text><text class="info-value">{{ detailData.customer || "-" }}</text></view>
          <view class="info-item"><text class="info-label">业务员</text><text class="info-value">{{ detailData.salesperson || "-" }}</text></view>
          <view class="info-item"><text class="info-label">报价日期</text><text class="info-value">{{ detailData.quotationDate || "-" }}</text></view>
          <view class="info-item"><text class="info-label">有效期至</text><text class="info-value">{{ detailData.validDate || "-" }}</text></view>
          <view class="info-item"><text class="info-label">付款方式</text><text class="info-value">{{ detailData.paymentMethod || "-" }}</text></view>
          <view class="info-item"><text class="info-label">总额</text><text class="info-value highlight">{{ formatAmount(totalAmount) }}</text></view>
          <view class="info-item"><text class="info-label">备注</text><text class="info-value">{{ detailData.remark || "-" }}</text></view>
        </view>
      </view>
      <view class="section">
        <view class="section-title">产品明细</view>
        <view v-if="detailData.products && detailData.products.length > 0" class="product-list">
          <view v-for="(item, index) in detailData.products" :key="index" class="product-card">
        <view v-if="products.length" class="product-list">
          <view v-for="(item, index) in products" :key="index" class="product-card">
            <view class="product-head">产品 {{ index + 1 }}</view>
            <view class="info-item">
              <text class="info-label">产品名称</text>
              <text class="info-value">{{ item.product || item.productName || "-" }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">规格型号</text>
              <text class="info-value">{{ item.specification || "-" }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">单位</text>
              <text class="info-value">{{ item.unit || "-" }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">数量</text>
              <text class="info-value">{{ item.quantity || "-" }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">单价</text>
              <text class="info-value">{{ formatAmount(item.unitPrice) }}</text>
            </view>
            <view class="info-item">
              <text class="info-label">金额</text>
              <text class="info-value highlight">{{ formatAmount(item.amount) }}</text>
            </view>
            <view class="info-item"><text class="info-label">产品</text><text class="info-value">{{ item.product || item.productName || "-" }}</text></view>
            <view class="info-item"><text class="info-label">规格</text><text class="info-value">{{ item.specification || "-" }}</text></view>
            <view class="info-item"><text class="info-label">单位</text><text class="info-value">{{ item.unit || "-" }}</text></view>
            <view class="info-item"><text class="info-label">纸张</text><text class="info-value">{{ item.paper || "-" }}</text></view>
            <view class="info-item"><text class="info-label">定量</text><text class="info-value">{{ item.paperWeight || "-" }}</text></view>
            <view class="info-item"><text class="info-label">数量</text><text class="info-value">{{ Number(item.quantity || 0) }}</text></view>
            <view class="info-item"><text class="info-label">单价</text><text class="info-value">{{ formatAmount(item.unitPrice) }}</text></view>
            <view class="info-item"><text class="info-label">印版费</text><text class="info-value">{{ formatAmount(item.printingFee) }}</text></view>
            <view class="info-item"><text class="info-label">刀版费</text><text class="info-value">{{ formatAmount(item.dieCuttingFee) }}</text></view>
            <view class="info-item"><text class="info-label">磨具费</text><text class="info-value">{{ formatAmount(item.grindingFee) }}</text></view>
            <view class="info-item"><text class="info-label">金额</text><text class="info-value highlight">{{ formatAmount(item.amount) }}</text></view>
          </view>
        </view>
        <view v-else class="empty-box">
          <text>暂无产品明细</text>
        </view>
        <view v-else class="empty-box"><text>暂无产品明细</text></view>
      </view>
    </view>
    <FooterButtons cancelText="返回" confirmText="编辑" @cancel="goBack" @confirm="goEdit" />
    <view class="detail-footer">
      <up-button type="primary" @click="goBack">返回</up-button>
    </view>
  </view>
</template>
<script setup>
  import { computed, ref } from "vue";
  import { onLoad, onShow } from "@dcloudio/uni-app";
  import FooterButtons from "@/components/FooterButtons.vue";
  import PageHeader from "@/components/PageHeader.vue";
  import { getQuotationDetail } from "@/api/salesManagement/salesQuotation";
  const quotationId = ref("");
  const detailData = ref({});
  const approverNames = computed(() => {
    const approverText = detailData.value.approveUserNames || detailData.value.approverNames || detailData.value.approveUserIds || "";
    if (Array.isArray(approverText)) return approverText.filter(Boolean);
    return String(approverText)
      .split(",")
      .map(item => item.trim())
      .filter(Boolean);
  const products = computed(() => {
    const rows = detailData.value?.products;
    if (Array.isArray(rows) && rows.length) return rows;
    if (detailData.value?.product || detailData.value?.productName) return [detailData.value];
    return [];
  });
  const goBack = () => {
    uni.navigateBack();
  };
  const totalAmount = computed(() => {
    const backendTotal = Number(detailData.value?.totalAmount || 0);
    if (backendTotal > 0) return backendTotal;
    return Number(
      products.value
        .reduce((sum, item) => {
          const unitPrice = Number(item?.unitPrice || 0);
          const printingFee = Number(item?.printingFee || 0);
          const dieCuttingFee = Number(item?.dieCuttingFee || 0);
          const grindingFee = Number(item?.grindingFee || 0);
          return sum + unitPrice + printingFee + dieCuttingFee + grindingFee;
        }, 0)
        .toFixed(2)
    );
  });
  const goEdit = () => {
    if (!quotationId.value) return;
    uni.navigateTo({ url: `/pages/sales/salesQuotation/edit?id=${quotationId.value}` });
  };
  const goBack = () => uni.navigateBack();
  const formatAmount = amount => `¥${Number(amount || 0).toFixed(2)}`;
  const loadDetailFromStorage = () => {
    const cachedData = uni.getStorageSync("salesQuotationDetail");
    detailData.value = cachedData || {};
    if (cachedData && typeof cachedData === "object") detailData.value = cachedData;
  };
  const loadDetailFromApi = () => {
    if (!quotationId.value) return Promise.resolve();
    uni.showLoading({ title: "加载中...", mask: true });
    return getQuotationDetail({ id: quotationId.value })
      .then(res => {
        detailData.value = res?.data || detailData.value || {};
      })
      .catch(() => {
        uni.showToast({ title: "加载详情失败", icon: "error" });
      })
      .finally(() => {
        uni.hideLoading();
      });
  };
  onLoad(options => {
    if (options?.id) {
      quotationId.value = options.id;
    }
    if (options?.id) quotationId.value = options.id;
    loadDetailFromStorage();
    loadDetailFromApi();
  });
  onShow(() => {
    loadDetailFromStorage();
    loadDetailFromApi();
  });
</script>
@@ -232,4 +199,15 @@
    color: #22324d;
    border-bottom: 1px solid #eef2f7;
  }
  .detail-footer {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
    background: #fff;
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
    z-index: 10;
  }
</style>