Files
wagoo-douy3/src/pages/client/service/details.vue
2026-03-06 16:54:32 +08:00

398 lines
9.8 KiB
Vue

<template>
<view class="shop-details-container">
<view class="banner-swiper" v-if="bannerList.length">
<swiper
class="swiper-wrapper"
circular
:indicator-dots="false"
:autoplay="true"
@change="bannerChange"
>
<swiper-item v-for="(banner, i) in bannerList" :key="i">
<image class="swiper-img" :src="banner" mode="aspectFill" />
</swiper-item>
</swiper>
<view class="flex-row-center dot-box">
<view
v-for="(banner, index) in bannerList"
:key="index"
class="dot-item"
:class="{ active: bannerIndex === index }"
></view>
</view>
</view>
<view class="good-info">
<view class="info-cell">
<view class="flex-row-between">
<view class="">
<text class="app-fc-mark fs-24"></text>
<text class="app-fc-mark fs-44">{{ details.price || 0 }}</text>
<text class="fs-24 origin-price">
¥{{ details.old_price || 0 }}
</text>
</view>
<view class="flex-row-end">
<text class="fs-22 app-fc-cancel">
已售 {{ details.xiaoliang || 0 }}
</text>
<view class="fs-22 app-fc-cancel split-line">|</view>
<text class="fs-22 app-fc-cancel">
剩余{{ details.kucun || 0 }}
</text>
</view>
</view>
<view class="fs-36 app-font-bold app-fc-main good-name">
{{ details.name || "" }}
</view>
<view class="flex-row-start">
<text
class="fs-20 sale-btn"
v-for="(label, i) in labelList"
:key="i"
:class="[i > 0 ? 'sale-today app-fc-mark' : 'app-fc-white']"
>
{{ label }}
</text>
</view>
</view>
<view class="info-cell">
<view class="flex-row-start cell-inner">
<text class="fs-26 postage-info">使用区间</text>
<text class="fs-26 app-fc-main flex-1">
{{ details.weight_name || "-" }}
</text>
</view>
<view class="flex-row-start cell-inner">
<text class="fs-26 postage-info">有效时间</text>
<text class="fs-26 app-fc-main flex-1">
{{ details.youxiao_time || "-" }}
</text>
</view>
<view class="flex-row-start cell-inner" @click="showGuideModal = true">
<text class="fs-26 postage-info">使用规则</text>
<text class="fs-26 app-fc-main flex-1 app-text-ellipse">
{{ details.desc || "-" }}
</text>
<image
class="arrow-icon"
:src="require('@/static/images/arrow_right_black.png')"
mode="widthFix"
/>
</view>
</view>
<view class="info-cell" v-if="remarkList.length">
<view class="width-fill flex-row-between">
<text class="fs-28 postage-info">
用户评价
<text class="fs-24 app-fc-normal"> ({{ remarkCount }}) </text>
</text>
<view class="flex-row-end" @click="jumpToRemark">
<text class="fs-26 app-fc-main">查看全部</text>
<image
class="arrow-icon"
:src="require('@/static/images/arrow_right_black.png')"
/>
</view>
</view>
<view class="remark-list">
<remark-item v-for="item in remarkList" :key="item.id" :data="item" />
</view>
</view>
</view>
<view class="fs-36 app-fc-normal rich-text-title"> 服务详情 </view>
<view class="rich-text-content" v-html="details.content"></view>
<view class="place-view"></view>
<view class="flex-row-between details-bottom">
<text class="fs-30 app-fc-main">
<text class="fs-48 app-fc-main app-font-bold">{{ payPrice }}</text>
</text>
<view
class="flex-center fs-30 app-fc-white app-font-bold opt-btn"
@click="createOrder"
>
立即购买
</view>
</view>
<use-guide-modal
v-if="showGuideModal"
title="使用规则"
:content="details.desc"
@ok="showGuideModal = false"
/>
<success-modal
v-if="showSuccessModal"
title="购买成功"
ok-text="立即预约"
@ok="paySuccessAction"
/>
</view>
</template>
<script>
import { getServiceCouponDetail } from "@/api/coupon";
import { getRemarkList } from "../../../api/shop";
import { ORDER_TYPE_PET_SERVICE } from '@/constants/app.business';
import {
serviceCouponCreateOrder,
serviceCouponOrderPay,
} from "../../../api/coupon";
import UseGuideModal from "@/components/coupon/UseGuideModal.vue";
import SuccessModal from "@/components/SuccessModal";
import RemarkItem from "@/components/goods/RemarkItem";
export default {
components: {
UseGuideModal,
SuccessModal,
RemarkItem
},
data() {
return {
couponId: "",
details: {},
bannerList: [],
bannerIndex: 0,
remarkList: [],
showGuideModal: false,
showSuccessModal: false,
remarkCount : 0
};
},
computed: {
labelList() {
return (this.details?.label || "").split(",").filter((v) => !!v);
},
payPrice() {
return +this.details.price || 0;
},
},
onLoad(options) {
this.couponId = options.id;
this.getDetails();
this.getRemarkListData();
},
methods: {
// 获取服务券详情
getDetails() {
getServiceCouponDetail(this.couponId).then((res) => {
this.details = res?.info || {};
this.bannerList = (this.details.fuwuquan_pic || []).split(',').filter(v => !!v);
});
},
// 获取评论列表
getRemarkListData() {
getRemarkList({ p: 1, num: 1, type: ORDER_TYPE_PET_SERVICE, guanlian_id: this.couponId }).then(
(res) => {
this.remarkList = res?.info || [];
this.remarkCount = res?.count || 0;
}
);
},
// 轮播图
bannerChange(e) {
this.bannerIndex = e.detail.current;
},
stripTags(html = "") {
return html.replace(/<[^>]+>/g, "");
},
jumpToRemark() {
uni.navigateTo({
url: `/pages/client/remark/list?shopId=${this.couponId}&type=${ORDER_TYPE_PET_SERVICE}`,
});
},
// 下单
createOrder() {
uni.showLoading({
title: "处理中",
icon: "none",
mask: true,
});
serviceCouponCreateOrder(this.couponId).then((res) => {
this.pay(res?.info);
});
},
// 支付
pay(orderId) {
serviceCouponOrderPay(orderId).then((res) => {
tt.pay({
orderInfo: {
order_id:res.data.orderInfo.order_id,
order_token:res.data.orderInfo.order_token,
},
service:5,
success: (res) => {
uni.hideLoading();
this.showSuccessModal = true;
},
fail: (err) => {
uni.hideLoading();
uni.showToast({
title: err?.msg || "支付失败",
icon: "none",
});
},
});
});
},
paySuccessAction(){
this.showSuccessModal = false;
uni.redirectTo({
url: '/pageHome/reservation/index'
})
}
},
};
</script>
<style lang="scss" scoped>
.shop-details-container {
height: 100%;
background: #f7f8fa;
.banner-swiper {
position: relative;
width: 100%;
height: 750rpx;
.swiper-wrapper {
width: 100%;
height: 750rpx;
.swiper-img {
width: 100%;
height: 100%;
}
}
.dot-box {
position: absolute;
bottom: 30rpx;
left: 0;
width: 100%;
.dot-item {
width: 12rpx;
height: 12rpx;
border-radius: 12rpx;
background: rgba(255, 255, 255, 0.6);
margin-right: 12rpx;
&.active {
width: 28rpx;
height: 12rpx;
background: #ffffff;
}
}
}
}
.good-info {
margin: 24rpx 32rpx;
.info-cell {
background: #fff;
border-radius: 20rpx;
padding: 36rpx 24rpx;
margin-bottom: 24rpx;
.split-line {
margin: 0 10rpx;
}
}
.cell-inner {
padding: 12rpx 0;
.info-arrow-icon {
width: 40rpx;
height: 20rpx;
}
}
.origin-price {
color: #726e71;
text-decoration: line-through;
margin-left: 18rpx;
line-height: 44rpx;
}
.good-name {
margin: 30rpx 0;
}
.sale-btn {
padding: 6rpx 12rpx;
background: $app_color_main;
border-radius: 4rpx;
border: 1rpx solid $app_color_main;
margin-right: 12rpx;
margin-bottom: 24rpx;
&.sale-today {
background: transparent;
}
}
.postage-info {
color: #9b939a;
margin-right: 32rpx;
}
.arrow-icon {
width: 30rpx;
height: 18rpx;
margin-left: 20rpx;
}
.remark-list {
border-top: 2rpx solid #ebebeb;
margin-top: 36rpx;
::v-deep {
.remark-item {
width: 100%;
margin: 0;
padding: 40rpx 0;
}
}
}
}
.rich-text-title {
margin: 32rpx 0 24rpx;
text-align: center;
}
.rich-text-content {
background: #fff;
padding: 24rpx;
}
.place-view {
height: 190rpx;
}
.details-bottom {
position: fixed;
bottom: 0;
padding-bottom: 20rpx;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
padding-top: 20rpx;
padding-left: 40rpx;
padding-right: 36rpx;
left: 0;
width: 100vw;
background: #fff;
box-sizing: border-box;
.opt-btn {
width: 260rpx;
height: 92rpx;
background: $app_color_main;
border-radius: 92rpx;
}
}
}
</style>