Files
yxe-mp/pages/share/detail.vue
T
2026-09-29 15:17:13 +08:00

330 lines
9.9 KiB
Vue

<template>
<view class="share-detail-page">
<view class="page-hero">
<AppNavbar title="分享详情" show-back/>
</view>
<view class="card block">
<view class="section-title">基本信息</view>
<view v-if="loading" class="empty">加载中...</view>
<template v-else>
<view class="info-row">
<text class="info-label">门店</text>
<text class="info-value">{{ detailEntry.merchantName || detailEntry.merchant_name || '未命名门店' }}</text>
</view>
<!-- <view class="info-row">-->
<!-- <text class="info-label">红包</text>-->
<!-- <text class="info-value text-red">¥{{ formatPrice(detailEntry.income || 0) }}</text>-->
<!-- </view>-->
<view class="info-row">
<text class="info-label">提交人</text>
<text class="info-value">{{ detailEntry.owner || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">日期</text>
<text class="info-value">{{ detailEntry.date || detailEntry.activityDateText || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">留言</text>
<text class="info-value">{{ detailEntry.remark || '-' }}</text>
</view>
</template>
</view>
<view class="card block">
<view class="section-title">小票查看</view>
<view v-if="receiptImages.length" class="media-images">
<image
v-for="(receipt, receiptIdx) in receiptImages"
:key="`receipt-${receiptIdx}`"
class="media-image"
:src="receipt"
mode="aspectFill"
@tap="previewReceipt(receiptImages, receiptIdx)"
/>
</view>
<view v-else class="detail-empty">暂无小票</view>
</view>
<view class="card block">
<view class="section-title">商品明细</view>
<view v-if="detailList.length" class="detail-list">
<view class="detail-item" v-for="(detail, idx) in detailList" :key="idx">
<view class="detail-head">
<view class="detail-name">{{ detail.goodsName || detail.goods_name || detail.name || detail.title || '商品' }}</view>
</view>
<view class="detail-grid">
<view class="detail-kv">
<text class="detail-label">数量</text>
<text class="detail-value">x {{ detail.num || detail.qty || 0 }}</text>
</view>
<view class="detail-kv">
<text class="detail-label">厂家</text>
<text class="detail-value">{{ resolveManufacturer(detail) }}</text>
</view>
<view class="detail-kv">
<text class="detail-label">编号</text>
<text class="detail-value">{{ resolveCode(detail) }}</text>
</view>
<view class="detail-kv">
<text class="detail-label">规格</text>
<text class="detail-value">{{ resolveSpec(detail) }}</text>
</view>
<view class="detail-kv">
<text class="detail-label">金额</text>
<text class="detail-value text-red">{{ formatMoney(resolveAmount(detail)) }}</text>
</view>
<!-- <view class="detail-kv">-->
<!-- <text class="detail-label">额外加提</text>-->
<!-- <text class="detail-value text-red">{{ formatMoney(resolveExtraAmount(detail)) }}</text>-->
<!-- </view>-->
</view>
</view>
</view>
<view v-else class="detail-empty">暂无明细</view>
</view>
</view>
</template>
<script>
import AppNavbar from "@/components/AppNavbar.vue";
export default {
components: {AppNavbar},
data() {
return {
loading: true,
detailEntry: {}
}
},
computed: {
detailList() {
const list = this.detailEntry.detailList
return Array.isArray(list) ? list : []
},
receiptImages() {
const list = this.detailEntry.receiptImages
return Array.isArray(list) ? list : []
}
},
onLoad(options) {
this.loadDetail(options || {})
},
methods: {
loadDetail(options = {}) {
const rawKey = options.cacheKey || ''
const cacheKey = rawKey ? decodeURIComponent(rawKey) : ''
let payload = null
if (cacheKey) {
try {
payload = uni.getStorageSync(cacheKey)
uni.removeStorageSync(cacheKey)
} catch (error) {
}
}
if (!payload) {
this.loading = false
uni.showToast({
title: '详情数据不存在',
icon: 'none'
})
return
}
this.detailEntry = this.normalizeShareItem(payload)
console.log(this.detailEntry)
this.loading = false
},
normalizeShareItem(item) {
const detailListRaw = item.detailList || item.list || []
const detailList = this.parseJsonMaybe(detailListRaw)
return {
...item,
detailList: Array.isArray(detailList) ? detailList : [],
receiptImages: this.resolveReceiptImages(item)
}
},
parseJsonMaybe(value) {
if (value === null || value === undefined || value === '') return []
if (typeof value === 'string') {
try {
return JSON.parse(value)
} catch (error) {
return []
}
}
return value
},
resolveReceiptImages(item = {}) {
const candidates = [
item.image,
item.images,
item.receiptPhoto,
item.receiptPhotos,
item.receipt_photo,
item.receipt_photos,
item.receiptImages
]
const urls = []
candidates.forEach(value => {
this.appendReceiptValue(urls, value)
})
return Array.from(new Set(urls.filter(url => /^https?:\/\//.test(url) || /^\/\//.test(url))))
},
appendReceiptValue(target, value) {
if (value === null || value === undefined || value === '') return
if (Array.isArray(value)) {
value.forEach(item => this.appendReceiptValue(target, item))
return
}
if (typeof value === 'object') {
const url = value.url || value.path || value.src || value.image || value.img
if (url) this.appendReceiptValue(target, url)
return
}
const text = String(value).trim()
if (!text) return
if (text.startsWith('[') || text.startsWith('{')) {
try {
const parsed = JSON.parse(text)
this.appendReceiptValue(target, parsed)
return
} catch (error) {
}
}
if (text.includes(',')) {
text.split(',').forEach(part => this.appendReceiptValue(target, part))
return
}
target.push(text)
},
previewReceipt(list = [], current = 0) {
if (!Array.isArray(list) || list.length === 0) return
uni.previewImage({
urls: list,
current: list[current] || list[0]
})
},
resolveManufacturer(detail) {
if (!detail) return '-'
const product = detail.product || {}
const value = detail.makerAbb
|| detail.manufacturer
|| detail.vendor
|| detail.factory
|| detail.brand
|| product.makerAbb
|| product.manufacturer
|| product.vendor
|| product.factory
|| product.brand
return this.normalizeText(value)
},
resolveCode(detail) {
if (!detail) return '-'
const value = detail.goodsCode
|| detail.code
|| detail.productCode
|| detail.skuCode
|| detail.barcode
return this.normalizeText(value)
},
resolveSpec(detail) {
if (!detail) return '-'
const product = detail.product || {}
const value = detail.spec
|| detail.specification
|| detail.standard
|| product.spec
|| product.specification
|| product.standard
return this.normalizeText(value)
},
resolveAmount(detail) {
if (!detail) return ''
if (detail.income !== undefined && detail.income !== null && detail.income !== '') return detail.income
if (detail.product && detail.product.income !== undefined && detail.product.income !== null && detail.product.income !== '') {
return detail.product.income
}
return ''
},
resolveExtraAmount(detail) {
if (!detail) return ''
if (detail.extraIncome !== undefined && detail.extraIncome !== null && detail.extraIncome !== '') return detail.extraIncome
if (detail.product && detail.product.extraIncome !== undefined && detail.product.extraIncome !== null && detail.product.extraIncome !== '') {
return detail.product.extraIncome
}
return ''
},
normalizeText(value) {
if (value === 0) return '0'
if (value === null || value === undefined || value === '') return '-'
return String(value)
},
formatMoney(value) {
if (value === null || value === undefined || value === '') return '-'
const num = Number(value)
if (Number.isFinite(num)) return `¥${num.toFixed(2)}`
return String(value)
},
formatPrice(value) {
const num = Number(value || 0)
return num.toFixed(2)
}
}
}
</script>
<style lang="scss" scoped>
@import '../audit/components/record.scss';
.share-detail-page {
min-height: 100vh;
background: #f5f5f5;
}
.page-hero {
background: url('https://yaodongxiao.oss-cn-guangzhou.aliyuncs.com/20260126/462bfa04cf0eff8698884c38b9d17519.png') no-repeat
top center / 100% auto;
}
.block {
margin: 18rpx 20rpx 0;
}
.info-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 20rpx;
padding: 10rpx 0;
border-bottom: 1rpx solid #edf1f7;
}
.info-row:last-child {
border-bottom: none;
}
.info-label {
color: #6b7280;
font-size: 24rpx;
white-space: nowrap;
}
.info-value {
color: #1f2937;
font-size: 24rpx;
text-align: right;
word-break: break-all;
}
.text-red {
color: #ef4444;
}
.empty {
color: var(--text-sub);
text-align: center;
padding: 16rpx 0;
}
</style>