topfans/frontend/pages/scan/verify.vue
2026-07-13 11:22:56 +08:00

254 lines
5.8 KiB
Vue

<template>
<view class="verify-page">
<image class="bg-image" src="/static/square/squearbj.png" mode="aspectFill"></image>
<view class="nav-bar">
<view class="nav-back" @tap="goBack"></view>
<text class="nav-title">周边验真</text>
</view>
<view v-if="loading" class="loading">
<text>加载中…</text>
</view>
<view v-else-if="error" class="error">
<text>{{ error }}</text>
<button @tap="loadData">重试</button>
</view>
<view v-else-if="data" class="content">
<view class="header">
<image class="thumb" :src="data.image || '/static/nft/collection.png'" mode="aspectFit" />
<view class="badge"><text>✓ 已通过验真</text></view>
</view>
<view class="info-card">
<view v-for="row in infoRows" :key="row.label" class="info-row">
<text class="info-label">{{ row.label }}</text>
<text class="info-value" :class="{ 'info-hash': row.hash }">{{ row.value || '—' }}</text>
</view>
</view>
<view class="tip-row">
<text class="tip-icon">💡</text>
<text class="tip-text">加入前请对比实物,确认一致后再添加</text>
</view>
<button class="mint-btn" :loading="submitting" :disabled="submitting" @tap="handleAddToCollection">
加入我的藏品
</button>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { getPeripheralByCodeApi, mintToMyCollectionApi } from '@/utils/api.js'
const code = ref('')
const data = ref(null)
const loading = ref(true)
const error = ref('')
const submitting = ref(false)
onLoad((options) => {
// ★ 优先用 code 查(mint 前也能查)
if (options.code) {
code.value = String(options.code)
loadData()
return
}
// 兼容老路径:asset_id
if (options.assetId) {
code.value = String(options.assetId)
loadData()
return
}
error.value = '二维码数据无效'
loading.value = false
})
const infoRows = computed(() => {
if (!data.value) return []
const d = data.value
console.log('infoRows data:', d)
return [
{ label: '周边编号', value: d.code || '—' },
{ label: '品牌', value: d.brand },
{ label: '公司', value: d.company },
{ label: '链上哈希', value: d.hash, hash: true },
{ label: '验证次数', value: `${d.verify_count}` },
{ label: '验证人', value: d.verifier },
{ label: '首次验证', value: formatDate(d.verified_at) },
// 周边素材类型(后端 omitempty,空时 d.material_type 为 undefined)
// { label: '素材类型', value: (d.material_type && d.material_type.trim()) || 'new' }
]
})
function goBack() {
uni.navigateBack({ delta: 1, fail: () => uni.switchTab({ url: '/pages/square/square' }) })
}
async function loadData() {
loading.value = true
error.value = ''
try {
// ★ 用 code 查 peripheral_info(周边未铸时也能查)
const res = await getPeripheralByCodeApi(code.value)
if (!res || !res.data) throw new Error(res?.message || '此物品暂无验真信息')
data.value = res.data
} catch (e) {
error.value = e.message || '加载失败'
} finally {
loading.value = false
}
}
async function handleAddToCollection() {
if (submitting.value) return
submitting.value = true
try {
const res = await mintToMyCollectionApi(code.value)
// console.log('mintToMyCollectionApi res:', res)
uni.showToast({ title: '已加入我的藏品', icon: 'success' })
setTimeout(() => {
uni.navigateTo({
url: `/pages/asset-detail/asset-detail?asset_id=${res.data?.asset_id}`
})
}, 800)
} catch (e) {
uni.showToast({ title: e.message || '添加失败', icon: 'none' })
} finally {
submitting.value = false
}
}
function formatDate(ts) {
if (!ts) return ''
const d = new Date(ts * 1000)
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
}
</script>
<style scoped>
.verify-page {
/* background: #0a0a0a; */
min-height: 100vh;
padding: 24rpx;
}
/* 背景图片 */
.bg-image {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 120%;
z-index: 0;
}
.nav-bar {
display: flex;
align-items: center;
height: 88rpx;
padding: 0 24rpx;
color: #fff;
position: relative;
top: 96rpx;
z-index: 1;
}
.nav-back {
font-size: 40rpx;
padding-right: 24rpx;
position: relative;
z-index: 1;
}
.nav-title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: 600;
}
.loading,
.error {
color: #aaa;
text-align: center;
padding: 80rpx 0;
position: relative;
z-index: 1;
}
.content {
position: relative;
z-index: 1;
}
.header {
/* display: flex;
align-items: center;
justify-content: center; */
margin-top: 64rpx;
}
.info-card {
/* background: #1a1a1a; */
border-radius: 16rpx;
padding: 24rpx;
margin-top: 24rpx;
}
.info-row {
display: flex;
justify-content: space-between;
padding: 16rpx 0;
/* border-bottom: 1rpx solid #2a2a2a; */
}
.info-row:last-child {
border-bottom: none;
}
.info-label {
color: rgba(255, 255, 255, 0.7);
font-size: 26rpx;
}
.info-value {
color: #fff;
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.7);
font-size: 28rpx;
width: 60%;
word-break: break-all;
text-align: left;
}
.info-value.info-hash {
font-family: monospace;
font-size: 22rpx;
color: #3ddc84;
}
.tip-row {
display: flex;
align-items: center;
gap: 8rpx;
padding: 24rpx 8rpx;
color: #888;
font-size: 26rpx;
}
.mint-btn {
background: linear-gradient(135deg, rgba(255, 222, 8, 0.49) 0%, rgba(252, 100, 102, 1) 61.1%, rgba(244, 88, 104, 1) 100%);
box-shadow: inset 0 0.03125rem 0 rgba(255, 255, 255, 0.45), 2px 2px 4px 0px #f2151578;
color: #fff;
border-radius: 100rpx;
margin-top: 32rpx;
font-weight: 600;
}
</style>