You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1089 lines
28 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="wrap">
<!-- <view class="back-icon" :style="{ top: tabTop }" @click="$u.throttle(back, 500)">-->
<!-- <u-icon name="arrow-left" :size="44"></u-icon>-->
<!-- </view>-->
<view style="height: 763rpx"></view>
<u-swiper
style="position: fixed; top: 0; left: 0; right: 0"
mode="none"
:height="763"
:list="swiper"
@change="(e) => (swiperIndex = e)"
></u-swiper>
<view class="content">
<view class="swiper-num">
{{ swiperIndex + 1 }} / {{ swiper.length }}
</view>
<view class="topbar">
<text>便宜又放心</text>
</view>
<view class="container">
<view class="price">
<view>
<text>¥</text>
<text style="font-size: 32rpx; padding-left: 6rpx">{{
productSku ? productSku.price : detail.price
}}</text>
</view>
</view>
<view class="title">
{{ detail.name }}
<text v-if="productSku">{{ productSku.name }}</text>
</view>
<view class="switch-type">
<scroll-view :scroll-x="true">
<view class="switch-type-scroll">
<view
class="switch-type-item"
:class="{
'switch-type-item-active': form.product_sku_id === tag.id,
}"
:style="{
'background': !tag.stock_num ? '#aaa' : ''
}"
v-for="tag in detail.product_skus"
:key="tag.id"
@tap="() => {
if (tag.stock_num) {
form.product_sku_id = tag.id
}
}"
>
{{ tag.name }}
</view>
</view>
</scroll-view>
<u-icon
name="arrow-right"
:size="34"
color="#333"
@tap="showType = true"
></u-icon>
</view>
<view class="goods">
<view class="goods-row">
<view class="goods-row__icon">
<u-icon name="car" :size="30" color="#333"></u-icon>
</view>
<view class="goods-row__text"> 多仓发货 </view>
</view>
<view class="goods-row">
<view class="goods-row__icon">
<u-icon name="heart" :size="30" color="#333"></u-icon>
</view>
<view class="goods-row__text"> 买贵必赔|退货宝|假一赔十 </view>
</view>
<view class="goods-row">
<view class="goods-row__icon">
<u-icon name="tags" :size="30" color="#333"></u-icon>
</view>
<view class="goods-row__text"> 品牌|型号 </view>
</view>
</view>
<view class="split"></view>
<view class="tabs" :style="'top:' + tabTop + 'px'">
<view
class="search"
:style="'padding-right:' + menuButtonLeft + 'px'"
>
<u-icon
style="padding: 0 20rpx"
name="arrow-left"
:size="36"
color="#333"
@tap="$u.route({type:'back'})"
></u-icon>
<view
class="flex1"
@click="$u.route({ url: '/package_sub/pages/Shop/Search' })"
>
<u-search
:show-action="false"
shape="round"
placeholder="请输入搜索内容"
></u-search>
</view>
<button class="clear-btn" style="padding: 0 14rpx;" open-type="share">
<u-icon
name="share"
:size="36"
color="#333"
></u-icon>
</button>
<button class="clear-btn" style="padding: 0 14rpx;" @click="$u.route({ url: '/package_sub/pages/Shop/Cart' })">
<u-icon
name="shopping-cart"
:size="36"
color="#333"
></u-icon>
</button>
</view>
<view
class="tab"
:class="{ 'tab-active': index === currentTab }"
v-for="(tab, index) in tabs"
:key="tab.text"
@click="tabClick(index)"
>
{{ tab.text }}
</view>
</view>
<view class="comment">
<view
class="panel-title"
@tap="
$u.route({
url: '/package_sub/pages/Shop/Reply',
})
"
>
<text class="flex1">评论</text>
<u-icon name="arrow-right" :size="28" color="#333"></u-icon>
</view>
<view
class="comment-container"
v-for="(res, index) in commentList"
:key="res.id"
>
<view class="left"
><image :src="res.url" mode="aspectFill"></image
></view>
<view class="right">
<view class="top">
<view class="name">{{ res.name }}</view>
<view class="like" :class="{ highlight: res.isLike }">
<view class="num">{{ res.likeNum }}</view>
<u-icon
v-if="!res.isLike"
name="thumb-up"
:size="30"
color="#9a9a9a"
></u-icon>
<u-icon
v-if="res.isLike"
name="thumb-up-fill"
:size="30"
></u-icon>
</view>
</view>
<view class="right-content">{{ res.contentText }}</view>
<view class="bottom">
{{ res.date }}
</view>
</view>
</view>
</view>
<view class="detail">
<view class="panel-title"> 宝贝详情 </view>
<view>
<u-parse :html="detail.content" lazy-load />
</view>
</view>
<view class="recommend">
<view class="panel-title"> 推荐商品 </view>
<view class="recommend-wrap">
<view
class="product-item"
v-for="item in recommendPro"
:key="item.id"
@tap="$u.throttle(() => {toDetail(item)}, 500)"
>
<view class="top">
<image
class="product-item__img"
:src="item.image ? item.image.url : ''"
mode="aspectFill"
></image>
</view>
<view class="bottom">
<view class="product-item__title">
{{ item.name }}
</view>
<view class="product-item__price">
<view>
<text>¥</text>
<text>{{ item.price }}</text>
<text>起</text>
</view>
<!-- <u-button-->
<!-- :hair-line="false"-->
<!-- type="warning"-->
<!-- ripple-->
<!-- shape="circle"-->
<!-- :custom-style="{-->
<!-- height: '28rpx',-->
<!-- 'line-height': '28rpx',-->
<!-- padding: '0 6rpx',-->
<!-- color: '#fff',-->
<!-- 'font-size': '17rpx',-->
<!-- }"-->
<!-- >加入购物车</u-button-->
<!-- >-->
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<u-back-top :scroll-top="scrollTop" :top="400"></u-back-top>
<!-- 类型选择 -->
<u-popup
v-model="showType"
mode="bottom"
border-radius="14"
safe-area-inset-bottom
@close="(isSkuSubmit = false), (isImmediateBuy = false)"
>
<view class="content">
<scroll-view scroll-y="true" style="height: 800rpx">
<view class="product">
<view
class="product-item"
:class="{
'product-item-active': form.product_sku_id === item.id,
}"
v-for="item in detail.product_skus || []"
:key="item.id"
@click="() => {
if (item.stock_num) {
form.product_sku_id = item.id
}
}"
>
<view class="top">
<image
class="product-item__img"
:src="item.image ? item.image.url : ''"
mode="aspectFill"
></image>
<image class="product-item__soldout" v-if="!item.stock_num" src="~@/package_sub/static/Shop/yishouxing.png" mode="aspectFit"></image>
</view>
<view class="bottom">
<view class="product-item__title">
{{ item.name }}
</view>
<view class="product-item__price">
<view>
<text>¥</text>
<text>{{ item.price }}</text>
</view>
</view>
</view>
<u-icon
class="product-item-active__check"
name="checkmark-circle-fill"
color="red"
:size="40"
v-if="form.product_sku_id === item.id"
></u-icon>
</view>
</view>
</scroll-view>
<view class="sku-num" style="padding-left: 10%;padding-top: 8rpx;" v-if="isSkuSubmit">
<text>数量:</text>
<u-number-box v-model="form.num" :min="1" :max="productSku.stock_num"></u-number-box>
</view>
<view class="confirm-btn">
<u-button
:hair-line="false"
:custom-style="{
'background-image':
'linear-gradient(-90deg, #e26165 0%, #c10d12 94%, #c10d12 100%)',
color: '#fff',
width: '80%',
'margin-top': '20rpx',
}"
@click="confirm"
>确定</u-button
>
</view>
</view>
</u-popup>
<!-- -->
<view class="navigation">
<view class="left">
<view class="item">
<u-icon name="server-fill" :size="40" color="#333"></u-icon>
<view class="text u-line-1">客服</view>
</view>
<view class="item car" @tap="$u.route({ url: '/package_sub/pages/Shop/Cart' })">
<u-badge
class="car-num"
:count="cartNum"
type="error"
:offset="[-3, -6]"
></u-badge>
<u-icon name="shopping-cart" :size="40" color="#333"></u-icon>
<view class="text u-line-1">购物车</view>
</view>
</view>
<view class="right">
<view
class="cart btn u-line-1"
@click="(isSkuSubmit = true), (showType = true)"
>加入购物车</view
>
<view
class="buy btn u-line-1"
@click="
(isImmediateBuy = true), (isSkuSubmit = true), (showType = true)
"
>立即购买</view
>
</view>
</view>
<u-toast ref="uToast" />
</view>
</template>
<script>
export default {
data() {
return {
scrollTop: 0,
swiperIndex: 0,
showType: false,
detail: {},
form: {
num: 1,
product_id: "",
product_sku_id: "",
},
tabs: [
{
text: "宝贝",
targetClass: "content",
},
{
text: "评价",
targetClass: "comment",
},
{
text: "详情",
targetClass: "detail",
},
{
text: "推荐",
targetClass: "recommend",
},
],
tabHeight: null,
currentTab: 0,
recommendPro: [],
timer: null,
cartNum: 0,
commentList: [],
isSkuSubmit: false,
isImmediateBuy: false,
};
},
computed: {
menuButtonLeft() {
return (
uni.getSystemInfoSync().windowWidth -
uni.getMenuButtonBoundingClientRect().left
);
},
tabTop() {
return uni.getMenuButtonBoundingClientRect().bottom;
},
swiper() {
return this.detail?.product_images?.map((i) => i.image?.url) || [];
},
productSku() {
return this.detail?.product_skus?.find(
(sku) => sku.id === this.form.product_sku_id
);
},
},
methods: {
toDetail(item) {
this.$u.route({
url: '/package_sub/pages/Shop/ProductDetail',
params: {
id: item.id
}
})
},
// 获取一个目标元素的高度
getElRect(elClass, dataVal) {
return new Promise((resolve, reject) => {
const query = uni.createSelectorQuery().in(this);
query
.select("." + elClass)
.fields(
{
rect: true,
},
(res) => {
// 如果节点尚未生成res值为null循环调用执行
if (!res) {
setTimeout(() => {
this.getElRect(elClass);
}, 10);
return;
}
if (dataVal) {
this["dataVal"] = res.top;
}
resolve(res.top);
}
)
.exec();
});
},
async getCartNum() {
try {
const res = await this.$u.api.cartList({
page: 1,
page_size: 1,
});
this.cartNum = res.total;
} catch (err) {}
},
async getComment() {
this.commentList = [
{
id: 1,
name: "叶轻眉",
date: "12-25 18:58",
contentText:
"我不信伊朗会没有后续反应,美国肯定会为今天的事情付出代价的",
url: "https://cdn.uviewui.com/uview/template/SmilingDog.jpg",
allReply: 12,
likeNum: 33,
isLike: false,
replyList: [
{
name: "uview",
contentStr:
"uview是基于uniapp的一个UI框架代码优美简洁宇宙超级无敌彩虹旋转好用用它",
},
{
name: "粘粘",
contentStr:
"今天吃什么,明天吃什么,晚上吃什么,我只是一只小猫咪为什么要烦恼这么多",
},
],
},
{
id: 2,
name: "叶轻眉1",
date: "01-25 13:58",
contentText:
"我不信伊朗会没有后续反应,美国肯定会为今天的事情付出代价的",
allReply: 0,
likeNum: 11,
isLike: false,
url: "https://cdn.uviewui.com/uview/template/niannian.jpg",
},
{
id: 3,
name: "叶轻眉2",
date: "03-25 13:58",
contentText:
"我不信伊朗会没有后续反应,美国肯定会为今天的事情付出代价的",
allReply: 0,
likeNum: 21,
isLike: false,
url: "../../../static/logo.png",
replyList: [
{
name: "uview",
contentStr:
"uview是基于uniapp的一个UI框架代码优美简洁宇宙超级无敌彩虹旋转好用用它",
},
{
name: "豆包",
contentStr: "想吃冰糖葫芦粘豆包但没钱5555.........",
},
],
},
{
id: 4,
name: "叶轻眉3",
date: "06-20 13:58",
contentText:
"我不信伊朗会没有后续反应,美国肯定会为今天的事情付出代价的",
url: "https://cdn.uviewui.com/uview/template/SmilingDog.jpg",
allReply: 0,
likeNum: 150,
isLike: false,
},
];
},
async getRecommendPro() {
try {
const res = await this.$u.api.productList({
page: 1,
page_size: 6,
sort_name: "sort",
});
this.recommendPro = res.list?.data;
} catch (err) {}
},
async pageScrollHandle() {
try {
if (!this.tabHeight) {
const rect = await Promise.all(
this.tabs.map((tab, index) => this.getElRect(tab.targetClass))
);
this.tabHeight = rect;
}
} catch (err) {
console.error(err);
}
},
tabClick(index) {
this.currentTab = index;
uni.pageScrollTo({
scrollTop: this.tabHeight[index] + 1,
});
},
back() {
this.$u.route({
type: "back",
});
},
async getDetail(id) {
try {
const res = await this.$u.api.productDetail({
id,
});
this.detail = res.detail;
// if (res.detail?.product_skus?.length > 0) {
// this.form["product_sku_id"] = res.detail.product_skus[0].id;
// }
this.$nextTick((_) => {
setTimeout((_) => {
this.pageScrollHandle();
}, 500);
});
} catch (err) {
console.error(err);
}
},
async confirm() {
try {
if (this.isSkuSubmit) {
if (!this.form.product_sku_id) {
this.$refs.uToast.show({
title: "请选择产品规格",
type: "warning",
});
return
}
if (!this.form.num) {
this.$refs.uToast.show({
title: "请选择产品数量",
type: "warning",
});
return
}
const res = await this.$u.api.cartSave(this.form);
this.$refs.uToast.show({
title: "已加入购物车",
type: "success",
});
this.getCartNum();
}
if (this.isImmediateBuy) {
}
this.showType = false;
} catch (err) {}
},
},
created() {
this.getRecommendPro();
this.getCartNum();
this.getComment();
},
onLoad(option) {
if (option.id) {
this.form.product_id = option.id;
this.getDetail(option.id);
}
},
onPageScroll({ scrollTop }) {
if (this.timer) return;
this.timer = setTimeout(() => {
// 节流
this.timer = null;
this.scrollTop = scrollTop;
if (scrollTop < this.tabHeight[1]) {
this.currentTab = 0;
} else if (
scrollTop >= this.tabHeight[1] &&
scrollTop - this.tabTop - 86 < this.tabHeight[2]
) {
this.currentTab = 1;
} else if (
scrollTop - this.tabTop - 86 >= this.tabHeight[2] &&
scrollTop - this.tabTop - 86 < this.tabHeight[3]
) {
this.currentTab = 2;
} else {
this.currentTab = 3;
}
}, 100);
},
};
</script>
<style>
page {
background: #f5f5f5;
}
</style>
<style lang="scss" scoped>
::v-deep .u-swiper-indicator {
bottom: 46rpx !important;
}
.clear-btn {
border: none;
background-color: transparent;
line-height: 1.5;
font-size: 28rpx;
color: #333;
padding: 0;
&::after {
border: none;
}
}
.flex1 {
flex: 1;
}
.wrap {
position: relative;
}
.back-icon {
width: 60rpx;
height: 60rpx;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
z-index: 2;
top: 40rpx;
left: 30rpx;
border-radius: 100%;
filter: drop-shadow(0 0 2rpx #fff);
}
.content {
margin-top: -40rpx;
position: relative;
.swiper-num {
color: #fff;
position: absolute;
top: -60rpx;
left: 50%;
transform: translateX(-50%);
background: rgba(0, 0, 0, 0.3);
border-radius: 36rpx;
padding: 4rpx 10rpx;
line-height: 1;
}
.topbar {
height: 122rpx;
border-radius: 40rpx 40rpx 0 0;
background-image: linear-gradient(
90deg,
#c10d12 0%,
#c10d12 4%,
#e26165 99%,
#e26165 100%
);
text {
font-size: 22rpx;
color: #ffffff;
float: right;
padding-top: 26rpx;
padding-right: 52rpx;
}
}
.container {
border-radius: 40rpx 40rpx;
margin-top: -40rpx;
background: #fff;
min-height: calc(100vh - 729rpx);
position: relative;
.price {
font-size: 30rpx;
color: red;
padding: 38rpx 30rpx 0 34rpx;
position: relative;
z-index: 4;
}
.title {
font-size: 28rpx;
line-height: 1.5;
color: #333333;
padding: 38rpx 30rpx 0;
position: relative;
z-index: 4;
}
.switch-type {
display: flex;
align-items: center;
justify-content: space-between;
padding: 42rpx calc(34rpx + 34rpx) 0 34rpx;
position: relative;
z-index: 4;
&-scroll {
display: flex;
align-items: center;
}
&-item {
flex-shrink: 0;
border-radius: 6rpx;
background-color: #f5f5f5;
padding: 22rpx 20rpx;
font-size: 20rpx;
color: #333333;
border: 1px solid transparent;
margin-right: 18rpx;
max-width: 240rpx;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
&-active {
background-color: #fdf3f3;
border-color: #c5151a;
color: #c20d12;
}
}
}
.goods {
position: relative;
z-index: 4;
background: #fff;
padding: 48rpx 36rpx 0;
&-row {
display: flex;
align-items: center;
&__text {
font-size: 22rpx;
color: #333;
padding-left: 18rpx;
}
}
}
.split {
background: #f5f5f5;
height: 30rpx;
width: 100vw;
margin-top: 30rpx;
position: relative;
z-index: 4;
&::before {
content: "";
z-index: 4;
position: absolute;
background: #fff;
left: 0;
right: 0;
top: -30rpx;
height: 30rpx;
}
}
.tabs {
width: 100vw;
display: flex;
align-items: center;
position: sticky;
z-index: 3;
top: 0;
background: #fff;
filter: drop-shadow(0 1rpx 1rpx #00000042);
.search {
display: flex;
align-items: center;
z-index: 3;
position: absolute;
top: 0;
left: 0;
right: 0;
background: #fff;
transform: translateY(-100%);
&::before {
content: "";
position: absolute;
background: #fff;
left: 0;
right: 0;
height: 200rpx;
top: -200rpx;
z-index: 3;
}
}
.tab {
flex: 1;
text-align: center;
font-size: 24rpx;
color: #333333;
padding: 26rpx 0;
&-active {
color: #000;
}
}
}
.comment {
position: relative;
z-index: 2;
.comment-container {
display: flex;
padding: 0 34rpx;
margin-top: 38rpx;
.left {
image {
width: 54rpx;
height: 54rpx;
border-radius: 50%;
background-color: #f2f2f2;
}
}
.right {
flex: 1;
padding-left: 14rpx;
font-size: 22rpx;
.top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10rpx;
.name {
color: #333;
font-size: 24rpx;
}
.like {
display: flex;
align-items: center;
color: #9a9a9a;
font-size: 26rpx;
.num {
margin-right: 4rpx;
color: #9a9a9a;
}
}
.highlight {
color: $uni-color-primary;
.num {
color: $uni-color-primary;
}
}
}
.right-content {
margin-bottom: 10rpx;
}
.bottom {
margin-top: 20rpx;
display: flex;
font-size: 24rpx;
color: #9a9a9a;
}
}
}
}
.detail {
position: relative;
z-index: 2;
}
.recommend {
padding-bottom: 40rpx;
position: relative;
z-index: 2;
&-wrap {
padding: 0 20rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 20rpx 56rpx;
.product-item {
.bottom {
padding-top: 16rpx;
}
&__img {
width: 100%;
height: 266rpx;
}
&__title {
font-size: 25rpx;
color: #333333;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
word-break: break-all;
}
&__price {
display: flex;
align-items: center;
justify-content: space-between;
text {
color: red;
}
text:nth-child(1) {
font-size: 21rpx;
}
text:nth-child(2) {
font-size: 32rpx;
}
}
}
}
}
.panel-title {
font-size: 32rpx;
color: #333333;
font-weight: 500;
padding: 36rpx 30rpx 32rpx 30rpx;
display: flex;
align-items: center;
}
}
}
.product {
padding: 80rpx 20rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 40rpx;
&-item {
background: #f5f5f5;
border: 1px solid transparent;
padding: 10rpx;
border-radius: 10rpx;
position: relative;
.bottom {
padding-top: 16rpx;
}
.top {
position: relative;
}
&__soldout {
height: 266rpx;
width: 100%;
padding: 40rpx;
box-sizing: border-box;
background: #ffffff99;
opacity: .86;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
&__img {
width: 100%;
height: 266rpx;
}
&__title {
font-size: 25rpx;
color: #333333;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
word-break: break-all;
}
&__price {
display: flex;
align-items: center;
justify-content: space-between;
text {
color: red;
}
text:nth-child(1) {
font-size: 21rpx;
}
text:nth-child(2) {
font-size: 32rpx;
}
}
&-active {
border-color: red;
&__check {
position: absolute;
top: 10rpx;
right: 10rpx;
}
}
}
}
.navigation {
display: flex;
margin-top: 100rpx;
border: solid 2rpx #f2f2f2;
background-color: #ffffff;
padding: 16rpx 0;
padding-bottom: calc(constant(safe-area-inset-bottom));
padding-bottom: calc(env(safe-area-inset-bottom));
z-index: 10;
position: sticky;
bottom: 0;
.left {
display: flex;
font-size: 20rpx;
.item {
color: #333;
margin: 0 30rpx;
&.car {
text-align: center;
position: relative;
.car-num {
position: absolute;
top: -10rpx;
right: -10rpx;
}
}
}
}
.right {
display: flex;
font-size: 28rpx;
align-items: center;
margin-left: auto;
margin-right: 20rpx;
.btn {
line-height: 64rpx;
padding: 0 34rpx;
color: #ffffff;
}
.cart {
border-radius: 6rpx 0 0 6rpx;
background-color: #ff9c1b;
}
.buy {
border-radius: 0 6rpx 6rpx 0;
background: linear-gradient(0deg, #ff2a00 0%, #f26011 100%);
}
}
}
</style>