321 lines
6.4 KiB
Vue
321 lines
6.4 KiB
Vue
<template>
|
|
<view class="app">
|
|
<view class="head-wrapper" :style="{ top: BarHeight + 'px' }">
|
|
<view class="head-menu">
|
|
<Left class="iconfont" @click="returns" />
|
|
<Home class="iconfont" @click="goHome" />
|
|
</view>
|
|
</view>
|
|
<!-- 幻灯片 -->
|
|
<nut-swiper
|
|
:init-page="0"
|
|
:pagination-visible="true"
|
|
pagination-color="#426543"
|
|
auto-play="3000"
|
|
>
|
|
<nut-swiper-item v-for="(itm, idx) in swiperList" :key="idx">
|
|
<img :alt="idx.toString()" :src="itm" />
|
|
</nut-swiper-item>
|
|
</nut-swiper>
|
|
<!-- 标题价格 -->
|
|
<view class="card">
|
|
<view class="header">
|
|
<view class="price">
|
|
<nut-price
|
|
size="normal"
|
|
:price="goodInfo.number as number"
|
|
position="after"
|
|
symbol="元"
|
|
/>
|
|
<nut-price
|
|
size="normal"
|
|
:price="goodInfo.exchange as number"
|
|
position="after"
|
|
symbol="积分"
|
|
/>
|
|
</view>
|
|
|
|
<view class="stock"> 库存剩余:{{ goodInfo.stock }}</view>
|
|
</view>
|
|
<view class="title">{{ goodInfo.name }}</view>
|
|
<view class="sub">{{ goodInfo.profile }}</view>
|
|
<nut-button size="small" type="primary" @click.stop="add_cart(goodInfo)">
|
|
<template #icon>
|
|
<Cart2 />
|
|
</template>
|
|
</nut-button>
|
|
</view>
|
|
<!-- 产品介绍 -->
|
|
<view class="rich-box">
|
|
<view class="title">产品介绍</view>
|
|
<rich-text
|
|
v-if="goodInfo.details"
|
|
class="rich"
|
|
:nodes="goodInfo.details"
|
|
></rich-text>
|
|
<nut-empty v-else description="暂无产品介绍"></nut-empty>
|
|
</view>
|
|
<view style="height: 9vh"></view>
|
|
<!-- 底部 -->
|
|
<!-- 购物车 -->
|
|
<cart ref="cartRef" :mer-info="mer_info" @update-cart="updateCartNum" />
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { Ref, ref } from "vue";
|
|
import Taro from "@tarojs/taro";
|
|
import { Home, Left } from "@nutui/icons-vue-taro";
|
|
import { getActiveGoodsDetail } from "@/api/goods";
|
|
import Cart from "@/components/Cart.vue";
|
|
import { Cart2 } from "@nutui/icons-vue-taro";
|
|
import { nextTick } from "vue";
|
|
|
|
const statusBarHeight = Taro.getSystemInfoSync()?.statusBarHeight;
|
|
const BarHeight = ref((statusBarHeight as number) + 7);
|
|
|
|
const swiperList = ref([]);
|
|
|
|
interface GoodInfo {
|
|
gid?: string;
|
|
name?: string;
|
|
number?: number;
|
|
exchange?: number;
|
|
cover?: string;
|
|
details?: string;
|
|
sku?: any[];
|
|
stock?: number;
|
|
profile?: string;
|
|
price?: number;
|
|
cartNum?: number;
|
|
}
|
|
|
|
const goodInfo = ref<GoodInfo>({});
|
|
|
|
Taro.useLoad(async (options) => {
|
|
await get_good_detail(options.gid);
|
|
mer_info.value = Taro.getStorageSync("mer_info");
|
|
await nextTick(async () => {
|
|
await cartRef.value.get_cart_list();
|
|
});
|
|
});
|
|
|
|
const get_good_detail = async (gid: string) => {
|
|
try {
|
|
const res = await getActiveGoodsDetail({ gid: gid });
|
|
goodInfo.value = {
|
|
...res.data.data,
|
|
// details: res.data.data.details.,
|
|
};
|
|
swiperList.value = res.data.data.rotation.split(",");
|
|
} catch (e) {
|
|
Taro.showToast({
|
|
title: e.msg,
|
|
icon: "none",
|
|
});
|
|
}
|
|
};
|
|
|
|
const returns = () => {
|
|
Taro.navigateBack({
|
|
delta: 1,
|
|
});
|
|
};
|
|
|
|
const goHome = () => {
|
|
Taro.switchTab({
|
|
url: "/pages/index/index",
|
|
});
|
|
};
|
|
|
|
interface CartItems {
|
|
bid: string;
|
|
gid: string;
|
|
number: number;
|
|
price: number;
|
|
cover: string;
|
|
name: string;
|
|
exchange: number;
|
|
}
|
|
|
|
const mer_info = ref<any>({});
|
|
|
|
const cartRef = ref(null) as Ref;
|
|
|
|
const add_cart = async (item: any) => {
|
|
const num = item.cartNum ? Number(item.cartNum) + 1 : 1;
|
|
await nextTick(async () => {
|
|
await cartRef.value.add_cart(item, num);
|
|
});
|
|
};
|
|
|
|
const updateCartNum = (cartItems: CartItems[]) => {
|
|
if (cartItems.length > 0) {
|
|
cartItems.forEach((cartItem: CartItems) => {
|
|
if (goodInfo.value.gid === cartItem.gid) {
|
|
goodInfo.value.cartNum = cartItem.number;
|
|
}
|
|
});
|
|
} else {
|
|
Reflect.deleteProperty(goodInfo.value, "cartNum");
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
page {
|
|
--nut-cell-box-shadow: none;
|
|
--nut-cell-title-font: 30px;
|
|
}
|
|
|
|
// sku遮罩
|
|
.overlay {
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
z-index: 1 !important;
|
|
}
|
|
|
|
.head-wrapper {
|
|
z-index: 999;
|
|
display: flex;
|
|
align-items: center;
|
|
position: fixed;
|
|
left: 30px;
|
|
top: 0;
|
|
height: 114px;
|
|
}
|
|
|
|
.head-menu {
|
|
display: flex;
|
|
align-items: center;
|
|
height: 54px;
|
|
width: 140px;
|
|
background: rgba(0, 0, 0, 0.25);
|
|
border-radius: 27px;
|
|
|
|
.iconfont {
|
|
flex: 1;
|
|
text-align: center;
|
|
color: #fff;
|
|
box-sizing: border-box;
|
|
}
|
|
}
|
|
|
|
.nut-swiper-item img {
|
|
width: 100%;
|
|
height: 730px;
|
|
}
|
|
|
|
.card {
|
|
padding: 30px;
|
|
background-color: #fff;
|
|
position: relative;
|
|
|
|
.header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
|
|
.price {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.title {
|
|
font-size: 30px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.stock {
|
|
color: #8f8f8f;
|
|
}
|
|
}
|
|
|
|
.sub {
|
|
font-size: 24px;
|
|
color: #999;
|
|
}
|
|
|
|
.nut-button {
|
|
// margin: 10px;
|
|
position: absolute;
|
|
right: 40rpx;
|
|
bottom: 20rpx;
|
|
}
|
|
}
|
|
|
|
.rich-box {
|
|
background-color: #fff;
|
|
margin-top: 20px;
|
|
width: 100vw;
|
|
box-sizing: border-box;
|
|
text-align: center;
|
|
padding: 15px 0;
|
|
|
|
.title {
|
|
font-size: 30px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.rich {
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
.nut-sku {
|
|
// 适配ios底部安全区域
|
|
padding-bottom: constant(safe-area-inset-bottom);
|
|
padding-bottom: env(safe-area-inset-bottom);
|
|
}
|
|
|
|
.sku-box {
|
|
padding: 100px 50px;
|
|
}
|
|
|
|
.bottom-box {
|
|
border-top: 1px solid #e5e5e585;
|
|
position: fixed;
|
|
box-sizing: border-box;
|
|
bottom: 0;
|
|
height: 150rpx;
|
|
background: #fff;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
// 适配ios底部安全区域
|
|
padding-bottom: constant(safe-area-inset-bottom);
|
|
padding-bottom: env(safe-area-inset-bottom);
|
|
z-index: 999;
|
|
width: 100%;
|
|
|
|
.left {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-start;
|
|
width: 300px;
|
|
padding: 0 20px;
|
|
box-sizing: border-box;
|
|
|
|
.icon {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #333;
|
|
font-size: 25px;
|
|
width: 100px;
|
|
}
|
|
}
|
|
|
|
.icon {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: #333;
|
|
font-size: 25px;
|
|
}
|
|
}
|
|
</style>
|