429 lines
11 KiB
Vue
429 lines
11 KiB
Vue
<template>
|
||
<view class="container">
|
||
<view>
|
||
<view class="card head">
|
||
<image v-if="p.cover" class="cover" :src="p.cover" mode="aspectFill" />
|
||
<view class="name">{{ p.name }}</view>
|
||
<view class="row between meta">
|
||
<view class="muted">服务时长:{{ p.durationMin }} 分钟</view>
|
||
<view class="price">
|
||
<text class="yen">¥</text>
|
||
<text class="num">{{ p.price }}</text>
|
||
<text class="ori muted" v-if="p.originPrice">¥{{ p.originPrice }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="row between meta2">
|
||
<view class="tag2">{{ categoryName }}</view>
|
||
<view class="muted">评分 {{ rating }} / 5.0</view>
|
||
</view>
|
||
<view class="tags row">
|
||
<view class="tag">{{ p.fitFor }}</view>
|
||
<view class="tag warn">禁忌:{{ p.taboo }}</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="card block">
|
||
<view class="b-t">服务介绍</view>
|
||
<view class="b-v muted">{{ p.desc }}</view>
|
||
</view>
|
||
|
||
<view class="card block">
|
||
<view class="b-t">功效说明</view>
|
||
<view class="b-v muted" v-for="x in effects" :key="x">· {{ x }}</view>
|
||
</view>
|
||
|
||
<view class="card block">
|
||
<view class="b-t">服务流程</view>
|
||
<view class="b-v muted" v-for="x in steps" :key="x">· {{ x }}</view>
|
||
</view>
|
||
|
||
<view class="card block">
|
||
<view class="b-t">适合人群</view>
|
||
<view class="b-v muted">{{ p.fitFor }}</view>
|
||
</view>
|
||
|
||
<view class="card block">
|
||
<view class="b-t">禁忌说明</view>
|
||
<view class="b-v muted">{{ p.taboo }}</view>
|
||
</view>
|
||
|
||
<view class="card block">
|
||
<view class="b-t">用户评价</view>
|
||
<view class="review">
|
||
<view class="r1 row between">
|
||
<view class="rn">顾客 A</view>
|
||
<view class="muted">5.0</view>
|
||
</view>
|
||
<view class="muted rt">做完肤感很通透,过程舒服,推荐。</view>
|
||
</view>
|
||
<view class="review">
|
||
<view class="r1 row between">
|
||
<view class="rn">顾客 B</view>
|
||
<view class="muted">4.8</view>
|
||
</view>
|
||
<view class="muted rt">店里环境很干净,技师很专业。</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="space"></view>
|
||
|
||
<view class="fixbar">
|
||
<view class="bar card">
|
||
<view class="btn btn-ghost a" @tap="book">立即预约</view>
|
||
<view class="btn btn-primary b" @tap="buy">立即购买</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<AiFloat />
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import AiFloat from '@/components/AiFloat.vue'
|
||
import { categories, projects } from '@/common/mockData'
|
||
|
||
const fallbackCategories = [
|
||
{ id: 'c1', name: '体验爆款' },
|
||
{ id: 'c2', name: '面部护理' },
|
||
{ id: 'c3', name: '身体养生' },
|
||
{ id: 'c4', name: '美甲美睫' },
|
||
{ id: 'c5', name: '特惠套餐' }
|
||
]
|
||
|
||
const fallbackProjects = [
|
||
{
|
||
id: 'p1',
|
||
categoryId: 'c1',
|
||
name: '水氧净透体验',
|
||
price: 99,
|
||
originPrice: 199,
|
||
durationMin: 60,
|
||
fitFor: '初次体验、暗沉、出油',
|
||
taboo: '近期激光/微针术后 7 天内不建议',
|
||
cover: '',
|
||
desc: '轻盈水氧 + 净透护理,适合快速提升肤感与通透度。'
|
||
},
|
||
{
|
||
id: 'p2',
|
||
categoryId: 'c2',
|
||
name: '深层清洁黑头管理',
|
||
price: 168,
|
||
originPrice: 268,
|
||
durationMin: 75,
|
||
fitFor: 'T 区油脂旺盛、黑头粉刺',
|
||
taboo: '炎症爆痘期需评估后进行',
|
||
cover: '',
|
||
desc: '清洁、舒缓、收敛三步走,减少反复出油与毛孔困扰。'
|
||
},
|
||
{
|
||
id: 'p3',
|
||
categoryId: 'c2',
|
||
name: '补水修护屏障护理',
|
||
price: 238,
|
||
originPrice: 368,
|
||
durationMin: 80,
|
||
fitFor: '敏感泛红、干燥紧绷',
|
||
taboo: '过敏急性期请先咨询',
|
||
cover: '',
|
||
desc: '修护屏障与舒缓敏感,适合换季与长期干燥人群。'
|
||
},
|
||
{
|
||
id: 'p4',
|
||
categoryId: 'c3',
|
||
name: '肩颈舒缓筋膜放松',
|
||
price: 188,
|
||
originPrice: 288,
|
||
durationMin: 60,
|
||
fitFor: '久坐办公、肩颈僵硬',
|
||
taboo: '急性损伤与发热期不建议',
|
||
cover: '',
|
||
desc: '深度放松肌群与筋膜,改善紧绷与酸胀。'
|
||
},
|
||
{
|
||
id: 'p5',
|
||
categoryId: 'c5',
|
||
name: '皮肤管理次卡 5 次',
|
||
price: 899,
|
||
originPrice: 1199,
|
||
durationMin: 60,
|
||
fitFor: '长期管理、复购人群',
|
||
taboo: '具体项目以到店评估为准',
|
||
cover: '',
|
||
desc: '灵活使用,随时预约,到店核销自动扣次。'
|
||
},
|
||
{
|
||
id: 'p6',
|
||
categoryId: 'c4',
|
||
name: '轻奢美甲 · 单色',
|
||
price: 168,
|
||
originPrice: 238,
|
||
durationMin: 75,
|
||
fitFor: '通勤、日常、显白',
|
||
taboo: '甲面破损/感染需先处理',
|
||
cover: '',
|
||
desc: '干净利落的通勤单色,显白耐看,可按肤色搭配色卡。'
|
||
},
|
||
{
|
||
id: 'p7',
|
||
categoryId: 'c4',
|
||
name: '自然单根美睫 · 清透款',
|
||
price: 198,
|
||
originPrice: 298,
|
||
durationMin: 90,
|
||
fitFor: '自然放大双眼、日常耐看',
|
||
taboo: '眼部炎症/过敏期不建议',
|
||
cover: '',
|
||
desc: '清透自然的单根嫁接,整体更轻盈,适合新手与通勤。'
|
||
},
|
||
{
|
||
id: 'p8',
|
||
categoryId: 'c3',
|
||
name: '全身精油舒压 · 90 分钟',
|
||
price: 298,
|
||
originPrice: 398,
|
||
durationMin: 90,
|
||
fitFor: '压力大、睡眠欠佳、疲劳',
|
||
taboo: '孕期/发热/急性炎症期不建议',
|
||
cover: '',
|
||
desc: '精油舒压与深度放松结合,帮助缓解疲劳与紧绷。'
|
||
},
|
||
{
|
||
id: 'p9',
|
||
categoryId: 'c5',
|
||
name: '新客体验套餐 · 3 次',
|
||
price: 299,
|
||
originPrice: 499,
|
||
durationMin: 60,
|
||
fitFor: '初次体验、想要快速改善肤感',
|
||
taboo: '具体项目以到店评估为准',
|
||
cover: '',
|
||
desc: '高性价比新客套餐,适合建立基础皮肤管理节奏。'
|
||
}
|
||
]
|
||
|
||
const runtimeProjects = Array.isArray(projects) && projects.length ? projects : fallbackProjects
|
||
const runtimeCategories = Array.isArray(categories) && categories.length ? categories : fallbackCategories
|
||
|
||
const detailPreset = {
|
||
p1: {
|
||
rating: 5.0,
|
||
effects: ['净透提亮肤感', '补水保湿', '舒缓修护'],
|
||
steps: ['洁面清洁', '水氧净透护理', '精华导入', '舒缓收尾与防护']
|
||
},
|
||
p2: {
|
||
rating: 4.8,
|
||
effects: ['减少黑头粉刺困扰', '清理油脂与角质', '舒缓收敛毛孔观感'],
|
||
steps: ['皮肤评估与卸妆', '深层清洁与导出', '舒缓镇定', '收尾修护']
|
||
},
|
||
p3: {
|
||
rating: 4.9,
|
||
effects: ['补水修护屏障', '舒缓敏感泛红', '改善干燥紧绷'],
|
||
steps: ['温和清洁', '舒缓修护导入', '补水面膜', '收尾锁水防护']
|
||
},
|
||
p4: {
|
||
rating: 4.8,
|
||
effects: ['放松肩颈肌群', '改善紧绷酸胀', '提升舒适度与精神状态'],
|
||
steps: ['热敷放松', '筋膜松解', '肩颈重点放松', '收尾舒缓']
|
||
},
|
||
p5: {
|
||
rating: 4.9,
|
||
effects: ['长期皮肤管理更划算', '支持随时预约使用', '到店核销自动扣次'],
|
||
steps: ['购买次卡', '在线预约', '到店出示核销码', '门店核销扣次']
|
||
}
|
||
}
|
||
|
||
export default {
|
||
components: { AiFloat },
|
||
data() {
|
||
return {
|
||
id: '',
|
||
project: runtimeProjects[0] || null,
|
||
rating: 4.9,
|
||
effects: ['效果以到店评估为准', '体验舒适、过程规范', '可按肤质做个性化调整'],
|
||
steps: ['到店评估', '护理服务', '舒缓收尾', '给出居家建议'],
|
||
categoryName: runtimeCategories[0]?.name || '项目'
|
||
}
|
||
},
|
||
computed: {
|
||
p() {
|
||
return (
|
||
this.project ||
|
||
runtimeProjects[0] ||
|
||
fallbackProjects[0] || {
|
||
id: 'p1',
|
||
categoryId: 'c1',
|
||
name: '水氧净透体验',
|
||
price: 99,
|
||
originPrice: 199,
|
||
durationMin: 60,
|
||
fitFor: '初次体验、暗沉、出油',
|
||
taboo: '近期激光/微针术后 7 天内不建议',
|
||
cover: '',
|
||
desc: '轻盈水氧 + 净透护理,适合快速提升肤感与通透度。'
|
||
}
|
||
)
|
||
},
|
||
displayProject() {
|
||
return this.project || runtimeProjects[0] || null
|
||
}
|
||
},
|
||
onLoad(query) {
|
||
this.id = query.id || query.projectId || ''
|
||
if (!this.id && runtimeProjects.length) this.id = runtimeProjects[0].id
|
||
this.project = runtimeProjects.find((x) => x.id === this.id) || runtimeProjects[0] || null
|
||
const cat = this.p ? runtimeCategories.find((c) => c.id === this.p.categoryId) : null
|
||
this.categoryName = cat ? cat.name : '项目'
|
||
const p = detailPreset[this.id] || null
|
||
this.rating = p ? p.rating : 4.9
|
||
this.effects = p ? p.effects : ['效果以到店评估为准', '体验舒适、过程规范', '可按肤质做个性化调整']
|
||
this.steps = p ? p.steps : ['到店评估', '护理服务', '舒缓收尾', '给出居家建议']
|
||
},
|
||
methods: {
|
||
book() {
|
||
uni.navigateTo({ url: `/pages/booking/create?projectId=${this.p.id}` })
|
||
},
|
||
buy() {
|
||
uni.navigateTo({ url: `/pages/order/confirm?type=coupon&projectId=${this.p.id}` })
|
||
},
|
||
goList() {
|
||
uni.switchTab({ url: '/pages/projects/list' })
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.head {
|
||
padding: 26rpx;
|
||
}
|
||
.cover {
|
||
width: 100%;
|
||
height: 320rpx;
|
||
border-radius: 18rpx;
|
||
margin-bottom: 18rpx;
|
||
}
|
||
.name {
|
||
font-size: 42rpx;
|
||
font-weight: 950;
|
||
}
|
||
.meta {
|
||
margin-top: 14rpx;
|
||
}
|
||
.meta2 {
|
||
margin-top: 12rpx;
|
||
}
|
||
.price {
|
||
display: flex;
|
||
align-items: baseline;
|
||
}
|
||
.yen {
|
||
font-size: 24rpx;
|
||
opacity: 0.8;
|
||
}
|
||
.num {
|
||
font-size: 46rpx;
|
||
font-weight: 950;
|
||
}
|
||
.ori {
|
||
margin-left: 10rpx;
|
||
font-size: 24rpx;
|
||
text-decoration: line-through;
|
||
}
|
||
.tag2 {
|
||
padding: 10rpx 14rpx;
|
||
border-radius: 999rpx;
|
||
font-size: 24rpx;
|
||
background: rgba(17, 24, 39, 0.06);
|
||
font-weight: 900;
|
||
}
|
||
.tags {
|
||
margin-top: 18rpx;
|
||
gap: 12rpx;
|
||
flex-wrap: wrap;
|
||
}
|
||
.tag {
|
||
padding: 10rpx 14rpx;
|
||
border-radius: 999rpx;
|
||
font-size: 24rpx;
|
||
background: rgba(17, 24, 39, 0.06);
|
||
color: #111827;
|
||
}
|
||
.warn {
|
||
background: rgba(245, 158, 11, 0.14);
|
||
color: #b45309;
|
||
}
|
||
.block {
|
||
margin-top: 18rpx;
|
||
padding: 22rpx;
|
||
}
|
||
.b-t {
|
||
font-weight: 950;
|
||
font-size: 30rpx;
|
||
}
|
||
.b-v {
|
||
margin-top: 12rpx;
|
||
font-size: 26rpx;
|
||
}
|
||
.review {
|
||
margin-top: 16rpx;
|
||
padding-top: 16rpx;
|
||
border-top: 1rpx solid rgba(17, 24, 39, 0.08);
|
||
}
|
||
.review:first-of-type {
|
||
border-top: 0;
|
||
padding-top: 0;
|
||
}
|
||
.r1 {
|
||
margin-bottom: 8rpx;
|
||
}
|
||
.rn {
|
||
font-size: 28rpx;
|
||
font-weight: 900;
|
||
}
|
||
.rt {
|
||
font-size: 26rpx;
|
||
}
|
||
.space {
|
||
height: 150rpx;
|
||
}
|
||
.empty {
|
||
margin-top: 18rpx;
|
||
padding: 34rpx 26rpx;
|
||
text-align: center;
|
||
}
|
||
.en {
|
||
font-size: 36rpx;
|
||
font-weight: 950;
|
||
}
|
||
.et {
|
||
margin-top: 12rpx;
|
||
font-size: 26rpx;
|
||
}
|
||
.eb {
|
||
margin-top: 20rpx;
|
||
}
|
||
.fixbar {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
padding: 18rpx 24rpx 24rpx;
|
||
background: linear-gradient(180deg, rgba(246, 247, 251, 0) 0%, rgba(246, 247, 251, 1) 46%);
|
||
}
|
||
.bar {
|
||
padding: 18rpx;
|
||
display: flex;
|
||
gap: 14rpx;
|
||
}
|
||
.a {
|
||
flex: 1;
|
||
height: 82rpx;
|
||
}
|
||
.b {
|
||
flex: 1;
|
||
height: 82rpx;
|
||
}
|
||
</style>
|
||
|