Files
beauty-miniapp-uni/pages/projects/detail.vue
T
2026-06-29 10:54:33 +08:00

429 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>