2025-10-14 17:46:23 +08:00

193 lines
6.0 KiB
Vue

<template>
<view class="exchange-page">
<uni-nav-bar left-icon="left" title="在线兑换" fixed color="#8B2316" height="180rpx" :border="false" backgroundColor="#ffffff" @clickLeft="goBack" />
<scroll-view scroll-y class="content">
<!-- 收货地址 -->
<view class="addr-card" @click="changeAddress">
<view class="addr-header">
<text class="addr-title">收货信息</text>
<text class="addr-change">{{ selectedAddress ? '更换' : '添加' }}</text>
</view>
<view v-if="selectedAddress" class="addr-info">
<text class="addr-row">{{ selectedAddress.receiver }} {{ selectedAddress.mobile }}</text>
<text class="addr-row small">{{ selectedAddress.fullAddress || (selectedAddress.region + ' ' + selectedAddress.detail) }}</text>
</view>
<view v-else class="addr-empty">请添加收货地址</view>
</view>
<view class="goods-title">{{ title }}</view>
<view class="price">{{ price }}积分</view>
<view class="divider"></view>
<view class="section">
<view class="section-label">兑换数量</view>
<view class="qty-row">
<view class="qty-btn" :class="{ active: qty === 1 }" @click="setQty(1)">1</view>
<view class="qty-btn" :class="{ active: qty === 2 }" @click="setQty(2)">2</view>
<view class="qty-btn" :class="{ active: qty === 3 }" @click="setQty(3)">3</view>
</view>
</view>
<view class="section">
<view class="section-label">所需积分</view>
<view class="need-points">{{ needPoints }}积分</view>
</view>
</scroll-view>
<view class="footer-bar">
<view class="confirm-btn" @click="goAddress">我要兑换</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad, onShow } from '@dcloudio/uni-app'
import goods_api from '@/api/goods_api'
const title = ref('')
const price = ref(0)
const qty = ref(1)
const needPoints = ref(0)
const totalPoints = ref(0)
const selectedAddress = ref(null)
const id = ref('')
const goBack = () => uni.navigateBack()
const setQty = (n) => {
qty.value = n
needPoints.value = n * Number(price.value || 0)
}
onLoad((opts) => {
if (opts) {
title.value = decodeURIComponent(opts.title || '')
price.value = Number(opts.price || 0)
id.value = opts.id || ''
setQty(1)
getTotalPoints()
}
})
onShow(() => {
loadAddress()
})
const STORAGE_KEY = 'goods_addresses'
const SELECTED_KEY = 'goods_selected_address'
const loadAddress = () => {
try {
const sel = uni.getStorageSync(SELECTED_KEY)
if (sel && sel.id) {
selectedAddress.value = sel
return
}
const list = uni.getStorageSync(STORAGE_KEY)
if (Array.isArray(list) && list.length) {
selectedAddress.value = list[0]
} else {
selectedAddress.value = null
}
} catch (e) {
selectedAddress.value = null
}
}
const changeAddress = () => {
let list = []
try {
const cached = uni.getStorageSync(STORAGE_KEY)
list = Array.isArray(cached) ? cached : []
} catch (e) { list = [] }
if (!list.length) {
uni.navigateTo({ url: '/pages_goods/exchange/address' })
return
}
uni.navigateTo({ url: '/pages_goods/exchange/address_list' })
}
const goAddress = () => {
if (needPoints.value > totalPoints.value) {
uni.showToast({ title: '积分不足', icon: 'none' })
return
}
if (!selectedAddress.value) {
changeAddress()
return
}
const data = {
goodsUuid: id.value,
address: selectedAddress.value.fullAddress,
user_name: selectedAddress.value.receiver,
mobile: selectedAddress.value.mobile,
goodsNum: qty.value,
email: selectedAddress.value.email
}
console.log(data)
//{"goodsUuid":"e6ca84edc8e64242a379dd0b895ea812",
// "address":"北京市东城区你一下",
// "user_name":"噢噢",
// "mobile":"13825244552",
// "goodsNum":"1",
// "email":""}
// 模拟下单:此处可调用后端接口完成兑换
uni.showModal({
title: '确认兑换',
content: `兑换“${title.value}”x${qty.value},共需 ${needPoints.value} 积分`,
success: (r) => {
if (r.confirm) {
// 扣减本地积分(演示)
totalPoints.value = Math.max(0, totalPoints.value - needPoints.value)
uni.removeStorageSync(SELECTED_KEY)
goods_api.createGoodsOrder(data).then(res => {
console.log(res)
if (res.code == 200) {
uni.showToast({ title: '兑换成功', icon: 'none' })
uni.navigateBack()
} else {
uni.showToast({ title: res.msg, icon: 'none' })
}
})
}
}
})
}
const getTotalPoints = () => {
goods_api.getTotalPoints && goods_api.getTotalPoints().then(res => {
if (res.code == 200) {
totalPoints.value = res.data || 0
}
}).catch(() => {})
}
</script>
<style scoped>
.exchange-page { min-height: 100vh; background: #fff; }
.content { position: absolute; top: 180rpx; bottom: 120rpx; left: 0; right: 0; background: #fff; }
.addr-card { background: #fff; padding: 24rpx; border-bottom: 1rpx solid #f0f0f0; }
.addr-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12rpx; }
.addr-title { color: #333; font-size: 30rpx; }
.addr-change { color: #38c1b1; font-size: 28rpx; }
.addr-info { display: flex; flex-direction: column; gap: 8rpx; }
.addr-row { color: #333; font-size: 28rpx; }
.addr-row.small { color: #666; font-size: 26rpx; }
.addr-empty { color: #bbb; font-size: 28rpx; }
.goods-title { padding: 24rpx; font-size: 32rpx; color: #333; line-height: 1.6; }
.price { padding: 0 24rpx 24rpx; color: #e74c3c; font-size: 30rpx; font-weight: 700; }
.divider { height: 16rpx; background: #f5f5f5; }
.section { padding: 24rpx; }
.section-label { color: #333; font-size: 30rpx; margin-bottom: 20rpx; }
.qty-row { display: flex; gap: 24rpx; }
.qty-btn { flex: 1; text-align: center; height: 96rpx; line-height: 96rpx; border: 2rpx solid #38c1b1; color: #38c1b1; border-radius: 12rpx; font-size: 36rpx; }
.qty-btn.active { background: #38c1b1; color: #fff; }
.need-points { color: #333; font-size: 30rpx; text-align: right; }
.footer-bar { position: fixed; left: 0; right: 0; bottom: 0; height: 120rpx; background: #27c5b8; display: flex; align-items: center; justify-content: center; }
.confirm-btn { color: #fff; font-size: 34rpx; font-weight: 700; }
</style>