469 lines
9.6 KiB
Plaintext
469 lines
9.6 KiB
Plaintext
<template>
|
||
<view class="page">
|
||
<scroll-view class="page-scroll" scroll-y>
|
||
<!-- 图片画廊 -->
|
||
<view class="gallery-section">
|
||
<swiper
|
||
class="gallery-swiper"
|
||
circular
|
||
indicator-dots
|
||
indicator-color="rgba(255,255,255,0.5)"
|
||
indicator-active-color="#ffffff"
|
||
>
|
||
<swiper-item v-for="(item, index) in caseDetail.afterImages" :key="index">
|
||
<image
|
||
class="gallery-image"
|
||
:src="item"
|
||
mode="aspectFill"
|
||
@click="previewImages(index)"
|
||
></image>
|
||
</swiper-item>
|
||
</swiper>
|
||
<view class="gallery-tag">
|
||
<text class="gallery-tag-text">{{ caseDetail.categoryName }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 基本信息 -->
|
||
<view class="info-section">
|
||
<text class="case-title">{{ caseDetail.title }}</text>
|
||
<view class="case-meta">
|
||
<text class="case-price">{{ caseDetail.price }}</text>
|
||
<view class="case-stats">
|
||
<text class="stat-text">👁 {{ caseDetail.views }}</text>
|
||
<text class="stat-text">❤ {{ caseDetail.likes }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 翻新前后对比 -->
|
||
<view class="compare-section">
|
||
<before-after
|
||
:beforeImage="caseDetail.beforeImages[0] || ''"
|
||
:afterImage="caseDetail.compareAfterImages[0] || ''"
|
||
:showTitle="true"
|
||
></before-after>
|
||
</view>
|
||
|
||
<!-- 详细信息 -->
|
||
<view class="detail-section">
|
||
<view class="detail-header">
|
||
<text class="detail-title">翻新详情</text>
|
||
</view>
|
||
|
||
<view class="detail-list">
|
||
<view class="detail-item">
|
||
<text class="detail-label">使用材质</text>
|
||
<text class="detail-value">{{ caseDetail.material }}</text>
|
||
</view>
|
||
<view class="detail-item">
|
||
<text class="detail-label">翻新工期</text>
|
||
<text class="detail-value">{{ caseDetail.duration }}</text>
|
||
</view>
|
||
<view class="detail-item">
|
||
<text class="detail-label">完成日期</text>
|
||
<text class="detail-value">{{ caseDetail.createTime }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="detail-desc">
|
||
<text class="desc-title">案例描述</text>
|
||
<text class="desc-content">{{ caseDetail.description }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部间距 -->
|
||
<view class="bottom-space"></view>
|
||
</scroll-view>
|
||
|
||
<!-- 底部操作栏 -->
|
||
<view class="bottom-bar">
|
||
<view class="bar-left">
|
||
<view class="bar-btn" @click="handleFavorite">
|
||
<text class="bar-icon">{{ isFavorite ? '❤️' : '🤍' }}</text>
|
||
<text class="bar-label">收藏</text>
|
||
</view>
|
||
<view class="bar-btn" @click="handleShare">
|
||
<text class="bar-icon">📤</text>
|
||
<text class="bar-label">分享</text>
|
||
</view>
|
||
</view>
|
||
<view class="bar-right">
|
||
<view class="contact-btn" @click="handleContact">
|
||
<text class="contact-text">在线咨询</text>
|
||
</view>
|
||
<view class="booking-btn" @click="goToBooking">
|
||
<text class="booking-text">立即预约</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { getCaseDetail } from '@/api/index.uts'
|
||
import { getServiceTypeName } from '@/utils/config.uts'
|
||
|
||
// 案例详情类型
|
||
type CaseDetail = {
|
||
id : string
|
||
title : string
|
||
category : string
|
||
categoryName : string
|
||
beforeImages : string[]
|
||
afterImages : string[]
|
||
// 用于对比组件的专用后图(不使用 images 回退)
|
||
compareAfterImages : string[]
|
||
description : string
|
||
material : string
|
||
duration : string
|
||
price : string
|
||
views : number
|
||
likes : number
|
||
createTime : string
|
||
}
|
||
|
||
// 案例ID
|
||
const caseId = ref('')
|
||
|
||
// 案例详情
|
||
const caseDetail = ref<CaseDetail>({
|
||
id: '',
|
||
title: '',
|
||
category: '',
|
||
categoryName: '',
|
||
beforeImages: [],
|
||
afterImages: [],
|
||
compareAfterImages: [],
|
||
description: '',
|
||
material: '',
|
||
duration: '',
|
||
price: '',
|
||
views: 0,
|
||
likes: 0,
|
||
createTime: ''
|
||
})
|
||
|
||
// 是否收藏
|
||
const isFavorite = ref(false)
|
||
|
||
// 获取案例详情
|
||
const fetchCaseDetail = async () => {
|
||
try {
|
||
const res = await getCaseDetail(caseId.value)
|
||
if (res.code === 0 && res.data != null) {
|
||
// 处理成功的返回数据
|
||
const data = res.data as UTSJSONObject
|
||
|
||
// 适配后端返回的字段
|
||
const images = data['images'] as string[] || []
|
||
const beforeImages = data['beforeImages'] as string[] || []
|
||
const afterImages = data['afterImages'] as string[] || []
|
||
const createdAt = data['createdAt'] as string || ''
|
||
|
||
// 画廊展示:如果afterImages为空,使用images作为回退
|
||
const displayAfterImages = afterImages.length > 0 ? afterImages : images
|
||
// 对比组件(before-after)应只使用专用的 afterImages,不回退到 images
|
||
const compareAfterImages = afterImages.length > 0 ? afterImages : []
|
||
|
||
caseDetail.value = {
|
||
id: String(data['id']),
|
||
title: data['title'] as string,
|
||
category: data['serviceType'] as string || '',
|
||
categoryName: getServiceTypeName(data['serviceType'] as string),
|
||
beforeImages: beforeImages,
|
||
afterImages: displayAfterImages,
|
||
compareAfterImages: compareAfterImages,
|
||
description: data['description'] as string,
|
||
material: data['materials'] as string || '优质材料',
|
||
duration: (data['duration'] as number || 0) + '天',
|
||
price: data['price'] != null ? '¥' + data['price'] : '面议',
|
||
views: data['views'] as number || 0,
|
||
likes: data['likes'] as number || 0,
|
||
createTime: createdAt.split('T')[0] || ''
|
||
} as CaseDetail
|
||
|
||
// 更新标题
|
||
uni.setNavigationBarTitle({
|
||
title: caseDetail.value.title
|
||
})
|
||
} else {
|
||
uni.showToast({
|
||
title: res.message || '加载失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
} catch (error) {
|
||
console.error('获取案例详情失败:', error)
|
||
uni.showToast({
|
||
title: '加载失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
|
||
// 预览图片
|
||
const previewImages = (index : number) => {
|
||
const urls = caseDetail.value.afterImages || []
|
||
const current = urls[index] || urls[0] || ''
|
||
uni.previewImage({
|
||
current: current,
|
||
urls: urls
|
||
})
|
||
}
|
||
|
||
// 收藏
|
||
const handleFavorite = () => {
|
||
isFavorite.value = !isFavorite.value
|
||
uni.showToast({
|
||
title: isFavorite.value ? '已收藏' : '已取消收藏',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
|
||
// 分享
|
||
const handleShare = () => {
|
||
// #ifdef MP-WEIXIN
|
||
// 微信小程序触发分享
|
||
// #endif
|
||
uni.showToast({
|
||
title: '分享功能开发中',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
|
||
// 在线咨询
|
||
const handleContact = () => {
|
||
uni.makePhoneCall({
|
||
phoneNumber: '400-888-8888',
|
||
fail: () => {
|
||
uni.showToast({
|
||
title: '拨打电话失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
// 预约
|
||
const goToBooking = () => {
|
||
uni.navigateTo({
|
||
url: '/pages/booking/index'
|
||
})
|
||
}
|
||
|
||
onLoad((options : OnLoadOptions) => {
|
||
caseId.value = options['id'] ?? ''
|
||
if (caseId.value != '') {
|
||
fetchCaseDetail()
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.page {
|
||
flex: 1;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.page-scroll {
|
||
flex: 1;
|
||
}
|
||
|
||
/* 图片画廊 */
|
||
.gallery-section {
|
||
position: relative;
|
||
}
|
||
|
||
.gallery-swiper {
|
||
height: 500rpx;
|
||
}
|
||
|
||
.gallery-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.gallery-tag {
|
||
position: absolute;
|
||
top: 24rpx;
|
||
left: 24rpx;
|
||
background-color: rgba(212, 165, 116, 0.9);
|
||
padding: 8rpx 20rpx;
|
||
border-radius: 8rpx;
|
||
}
|
||
|
||
.gallery-tag-text {
|
||
font-size: 24rpx;
|
||
color: #ffffff;
|
||
}
|
||
|
||
/* 基本信息 */
|
||
.info-section {
|
||
background-color: #ffffff;
|
||
padding: 32rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.case-title {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: #333333;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.case-meta {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.case-price {
|
||
font-size: 40rpx;
|
||
font-weight: 600;
|
||
color: #D4A574;
|
||
}
|
||
|
||
.case-stats {
|
||
flex-direction: row;
|
||
}
|
||
|
||
.stat-text {
|
||
font-size: 26rpx;
|
||
color: #909399;
|
||
margin-left: 24rpx;
|
||
}
|
||
|
||
/* 对比区域 */
|
||
.compare-section {
|
||
padding: 0 24rpx;
|
||
}
|
||
|
||
/* 详细信息 */
|
||
.detail-section {
|
||
background-color: #ffffff;
|
||
margin: 24rpx;
|
||
border-radius: 16rpx;
|
||
padding: 32rpx;
|
||
}
|
||
|
||
.detail-header {
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.detail-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: #333333;
|
||
}
|
||
|
||
.detail-list {
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
.detail-item {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
padding: 16rpx 0;
|
||
border-bottom-width: 1rpx;
|
||
border-bottom-style: solid;
|
||
border-bottom-color: #EBEEF5;
|
||
}
|
||
|
||
.detail-label {
|
||
font-size: 28rpx;
|
||
color: #909399;
|
||
}
|
||
|
||
.detail-value {
|
||
font-size: 28rpx;
|
||
color: #333333;
|
||
}
|
||
|
||
.detail-desc {
|
||
padding-top: 16rpx;
|
||
}
|
||
|
||
.desc-title {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #333333;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.desc-content {
|
||
font-size: 28rpx;
|
||
color: #606266;
|
||
line-height: 48rpx;
|
||
}
|
||
|
||
/* 底部间距 */
|
||
.bottom-space {
|
||
height: 160rpx;
|
||
}
|
||
|
||
/* 底部操作栏 */
|
||
.bottom-bar {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
background-color: #ffffff;
|
||
padding: 16rpx 24rpx;
|
||
padding-bottom: 32rpx;
|
||
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
.bar-left {
|
||
flex-direction: row;
|
||
}
|
||
|
||
.bar-btn {
|
||
align-items: center;
|
||
padding: 0 24rpx;
|
||
}
|
||
|
||
.bar-icon {
|
||
font-size: 40rpx;
|
||
}
|
||
|
||
.bar-label {
|
||
font-size: 22rpx;
|
||
color: #606266;
|
||
margin-top: 4rpx;
|
||
}
|
||
|
||
.bar-right {
|
||
flex-direction: row;
|
||
flex: 1;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.contact-btn {
|
||
background-color: #f5f5f5;
|
||
padding: 20rpx 40rpx;
|
||
border-radius: 999rpx;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.contact-text {
|
||
font-size: 28rpx;
|
||
color: #606266;
|
||
}
|
||
|
||
.booking-btn {
|
||
background-color: #D4A574;
|
||
padding: 20rpx 48rpx;
|
||
border-radius: 999rpx;
|
||
}
|
||
|
||
.booking-text {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #ffffff;
|
||
}
|
||
</style>
|