564 lines
12 KiB
Plaintext
564 lines
12 KiB
Plaintext
<template>
|
||
<view class="page">
|
||
<scroll-view class="page-scroll" scroll-y>
|
||
<!-- 顶部横幅 -->
|
||
<view class="banner">
|
||
<view class="banner-content">
|
||
<text class="banner-title">专业沙发翻新服务</text>
|
||
<text class="banner-desc">让旧沙发焕发新生</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 服务类型 -->
|
||
<view class="section">
|
||
<section-header title="服务类型"></section-header>
|
||
<view class="service-grid">
|
||
<view
|
||
class="service-item"
|
||
v-for="item in serviceTypes"
|
||
:key="item.id"
|
||
@click="handleServiceClick(item)"
|
||
>
|
||
<view class="service-icon-bg">
|
||
<text class="service-icon">{{ item.emoji }}</text>
|
||
</view>
|
||
<text class="service-name">{{ item.name }}</text>
|
||
<text class="service-desc">{{ item.desc }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 服务流程 -->
|
||
<view class="section">
|
||
<section-header title="服务流程"></section-header>
|
||
<view class="process-list">
|
||
<view class="process-item" v-for="(item, index) in processList" :key="item.step">
|
||
<view class="process-step">
|
||
<text class="step-num">{{ item.step }}</text>
|
||
</view>
|
||
<view class="process-content">
|
||
<text class="process-title">{{ item.title }}</text>
|
||
<text class="process-desc">{{ item.description }}</text>
|
||
</view>
|
||
<view class="process-line" v-if="index < processList.length - 1"></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 材质说明 -->
|
||
<view class="section">
|
||
<section-header title="材质说明"></section-header>
|
||
<view class="material-list">
|
||
<view class="material-item" v-for="item in materials" :key="item.name">
|
||
<view class="material-header">
|
||
<text class="material-name">{{ item.name }}</text>
|
||
<text class="material-price">{{ item.price }}</text>
|
||
</view>
|
||
<text class="material-desc">{{ item.desc }}</text>
|
||
<view class="material-tags">
|
||
<text class="material-tag" v-for="tag in item.tags" :key="tag">{{ tag }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 常见问题 -->
|
||
<view class="section">
|
||
<section-header title="常见问题"></section-header>
|
||
<view class="faq-list">
|
||
<view
|
||
class="faq-item"
|
||
v-for="(item, index) in faqList"
|
||
:key="index"
|
||
@click="toggleFaq(index)"
|
||
>
|
||
<view class="faq-header">
|
||
<text class="faq-question">{{ item.question }}</text>
|
||
<text class="faq-arrow">{{ item.expanded ? '−' : '+' }}</text>
|
||
</view>
|
||
<view class="faq-answer" v-if="item.expanded">
|
||
<text class="faq-answer-text">{{ item.answer }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部间距 -->
|
||
<view class="bottom-space"></view>
|
||
</scroll-view>
|
||
|
||
<!-- 底部预约按钮 -->
|
||
<view class="bottom-bar">
|
||
<view class="bar-info">
|
||
<text class="bar-title">免费上门评估</text>
|
||
<text class="bar-desc">专业师傅为您量身定制方案</text>
|
||
</view>
|
||
<view class="bar-btn" @click="goToBooking">
|
||
<text class="bar-btn-text">立即预约</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { getServiceProcess, getActiveServices } from '@/api/index.uts'
|
||
|
||
// 服务类型
|
||
type ServiceType = {
|
||
id : number
|
||
name : string
|
||
desc : string
|
||
emoji : string
|
||
type : string
|
||
basePrice : number
|
||
}
|
||
|
||
// 流程类型
|
||
type ProcessItem = {
|
||
step : number
|
||
title : string
|
||
description : string
|
||
}
|
||
|
||
// 材质类型
|
||
type MaterialItem = {
|
||
name : string
|
||
price : string
|
||
desc : string
|
||
tags : string[]
|
||
}
|
||
|
||
// FAQ类型
|
||
type FaqItem = {
|
||
question : string
|
||
answer : string
|
||
expanded : boolean
|
||
}
|
||
|
||
// 服务类型数据
|
||
const serviceTypes = ref<ServiceType[]>([])
|
||
|
||
// 服务流程
|
||
const processList = ref<ProcessItem[]>([
|
||
{ step: 1, title: '在线预约', description: '填写信息,预约上门时间' },
|
||
{ step: 2, title: '上门评估', description: '专业师傅免费上门勘察' },
|
||
{ step: 3, title: '确认方案', description: '沟通翻新方案和价格' },
|
||
{ step: 4, title: '取件翻新', description: '取回沙发进行专业翻新' },
|
||
{ step: 5, title: '送货验收', description: '送货上门,满意付款' }
|
||
])
|
||
|
||
// 材质说明
|
||
const materials = ref<MaterialItem[]>([
|
||
{
|
||
name: '头层牛皮',
|
||
price: '¥800-1500/平',
|
||
desc: '采用进口头层牛皮,质地柔软,透气性好,使用寿命长',
|
||
tags: ['进口原料', '透气舒适', '耐用耐磨']
|
||
},
|
||
{
|
||
name: '二层牛皮',
|
||
price: '¥400-800/平',
|
||
desc: '经济实惠的选择,经过特殊处理后外观与头层相近',
|
||
tags: ['性价比高', '外观精美', '易打理']
|
||
},
|
||
{
|
||
name: '科技布',
|
||
price: '¥200-500/平',
|
||
desc: '新型环保面料,防水防污,清洁方便,触感舒适',
|
||
tags: ['防水防污', '易清洁', '环保健康']
|
||
},
|
||
{
|
||
name: '棉麻布艺',
|
||
price: '¥150-400/平',
|
||
desc: '天然面料,透气舒适,适合追求自然风格的客户',
|
||
tags: ['天然环保', '透气清爽', '风格多样']
|
||
}
|
||
])
|
||
|
||
// 常见问题
|
||
const faqList = ref<FaqItem[]>([
|
||
{
|
||
question: '翻新需要多长时间?',
|
||
answer: '根据沙发的大小和翻新程度不同,一般需要3-10个工作日。局部修复1-3天,整体翻新5-10天。',
|
||
expanded: false
|
||
},
|
||
{
|
||
question: '翻新后能保证多久?',
|
||
answer: '我们提供2年质保服务,质保期内如有非人为损坏,免费维修。头层牛皮正常使用可达10年以上。',
|
||
expanded: false
|
||
},
|
||
{
|
||
question: '可以上门取送吗?',
|
||
answer: '是的,我们提供免费上门取送服务(市区范围内),郊区会收取少量运输费用。',
|
||
expanded: false
|
||
},
|
||
{
|
||
question: '如何报价?',
|
||
answer: '我们提供免费上门评估服务,师傅会根据沙发的实际情况给出详细报价,价格透明无隐形消费。',
|
||
expanded: false
|
||
},
|
||
{
|
||
question: '定金和尾款如何支付?',
|
||
answer: '确认订单后支付30%定金,翻新完成验收满意后支付尾款。支持微信、支付宝、银行转账等多种方式。',
|
||
expanded: false
|
||
}
|
||
])
|
||
|
||
// 获取服务列表
|
||
const fetchServices = async () => {
|
||
try {
|
||
const res = await getActiveServices()
|
||
if (res.code == 0 && res.data != null) {
|
||
const data = res.data as UTSJSONObject
|
||
const list = data['list'] as UTSJSONObject[] || []
|
||
|
||
// 服务类型emoji映射
|
||
const emojiMap = {
|
||
fabric: '🛋️',
|
||
leather: '💺',
|
||
cleaning: '✨',
|
||
repair: '🔧',
|
||
custom: '💎'
|
||
} as UTSJSONObject
|
||
|
||
serviceTypes.value = list.map((item) : ServiceType => {
|
||
const type = item['type'] as string
|
||
return {
|
||
id: item['id'] as number,
|
||
name: item['name'] as string,
|
||
desc: item['description'] as string,
|
||
emoji: emojiMap[type] as string || '🛋️',
|
||
type: type,
|
||
basePrice: item['basePrice'] as number
|
||
} as ServiceType
|
||
})
|
||
}
|
||
} catch (e) {
|
||
console.error('获取服务列表失败', e)
|
||
}
|
||
}
|
||
|
||
// 获取服务流程(暂时使用固定数据)
|
||
const fetchServiceProcess = async () => {
|
||
// 流程数据已在初始化时设置
|
||
}
|
||
|
||
// 切换FAQ展开
|
||
const toggleFaq = (index : number) => {
|
||
faqList.value[index].expanded = !faqList.value[index].expanded
|
||
}
|
||
|
||
// 服务点击
|
||
const handleServiceClick = (item : ServiceType) => {
|
||
uni.navigateTo({
|
||
url: '/pages/booking/index'
|
||
})
|
||
}
|
||
|
||
// 去预约
|
||
const goToBooking = () => {
|
||
uni.navigateTo({
|
||
url: '/pages/booking/index'
|
||
})
|
||
}
|
||
|
||
onLoad(() => {
|
||
fetchServices()
|
||
fetchServiceProcess()
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.page {
|
||
flex: 1;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
.page-scroll {
|
||
flex: 1;
|
||
}
|
||
|
||
/* 顶部横幅 */
|
||
.banner {
|
||
height: 280rpx;
|
||
background: linear-gradient(135deg, #D4A574 0%, #B8895A 100%);
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.banner-content {
|
||
align-items: center;
|
||
}
|
||
|
||
.banner-title {
|
||
font-size: 40rpx;
|
||
font-weight: 600;
|
||
color: #ffffff;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.banner-desc {
|
||
font-size: 28rpx;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
}
|
||
|
||
/* 通用section */
|
||
.section {
|
||
padding: 0 24rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
/* 服务类型 */
|
||
.service-grid {
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
background-color: #ffffff;
|
||
border-radius: 16rpx;
|
||
padding: 16rpx;
|
||
}
|
||
|
||
.service-item {
|
||
width: 50%;
|
||
padding: 24rpx;
|
||
align-items: center;
|
||
}
|
||
|
||
.service-icon-bg {
|
||
width: 100rpx;
|
||
height: 100rpx;
|
||
background-color: #FDF6EE;
|
||
border-radius: 50%;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.service-icon {
|
||
font-size: 48rpx;
|
||
}
|
||
|
||
.service-name {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #333333;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.service-desc {
|
||
font-size: 24rpx;
|
||
color: #909399;
|
||
}
|
||
|
||
/* 服务流程 */
|
||
.process-list {
|
||
background-color: #ffffff;
|
||
border-radius: 16rpx;
|
||
padding: 32rpx;
|
||
}
|
||
|
||
.process-item {
|
||
flex-direction: row;
|
||
position: relative;
|
||
padding-bottom: 32rpx;
|
||
}
|
||
|
||
.process-step {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
background-color: #D4A574;
|
||
border-radius: 50%;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 24rpx;
|
||
z-index: 1;
|
||
}
|
||
|
||
.step-num {
|
||
font-size: 24rpx;
|
||
font-weight: 600;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.process-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.process-title {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #333333;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
.process-desc {
|
||
font-size: 26rpx;
|
||
color: #909399;
|
||
}
|
||
|
||
.process-line {
|
||
position: absolute;
|
||
left: 22rpx;
|
||
top: 56rpx;
|
||
width: 4rpx;
|
||
height: 60rpx;
|
||
background-color: #E8C9A8;
|
||
}
|
||
|
||
/* 材质说明 */
|
||
.material-list {
|
||
background-color: #ffffff;
|
||
border-radius: 16rpx;
|
||
}
|
||
|
||
.material-item {
|
||
padding: 32rpx;
|
||
border-bottom-width: 1rpx;
|
||
border-bottom-style: solid;
|
||
border-bottom-color: #EBEEF5;
|
||
}
|
||
|
||
.material-item:last-child {
|
||
border-bottom-width: 0;
|
||
}
|
||
|
||
.material-header {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.material-name {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #333333;
|
||
}
|
||
|
||
.material-price {
|
||
font-size: 28rpx;
|
||
color: #D4A574;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.material-desc {
|
||
font-size: 26rpx;
|
||
color: #606266;
|
||
margin-bottom: 16rpx;
|
||
line-height: 40rpx;
|
||
}
|
||
|
||
.material-tags {
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.material-tag {
|
||
font-size: 22rpx;
|
||
color: #D4A574;
|
||
background-color: #FDF6EE;
|
||
padding: 8rpx 16rpx;
|
||
border-radius: 8rpx;
|
||
margin-right: 16rpx;
|
||
margin-bottom: 8rpx;
|
||
}
|
||
|
||
/* 常见问题 */
|
||
.faq-list {
|
||
background-color: #ffffff;
|
||
border-radius: 16rpx;
|
||
}
|
||
|
||
.faq-item {
|
||
padding: 32rpx;
|
||
border-bottom-width: 1rpx;
|
||
border-bottom-style: solid;
|
||
border-bottom-color: #EBEEF5;
|
||
}
|
||
|
||
.faq-item:last-child {
|
||
border-bottom-width: 0;
|
||
}
|
||
|
||
.faq-header {
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.faq-question {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #333333;
|
||
flex: 1;
|
||
}
|
||
|
||
.faq-arrow {
|
||
font-size: 36rpx;
|
||
color: #909399;
|
||
margin-left: 16rpx;
|
||
}
|
||
|
||
.faq-answer {
|
||
margin-top: 16rpx;
|
||
padding-top: 16rpx;
|
||
border-top-width: 1rpx;
|
||
border-top-style: dashed;
|
||
border-top-color: #EBEEF5;
|
||
}
|
||
|
||
.faq-answer-text {
|
||
font-size: 26rpx;
|
||
color: #606266;
|
||
line-height: 44rpx;
|
||
}
|
||
|
||
/* 底部间距 */
|
||
.bottom-space {
|
||
height: 160rpx;
|
||
}
|
||
|
||
/* 底部操作栏 */
|
||
.bottom-bar {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index:2;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
background-color: #ffffff;
|
||
padding: 20rpx 32rpx;
|
||
padding-bottom: 40rpx;
|
||
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
.bar-info {
|
||
flex: 1;
|
||
}
|
||
|
||
.bar-title {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #333333;
|
||
}
|
||
|
||
.bar-desc {
|
||
font-size: 24rpx;
|
||
color: #909399;
|
||
margin-top: 4rpx;
|
||
}
|
||
|
||
.bar-btn {
|
||
background-color: #D4A574;
|
||
padding: 24rpx 56rpx;
|
||
border-radius: 999rpx;
|
||
}
|
||
|
||
.bar-btn-text {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: #ffffff;
|
||
}
|
||
</style>
|