145 lines
3.4 KiB
Vue
145 lines
3.4 KiB
Vue
<script setup>
|
||
import { ref } from "vue";
|
||
import { onLoad } from "@dcloudio/uni-app";
|
||
import Container from "@/components/Container.vue";
|
||
import CoinHeader from "./components/CoinHeader.vue";
|
||
import ProductHeroSwiper from "./components/ProductHeroSwiper.vue";
|
||
import OfflineExchangeNotice from "./components/OfflineExchangeNotice.vue";
|
||
import { getGiftDetailAPI } from "@/apis";
|
||
|
||
const productDetail = ref({
|
||
name: "",
|
||
coinPrice: 0,
|
||
stock: 0,
|
||
images: [],
|
||
descriptions: [],
|
||
});
|
||
|
||
const loadProductDetail = async (id) => {
|
||
try {
|
||
const result = await getGiftDetailAPI(id);
|
||
const images = Array.isArray(result?.images)
|
||
? result.images
|
||
.slice()
|
||
.sort((first, second) =>
|
||
(Number(first?.sort_order) || 0) - (Number(second?.sort_order) || 0)
|
||
)
|
||
.map((item) => item?.image_url)
|
||
.filter(Boolean)
|
||
: [];
|
||
|
||
productDetail.value = {
|
||
...productDetail.value,
|
||
name: result?.name || "",
|
||
coinPrice: Number(result?.coin_price) || 0,
|
||
stock: Number(result?.stock) || 0,
|
||
images,
|
||
descriptions: result?.description
|
||
? String(result.description).split(/\r?\n/).filter(Boolean)
|
||
: [],
|
||
};
|
||
} catch (error) {
|
||
console.error("加载礼品详情失败", error);
|
||
}
|
||
};
|
||
|
||
onLoad((options = {}) => {
|
||
const id = Number(options.id);
|
||
if (!Number.isInteger(id) || id <= 0) {
|
||
uni.showToast({
|
||
title: "商品参数无效",
|
||
icon: "none",
|
||
});
|
||
return;
|
||
}
|
||
|
||
loadProductDetail(id);
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<Container :bgType="6" :isHome="true">
|
||
<template #header>
|
||
<CoinHeader title="商品详情" />
|
||
</template>
|
||
<view class="product-detail">
|
||
<ProductHeroSwiper
|
||
:images="productDetail.images"
|
||
/>
|
||
<view class="product-detail__summary">
|
||
<text class="product-detail__name">{{ productDetail.name }}</text>
|
||
<view class="product-detail__balance">
|
||
<text>金币:</text>
|
||
<text class="product-detail__amount">{{ productDetail.coinPrice }}</text>
|
||
<text>(剩余{{ productDetail.stock }}个)</text>
|
||
</view>
|
||
</view>
|
||
<OfflineExchangeNotice variant="red" />
|
||
<view class="product-detail__content">
|
||
<text
|
||
v-for="(description, index) in productDetail.descriptions"
|
||
:key="index"
|
||
class="product-detail__paragraph"
|
||
>
|
||
{{ description }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</Container>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.product-detail {
|
||
width: 100%;
|
||
min-height: 100%;
|
||
padding-bottom: 60rpx;
|
||
box-sizing: border-box;
|
||
background-color: #22222e;
|
||
}
|
||
|
||
.product-detail__summary {
|
||
padding: 30rpx 40rpx 20rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.product-detail__name {
|
||
color: #ffd947;
|
||
font-size: 52rpx;
|
||
line-height: 74rpx;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.product-detail__balance {
|
||
display: flex;
|
||
align-items: baseline;
|
||
margin-top: 10rpx;
|
||
color: rgba(255, 255, 255, 0.75);
|
||
font-size: 24rpx;
|
||
line-height: 34rpx;
|
||
}
|
||
|
||
.product-detail__amount {
|
||
margin-right: 6rpx;
|
||
color: #ffffff;
|
||
font-size: 36rpx;
|
||
line-height: 50rpx;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.product-detail__content {
|
||
padding: 38rpx 40rpx 60rpx;
|
||
box-sizing: border-box;
|
||
border-bottom: 2rpx solid rgba(255, 217, 71, 0.05);
|
||
}
|
||
|
||
.product-detail__paragraph {
|
||
display: block;
|
||
margin-bottom: 20rpx;
|
||
color: rgba(255, 255, 255, 0.75);
|
||
font-size: 26rpx;
|
||
line-height: 40rpx;
|
||
text-align: justify;
|
||
}
|
||
</style>
|