| | |
| | | <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> |
| | | |
| | |
| | | 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> |