pension-vue/src/views/shop/productInfo.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>