gaoluyang
2026-04-27 36c8ae70cae3de90e642b080553abe70d3345c74
src/pages/sales/salesQuotation/index.vue
@@ -7,25 +7,16 @@
        <view class="search-input">
          <up-input
            class="search-text"
            v-model="quotationNo"
            placeholder="请输入报价单号搜索"
            v-model="searchForm.product"
            placeholder="请输入产品名称搜索"
            clearable
            @change="getList"
            @change="getList(true)"
          />
        </view>
        <view class="filter-button" @click="getList">
        <view class="filter-button" @click="getList(true)">
          <up-icon name="search" size="24" color="#999"></up-icon>
        </view>
      </view>
    </view>
    <view class="tabs-section">
      <up-tabs
        v-model="tabValue"
        :list="tabList"
        itemStyle="width: 20%;height: 80rpx;"
        @change="onTabChange"
      />
    </view>
    <view v-if="quotationList.length > 0" class="ledger-list">
@@ -35,37 +26,44 @@
            <view class="document-icon">
              <up-icon name="file-text" size="16" color="#ffffff"></up-icon>
            </view>
            <text class="item-id">{{ item.quotationNo || "-" }}</text>
            <text class="item-id">{{ item.product || "-" }}</text>
          </view>
          <text class="item-index">{{ item.status || "-" }}</text>
        </view>
        <up-divider></up-divider>
        <view class="item-details">
          <view class="detail-row">
            <text class="detail-label">客户名称</text>
            <text class="detail-value">{{ item.customer || "-" }}</text>
            <text class="detail-label">规格</text>
            <text class="detail-value">{{ item.specification || "-" }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">业务员</text>
            <text class="detail-value">{{ item.salesperson || "-" }}</text>
            <text class="detail-label">纸张</text>
            <text class="detail-value">{{ item.paper || "-" }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">报价日期</text>
            <text class="detail-value">{{ item.quotationDate || "-" }}</text>
            <text class="detail-label">定量</text>
            <text class="detail-value">{{ item.paperWeight || "-" }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">有效期至</text>
            <text class="detail-value">{{ item.validDate || "-" }}</text>
            <text class="detail-label">单价</text>
            <text class="detail-value">{{ formatAmount(item.unitPrice) }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">付款方式</text>
            <text class="detail-value">{{ item.paymentMethod || "-" }}</text>
            <text class="detail-label">印版费</text>
            <text class="detail-value">{{ formatAmount(item.printingFee) }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">报价金额</text>
            <text class="detail-value highlight">{{ formatAmount(item.totalAmount) }}</text>
            <text class="detail-label">刀版费</text>
            <text class="detail-value">{{ formatAmount(item.dieCuttingFee) }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">磨具费</text>
            <text class="detail-value">{{ formatAmount(item.grindingFee) }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">数量</text>
            <text class="detail-value">{{ Number(item.quantity || 0) }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">备注</text>
@@ -74,25 +72,15 @@
        </view>
        <view class="action-buttons">
               <up-button
                  class="action-btn"
              size="small"
              type="primary"
              :disabled="!canEdit(item)"
              @click="goEdit(item)"
               >
                  编辑
               </up-button>
          <up-button class="action-btn" size="small" type="primary" @click="goEdit(item)">编辑</up-button>
          <up-button class="action-btn" size="small" @click="goDetail(item)">详情</up-button>
          <up-button class="action-btn" size="small" type="error" plain @click="handleDelete(item)">
            删除
          </up-button>
          <up-button class="action-btn" size="small" type="error" plain @click="handleDelete(item)">删除</up-button>
        </view>
      </view>
    </view>
    <view v-else class="no-data">
      <text>暂无销售报价数据</text>
      <text>暂无数据</text>
    </view>
    <view class="fab-button" @click="goAdd">
@@ -105,70 +93,73 @@
  import { reactive, ref } from "vue";
  import { onShow } from "@dcloudio/uni-app";
  import PageHeader from "@/components/PageHeader.vue";
  import { deleteQuotation, getQuotationList } from "@/api/salesManagement/salesQuotation";
  import { deleteQuotation } from "@/api/salesManagement/salesQuotation";
  import { quotationProductListPage } from "@/api/salesManagement/salesQuotationProduct";
  const quotationNo = ref("");
  const searchForm = reactive({ product: "" });
  const quotationList = ref([]);
  const tabList = reactive([
    { name: "全部", value: "" },
    { name: "待审批", value: "待审批" },
    { name: "审核中", value: "审核中" },
    { name: "通过", value: "通过" },
    { name: "拒绝", value: "拒绝" },
  ]);
  const tabValue = ref(0);
  const page = {
    current: -1,
    size: -1,
  };
  const goBack = () => {
    uni.navigateBack();
  };
  const goAdd = () => {
    uni.navigateTo({ url: "/pages/sales/salesQuotation/edit" });
  };
  const goBack = () => uni.navigateBack();
  const goAdd = () => uni.navigateTo({ url: "/pages/sales/salesQuotation/edit" });
  const goEdit = item => {
    if (!canEdit(item)) return;
    const source = item?.__raw || item || {};
    uni.setStorageSync("salesQuotationEdit", source);
    uni.navigateTo({ url: `/pages/sales/salesQuotation/edit?id=${item.id}` });
  };
  const goDetail = item => {
    uni.setStorageSync("salesQuotationDetail", item || {});
    uni.navigateTo({ url: `/pages/sales/salesQuotation/detail?id=${item.id}` });
  };
  const canEdit = item => ["待审批", "拒绝"].includes(item?.status);
  const formatAmount = amount => `¥${Number(amount || 0).toFixed(2)}`;
  const onTabChange = val => {
    tabValue.value = val.index;
    getList();
  };
  const calcTotalAmountFromProducts = products =>
    Number(
      (products || [])
        .reduce((sum, product) => {
          const unitPrice = Number(product?.unitPrice || 0);
          const printingFee = Number(product?.printingFee || 0);
          const dieCuttingFee = Number(product?.dieCuttingFee || 0);
          const grindingFee = Number(product?.grindingFee || 0);
          return sum + unitPrice + printingFee + dieCuttingFee + grindingFee;
        }, 0)
        .toFixed(2)
    );
  const getCurrentStatus = () => {
    const currentTab = tabList[tabValue.value];
    return currentTab?.value || "";
  };
  const formatAmount = amount => {
    const num = Number(amount || 0);
    return `¥${num.toFixed(2)}`;
  const normalizeQuotation = row => {
    const sourceProducts = Array.isArray(row?.products) && row.products.length ? row.products : [row];
    const first = sourceProducts[0] || {};
    return {
      ...row,
      __raw: row,
      customer: row?.customer || row?.customerName || first?.customer || first?.customerName || "",
      salesperson: row?.salesperson || row?.salesman || row?.salesPerson || first?.salesperson || "",
      quotationDate: row?.quotationDate || row?.quoteDate || first?.quotationDate || "",
      validDate: row?.validDate || row?.expireDate || first?.validDate || "",
      paymentMethod: row?.paymentMethod || row?.paymentType || first?.paymentMethod || "",
      product: first.product || first.productName || row?.product || "",
      specification: first.specification || row?.specification || "",
      paper: first.paper || row?.paper || "",
      paperWeight: first.paperWeight || row?.paperWeight || "",
      unitPrice: Number(first.unitPrice || row?.unitPrice || 0),
      printingFee: Number(first.printingFee || row?.printingFee || 0),
      dieCuttingFee: Number(first.dieCuttingFee || row?.dieCuttingFee || 0),
      grindingFee: Number(first.grindingFee || row?.grindingFee || 0),
      quantity: Number(first.quantity || row?.quantity || 0),
      quotationNo: row?.quotationNo || first?.quotationNo || "",
      totalAmount: Number(row?.totalAmount || calcTotalAmountFromProducts(sourceProducts)),
    };
  };
  const getList = () => {
    uni.showLoading({ title: "加载中...", mask: true });
    getQuotationList({
      ...page,
      quotationNo: quotationNo.value,
      status: getCurrentStatus(),
    quotationProductListPage({
      current: -1,
      size: -1,
      product: String(searchForm.product || "").trim(),
    })
      .then(res => {
        const records = res?.data?.records || res?.records || [];
        quotationList.value = Array.isArray(records) ? records : [];
        quotationList.value = Array.isArray(records) ? records.map(normalizeQuotation) : [];
      })
      .catch(() => {
        uni.showToast({ title: "查询失败", icon: "error" });
@@ -182,11 +173,11 @@
    if (!item?.id) return;
    uni.showModal({
      title: "删除确认",
      content: "确认删除该报价单吗?",
      content: "确认删除该报价吗?",
      success: res => {
        if (!res.confirm) return;
        uni.showLoading({ title: "处理中...", mask: true });
        deleteQuotation(item.id)
        deleteQuotation([item.id])
          .then(() => {
            uni.showToast({ title: "删除成功", icon: "success" });
            getList();
@@ -208,11 +199,6 @@
<style scoped lang="scss">
  @import "@/styles/sales-common.scss";
  .tabs-section {
    background: #ffffff;
    padding: 0 12px 8px 12px;
  }
  .item-index {
    max-width: 180rpx;