280 lines
9.5 KiB
Vue
280 lines
9.5 KiB
Vue
<template>
|
|
<div class="flex flex-col min-h-screen bg-white pb-safe">
|
|
<div class="flex-auto min-h-0 overflow-y-auto bg-[#f5f5f5]">
|
|
<!-- 商品主图 -->
|
|
<div class="h-[640rpx]">
|
|
<nut-swiper :auto-play="3000" @change="onChange" :is-prevent-default="false">
|
|
<nut-swiper-item v-for="(item, index) in swapperList" :key="index" class="h-full">
|
|
<img :src="item" alt="" class="h-[640rpx] object-cover" draggable="false" @click.stop="showFn" />
|
|
</nut-swiper-item>
|
|
<template #page>
|
|
<div
|
|
class="w-[76rpx] h-[44rpx] bg-[#545255] absolute bottom-[16rpx] right-[16rpx] flex items-center justify-center rounded-full text-[#FDFDFD] text-[28rpx]">
|
|
{{ val }}/{{ swapperList.length }}
|
|
</div>
|
|
</template>
|
|
</nut-swiper>
|
|
</div>
|
|
|
|
<!-- 价格、包邮、分享 -->
|
|
<div class="flex flex-col bg-white rounded-[24rpx] mx-[20rpx] bg-gradient-to-r from-[#FF703F] via-[#FF4417] to-[#ff673b] mt-[20rpx]">
|
|
<div class="flex justify-between">
|
|
<div class="flex items-center space-x-2 pl-[30rpx] pt-[34rpx] mb-[32rpx]">
|
|
<nut-price :price="productInfo?.price" size="large" class="product-price font-700" />
|
|
<div class="w-[90rpx] h-[46rpx] bg-white text-[#F46034] flex items-center justify-center rounded-[10rpx_10rpx_10rpx_0]">包邮</div>
|
|
</div>
|
|
<div class="custom-share ml-[30rpx] flex items-center justify-center">
|
|
<div
|
|
class="w-[154rpx] h-[60rpx] rounded-full border-[#fff] border-solid border-[2rpx] bg-[#ff704b] flex items-center justify-center z-1"
|
|
@click="shareShow = true">
|
|
<img src="/images/product/share.png" alt="share" class="w-[34rpx] h-[29rpx] mr-[8rpx]" />
|
|
<div class="text-[32rpx] text-white">分享</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- 商品标题 -->
|
|
<div class="bg-white rounded-[24rpx] p-[30rpx]">
|
|
<p class="text-[40rpx] font-500">{{ productInfo.title }}</p>
|
|
<p class="text-[30rpx] text-[#666] font-400 mt-[16rpx]">{{ productInfo.subtitle }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-[20rpx] bg-white py-[30rpx] px-[28rpx] mx-[20rpx] mt-[20rpx] mb-[20rpx]">
|
|
<div class="text-[40rpx] font-600 text-center bg-[url(/images/product/curve.png)] bg-no-repeat bg-center-bottom h-[70rpx] custom-bg-size pt-[10rpx]">
|
|
规格参数
|
|
</div>
|
|
<div class="mt-[10rpx]">
|
|
<p class="text-[30rpx] font-600 mb-[18rpx]">商品规格</p>
|
|
<nut-cell-group>
|
|
<nut-cell :title="item.name" :desc="item.value" v-for="item in productSpecifications" :key="item.value">
|
|
<template #title>
|
|
<div class="text-[#666] text-[30rpx] font-400">{{ item.name }}</div>
|
|
</template>
|
|
<template #desc>
|
|
<div class="text-[30rpx] font-400 text-[#333] text-start">{{ item.value }}</div>
|
|
</template>
|
|
</nut-cell>
|
|
</nut-cell-group>
|
|
</div>
|
|
<div class="mt-[10rpx]">
|
|
<p class="text-[30rpx] font-600 mb-[18rpx]">参数</p>
|
|
<nut-cell-group>
|
|
<nut-cell :title="item.name" :desc="item.value" v-for="item in productParams" :key="item.value">
|
|
<template #title>
|
|
<div class="text-[#666] text-[30rpx] font-400">{{ item.name }}</div>
|
|
</template>
|
|
<template #desc>
|
|
<div class="text-[30rpx] font-400 text-[#333] text-start">{{ item.value }}</div>
|
|
</template>
|
|
</nut-cell>
|
|
</nut-cell-group>
|
|
</div>
|
|
</div>
|
|
|
|
<ul class="grid gap-[20rpx] items-center">
|
|
<li v-for="item in productionImgs" :key="item">
|
|
<img :src="item" alt="" class="w-[100%] object-contain" />
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="bg-white py-[16rpx] px-[30rpx] flex items-center justify-between">
|
|
<nut-badge :value="shoppingCount" :max="99">
|
|
<div class="flex flex-col items-center" @click="shoppingCartShow = true">
|
|
<ShoppingCart class="w-[60rpx] h-[60rpx]" />
|
|
<span class="text-[28rpx] font-400">购物车</span>
|
|
</div>
|
|
</nut-badge>
|
|
<div class="w-[272rpx] h-[100rpx] rounded-[20rpx] text-[40rpx] font-600 flex items-center justify-center bg-[#F68E1D] text-white" @click="addGoods">
|
|
加入购物车
|
|
</div>
|
|
<div class="w-[272rpx] h-[100rpx] rounded-[20rpx] text-[40rpx] font-600 flex items-center justify-center bg-[#28BEBB] text-white" @click="handleCheckout">
|
|
立即购买
|
|
</div>
|
|
</div>
|
|
<nut-image-preview :show="state.showPreview" :images="state.imgData" @close="hideFn" />
|
|
|
|
<Share v-model:show="shareShow" />
|
|
|
|
<nut-action-sheet v-model:visible="shoppingCartShow" class="">
|
|
<div class="flex flex-col">
|
|
<div class="relative py-[32rpx] text-center bg-white">
|
|
<div class="text-[36rpx] font-500">购物车</div>
|
|
<div
|
|
class="w-[60rpx] h-[60rpx] bg-[#f5f5f5] rounded-full absolute top-[20rpx] right-[20rpx] flex items-center justify-center"
|
|
@click="shoppingCartShow = false">
|
|
<X class="text-[25rpx]" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex-auto min-h-0 pb-safe flex flex-col">
|
|
<ShoppingCartComponent />
|
|
</div>
|
|
</div>
|
|
</nut-action-sheet>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { getRequest } from "@/api/customFetch";
|
|
import { getProductInfo } from "@/api/interfaceDocument";
|
|
import { useRoute } from "vue-router";
|
|
import { ShoppingCart, X } from "lucide-vue-next";
|
|
import { useShoppingCartStore } from "@/store/shoppingCart";
|
|
import { useRouter } from "vue-router";
|
|
import ShoppingCartComponent from "@/components/shopping-cart/index.vue";
|
|
import { CartTypeEnum } from "@/types/cart";
|
|
import Share from "@/components/shopping-cart/share.vue";
|
|
|
|
const router = useRouter();
|
|
const route = useRoute();
|
|
const swapperList = ref<string[]>([]);
|
|
const val = ref(1);
|
|
const productInfo = ref<any>({});
|
|
|
|
const onChange = (index: number) => {
|
|
val.value = index + 1;
|
|
};
|
|
|
|
const productSpecifications = [
|
|
{ name: "商品编号", value: "1015955073277" },
|
|
{ name: "保质期", value: "3年" },
|
|
{ name: "数量", value: "6包" },
|
|
];
|
|
|
|
const productParams = [
|
|
{ name: "功能", value: "吸水亲肤透气" },
|
|
{ name: "尺码", value: "L" },
|
|
{ name: "国产/进口", value: "国产" },
|
|
];
|
|
|
|
const productionImgs = ref<string[]>([]);
|
|
const useProductInfo = () => {
|
|
const id = route.query.id || 1;
|
|
getRequest(getProductInfo(), { id }).then((resp) => {
|
|
if (resp.code === 200) {
|
|
swapperList.value = (resp.result as { cover: string[] }).cover;
|
|
state.imgData = (resp.result as { cover: string[] }).cover.map((item) => ({ src: item }));
|
|
productionImgs.value = (resp.result as { images: string[] }).images;
|
|
productInfo.value = resp.result;
|
|
}
|
|
});
|
|
};
|
|
useProductInfo();
|
|
|
|
const state = reactive<{ showPreview: boolean; imgData: { src: string }[] }>({
|
|
showPreview: false,
|
|
imgData: [],
|
|
});
|
|
const showFn = () => {
|
|
state.showPreview = true;
|
|
};
|
|
const hideFn = () => {
|
|
state.showPreview = false;
|
|
};
|
|
|
|
const shoppingCartStore = useShoppingCartStore();
|
|
const shoppingCount = ref(shoppingCartStore.getShoppingCartByMall.map((item) => item.count).reduce((a, b) => Number(a) + Number(b), 0));
|
|
const addGoods = () => {
|
|
shoppingCartStore.addShoppingCart({
|
|
type: CartTypeEnum.Mall,
|
|
name: "防摔防滑地垫 卫生间适老化改造专用防摔防滑地垫 卫生",
|
|
price: 120,
|
|
count: 1,
|
|
cover: swapperList.value[0],
|
|
id: 1,
|
|
desc: "600g专用600g专用",
|
|
});
|
|
};
|
|
shoppingCartStore.$subscribe((_mutation, state) => {
|
|
shoppingCount.value = state.shoppingCartList.filter(item => item.type === CartTypeEnum.Mall).map((item) => Number(item.count)).reduce((a, b) => Number(a) + Number(b), 0);
|
|
});
|
|
|
|
const shareShow = ref(false);
|
|
|
|
const handleCheckout = () => {
|
|
router.push({ path: "/checkout", query: { id: route.query.id } });
|
|
};
|
|
|
|
const shoppingCartShow = ref(false);
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
:deep(.product-price) {
|
|
--nut-price-symbol-big-size: 26px;
|
|
--nut-price-big-size: 40px;
|
|
--nut-price-decimal-big-size: 24px;
|
|
--nut-primary-color: #fff;
|
|
.nut-price--symbol {
|
|
margin-right: 5px;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
.custom-bg-size {
|
|
background-size: 82px auto;
|
|
}
|
|
|
|
:deep(.nut-cell-group__wrap) {
|
|
margin: 0;
|
|
box-shadow: unset;
|
|
border: 1px solid #edf0f2;
|
|
|
|
.nut-cell {
|
|
padding: 0;
|
|
}
|
|
.nut-cell::after {
|
|
border-bottom: unset;
|
|
}
|
|
|
|
.nut-cell:not(:last-child) {
|
|
border-bottom: 1px solid #edf0f2;
|
|
}
|
|
|
|
.nut-cell__title {
|
|
width: 30%;
|
|
flex: unset;
|
|
padding: 10px 0 10px 20px;
|
|
border-right: 1px solid #edf0f2;
|
|
background-color: #f5f6f7;
|
|
}
|
|
|
|
.nut-cell__value {
|
|
padding: 10px 20px;
|
|
}
|
|
}
|
|
|
|
.custom-share {
|
|
position: relative;
|
|
width: 120px;
|
|
|
|
&::after {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
border-radius: 0 50px 0 0;
|
|
background: #ff673b;
|
|
transform: skewX(-15deg);
|
|
}
|
|
&::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
width: 100px;
|
|
height: 100%;
|
|
border-radius: 0 12px 0 0;
|
|
background: #ff673b;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<style>
|
|
.share-action + .nut-popup {
|
|
overflow: unset;
|
|
}
|
|
</style>
|