GameServicePlatform/pages/service/detail.vue

468 lines
9.0 KiB
Vue

<template>
<view class="service-detail-page page-container">
<!-- 服务封面 -->
<view class="service-cover">
<image :src="service.coverImage" mode="aspectFill" class="cover-image" />
<view class="cover-overlay"></view>
<!-- 返回按钮 -->
<view class="back-btn" @click="goBack">
<text class="back-icon"></text>
</view>
<!-- 分享按钮 -->
<view class="share-btn" @click="handleShare">
<text class="share-icon">📤</text>
</view>
</view>
<!-- 服务信息 -->
<view class="service-info cyber-card">
<view class="service-header">
<view class="service-title">{{ service.name }}</view>
<view class="price-container">
<text class="price-symbol">¥</text>
<text class="price-value">{{ service.price }}</text>
</view>
</view>
<view class="service-stats">
<view class="stat-item">
<text class="stat-label">评分</text>
<text class="stat-value neon-text">{{ service.rating }} </text>
</view>
<view class="stat-item">
<text class="stat-label">销量</text>
<text class="stat-value">{{ service.salesCount }}</text>
</view>
<view class="stat-item">
<text class="stat-label">时长</text>
<text class="stat-value">{{ service.serviceTime }}分钟</text>
</view>
</view>
<view class="tags-container">
<view v-for="tag in service.tags" :key="tag" class="tag-item">
{{ tag }}
</view>
</view>
</view>
<!-- 商家信息 -->
<view class="merchant-card cyber-card">
<view class="merchant-header">
<image :src="service.merchantAvatar" class="merchant-avatar" />
<view class="merchant-info">
<text class="merchant-name">{{ service.merchantName }}</text>
<text class="merchant-desc">{{ service.playerCount }}位专业选手</text>
</view>
<button class="contact-btn" @click="contactMerchant">联系商家</button>
</view>
</view>
<!-- 服务详情 -->
<view class="service-detail cyber-card">
<view class="section-title">服务详情</view>
<view class="detail-content">
<text class="detail-text">{{ service.description }}</text>
</view>
</view>
<!-- 用户评价 -->
<view class="evaluations-card cyber-card">
<view class="section-title">
用户评价 ({{ service.reviewCount }})
</view>
<view class="evaluation-item" v-for="i in 3" :key="i">
<view class="eval-header">
<image src="https://picsum.photos/100/100?random=401" class="user-avatar" />
<view class="user-info">
<text class="user-name">用户{{ i }}</text>
<view class="rating"></view>
</view>
<text class="eval-time">2天前</text>
</view>
<text class="eval-content">服务很棒,选手很专业,下次还会再来!</text>
</view>
<view class="view-all" @click="viewAllEvaluations">
查看全部评价 >
</view>
</view>
<!-- 底部操作栏 -->
<view class="bottom-bar">
<view class="action-btns">
<view class="icon-btn" @click="toggleFavorite">
<text class="icon">{{ isFavorite ? '❤️' : '🤍' }}</text>
<text class="label">收藏</text>
</view>
<view class="icon-btn" @click="contactService">
<text class="icon">💬</text>
<text class="label">客服</text>
</view>
</view>
<button class="order-btn cyber-button" @click="goOrder">
立即下单
</button>
</view>
</view>
</template>
<script setup>
import { ref, onLoad } from '@dcloudio/uni-app'
import { getServiceById } from '../../mock'
const service = ref({
id: 0,
name: '',
coverImage: '',
price: 0,
originalPrice: 0,
description: '',
serviceTime: 0,
salesCount: 0,
rating: 5.0,
reviewCount: 0,
tags: [],
merchantName: '',
merchantAvatar: '',
playerCount: 0
})
const isFavorite = ref(false)
onLoad((options) => {
if (options.id) {
const serviceData = getServiceById(parseInt(options.id))
if (serviceData) {
service.value = serviceData
}
}
})
const goBack = () => {
uni.navigateBack()
}
const handleShare = () => {
uni.showShareMenu({
title: service.value.name,
path: `/pages/service/detail?id=${service.value.id}`
})
}
const toggleFavorite = () => {
isFavorite.value = !isFavorite.value
uni.showToast({
title: isFavorite.value ? '已收藏' : '取消收藏',
icon: 'none'
})
}
const contactMerchant = () => {
uni.showToast({ title: '联系商家', icon: 'none' })
}
const contactService = () => {
uni.showToast({ title: '联系客服', icon: 'none' })
}
const viewAllEvaluations = () => {
uni.showToast({ title: '查看全部评价', icon: 'none' })
}
const goOrder = () => {
uni.navigateTo({
url: `/pages/order/create?serviceId=${service.value.id}`
})
}
</script>
<style lang="scss" scoped>
.service-detail-page {
padding-bottom: 160rpx;
}
.service-cover {
position: relative;
width: 100%;
height: 600rpx;
margin-bottom: 24rpx;
}
.cover-image {
width: 100%;
height: 100%;
}
.cover-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 200rpx;
background: linear-gradient(180deg, transparent 0%, rgba(10, 14, 39, 0.9) 100%);
}
.back-btn,
.share-btn {
position: absolute;
top: 40rpx;
width: 72rpx;
height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
background: rgba(10, 14, 39, 0.8);
border: 1px solid rgba(0, 255, 255, 0.3);
border-radius: 50%;
backdrop-filter: blur(10rpx);
}
.back-btn {
left: 24rpx;
}
.share-btn {
right: 24rpx;
}
.back-icon,
.share-icon {
font-size: 36rpx;
color: #00ffff;
}
.service-info,
.merchant-card,
.service-detail,
.evaluations-card {
margin: 0 24rpx 24rpx;
padding: 32rpx;
}
.service-header {
margin-bottom: 28rpx;
}
.service-title {
font-size: 36rpx;
font-weight: bold;
color: #ffffff;
margin-bottom: 20rpx;
}
.price-container {
display: flex;
align-items: baseline;
}
.price-symbol {
font-size: 32rpx;
font-weight: bold;
color: #00ffff;
}
.price-value {
font-size: 56rpx;
font-weight: bold;
color: #00ffff;
text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
}
.service-stats {
display: flex;
gap: 48rpx;
margin-bottom: 28rpx;
}
.stat-item {
display: flex;
flex-direction: column;
gap: 8rpx;
}
.stat-label {
font-size: 24rpx;
color: #7a7e9d;
}
.stat-value {
font-size: 28rpx;
font-weight: bold;
color: #ffffff;
}
.tags-container {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
.tag-item {
padding: 8rpx 20rpx;
background: rgba(0, 255, 255, 0.1);
border: 1px solid rgba(0, 255, 255, 0.3);
border-radius: 8rpx;
font-size: 24rpx;
color: #00ffff;
}
.merchant-header {
display: flex;
align-items: center;
gap: 20rpx;
}
.merchant-avatar {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
border: 1px solid rgba(0, 255, 255, 0.3);
}
.merchant-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 8rpx;
}
.merchant-name {
font-size: 30rpx;
font-weight: bold;
color: #ffffff;
}
.merchant-desc {
font-size: 24rpx;
color: #7a7e9d;
}
.contact-btn {
padding: 16rpx 32rpx;
background: transparent;
border: 1px solid #00ffff;
border-radius: 8rpx;
font-size: 26rpx;
color: #00ffff;
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #ffffff;
margin-bottom: 24rpx;
}
.detail-content {
padding: 24rpx;
background: rgba(10, 14, 39, 0.5);
border-radius: 12rpx;
}
.detail-text {
font-size: 28rpx;
line-height: 1.8;
color: #a0a4c4;
}
.evaluation-item {
padding: 24rpx 0;
border-bottom: 1px solid rgba(0, 255, 255, 0.1);
}
.eval-header {
display: flex;
align-items: center;
gap: 16rpx;
margin-bottom: 16rpx;
}
.user-avatar {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
}
.user-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 8rpx;
}
.user-name {
font-size: 26rpx;
color: #ffffff;
}
.rating {
font-size: 20rpx;
}
.eval-time {
font-size: 22rpx;
color: #7a7e9d;
}
.eval-content {
font-size: 26rpx;
line-height: 1.6;
color: #a0a4c4;
}
.view-all {
text-align: center;
padding: 24rpx 0;
font-size: 26rpx;
color: #00ffff;
}
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
gap: 24rpx;
padding: 24rpx;
background: linear-gradient(180deg, transparent 0%, rgba(10, 14, 39, 0.95) 20%, rgba(10, 14, 39, 1) 100%);
border-top: 1px solid rgba(0, 255, 255, 0.2);
backdrop-filter: blur(20rpx);
}
.action-btns {
display: flex;
gap: 32rpx;
}
.icon-btn {
display: flex;
flex-direction: column;
align-items: center;
gap: 8rpx;
}
.icon {
font-size: 40rpx;
}
.label {
font-size: 22rpx;
color: #7a7e9d;
}
.order-btn {
flex: 1;
height: 88rpx;
line-height: 88rpx;
text-align: center;
font-size: 32rpx;
font-weight: bold;
}
</style>