330 lines
9.9 KiB
Vue
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>
|