191 lines
4.5 KiB
Vue
191 lines
4.5 KiB
Vue
<template>
|
|
<view class="page">
|
|
<scroll-view class="msgs" scroll-y :scroll-top="scrollTop">
|
|
<view class="container">
|
|
<view v-for="m in messages" :key="m.id" class="m" :class="{ me: m.role === 'user' }">
|
|
<view class="bubble" :class="{ bme: m.role === 'user' }">
|
|
<text class="txt">{{ m.text }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="chips" v-if="showChips">
|
|
<view class="chip" @tap="send('推荐爆款项目')">推荐爆款项目</view>
|
|
<view class="chip" @tap="send('我想预约今天')">我想预约今天</view>
|
|
<view class="chip" @tap="send('敏感肌适合做什么')">敏感肌适合做什么</view>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
|
|
<view class="quick row">
|
|
<view class="q" @tap="send('查档期')">查档期</view>
|
|
<view class="q" @tap="send('看价格')">看价格</view>
|
|
<view class="q" @tap="send('我要预约')">我要预约</view>
|
|
</view>
|
|
|
|
<view class="bar">
|
|
<view class="voice" @tap="mockVoice">按住说话</view>
|
|
<input class="ipt" v-model="text" confirm-type="send" @confirm="send(text)" placeholder="说出你的需求,例如:想做补水/我想预约今天 14:30" />
|
|
<view class="send" @tap="send(text)">发送</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { aiReply } from '@/common/aiRules'
|
|
import { projects } from '@/common/mockData'
|
|
|
|
function mid() {
|
|
return `${Date.now()}_${Math.floor(Math.random() * 10000)}`
|
|
}
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
projectId: '',
|
|
messages: [],
|
|
text: '',
|
|
scrollTop: 0
|
|
}
|
|
},
|
|
computed: {
|
|
showChips() {
|
|
return this.messages.length < 4
|
|
}
|
|
},
|
|
onLoad(query) {
|
|
this.projectId = query.projectId || ''
|
|
const base =
|
|
'我是 AI 预约助手。你可以直接说“预约 + 时间 + 项目/需求”,我会把你带到预约/购买流程。'
|
|
const ctx = this.projectId ? projects.find((x) => x.id === this.projectId) : null
|
|
const intro = ctx ? `当前项目:${ctx.name}。你想预约哪个日期和时段?` : base
|
|
this.messages = [
|
|
{ id: mid(), role: 'assistant', text: intro }
|
|
]
|
|
this.bump()
|
|
},
|
|
methods: {
|
|
bump() {
|
|
this.$nextTick(() => {
|
|
this.scrollTop = this.scrollTop + 99999
|
|
})
|
|
},
|
|
send(raw) {
|
|
const v = (raw || '').trim()
|
|
if (!v) return
|
|
this.messages.push({ id: mid(), role: 'user', text: v })
|
|
this.text = ''
|
|
|
|
const r = aiReply(v)
|
|
this.messages.push({ id: mid(), role: 'assistant', text: r.text })
|
|
this.bump()
|
|
|
|
if (r.action?.type === 'go_booking') {
|
|
setTimeout(() => {
|
|
uni.navigateTo({ url: `/pages/booking/create?projectId=${r.action.projectId}` })
|
|
}, 450)
|
|
}
|
|
},
|
|
mockVoice() {
|
|
uni.showToast({ title: '语音输入(原型演示)', icon: 'none' })
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.page {
|
|
height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.msgs {
|
|
flex: 1;
|
|
}
|
|
.m {
|
|
display: flex;
|
|
margin: 14rpx 0;
|
|
}
|
|
.me {
|
|
justify-content: flex-end;
|
|
}
|
|
.bubble {
|
|
max-width: 620rpx;
|
|
padding: 18rpx 18rpx;
|
|
border-radius: 22rpx;
|
|
background: #fff;
|
|
border: 1rpx solid rgba(17, 24, 39, 0.08);
|
|
box-shadow: 0 10rpx 28rpx rgba(17, 24, 39, 0.06);
|
|
}
|
|
.bme {
|
|
background: rgba(59, 130, 246, 0.12);
|
|
border-color: rgba(59, 130, 246, 0.22);
|
|
}
|
|
.txt {
|
|
font-size: 28rpx;
|
|
line-height: 1.5;
|
|
}
|
|
.quick {
|
|
padding: 12rpx 18rpx 6rpx;
|
|
background: #fff;
|
|
border-top: 1rpx solid rgba(17, 24, 39, 0.08);
|
|
gap: 12rpx;
|
|
}
|
|
.q {
|
|
padding: 12rpx 16rpx;
|
|
border-radius: 999rpx;
|
|
background: rgba(17, 24, 39, 0.06);
|
|
font-size: 26rpx;
|
|
font-weight: 800;
|
|
}
|
|
.bar {
|
|
padding: 18rpx;
|
|
background: #fff;
|
|
display: flex;
|
|
gap: 12rpx;
|
|
}
|
|
.voice {
|
|
width: 160rpx;
|
|
height: 82rpx;
|
|
border-radius: 18rpx;
|
|
background: rgba(17, 24, 39, 0.06);
|
|
font-size: 24rpx;
|
|
font-weight: 900;
|
|
color: rgba(17, 24, 39, 0.78);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.ipt {
|
|
flex: 1;
|
|
height: 82rpx;
|
|
padding: 0 16rpx;
|
|
border-radius: 18rpx;
|
|
background: rgba(17, 24, 39, 0.05);
|
|
font-size: 28rpx;
|
|
}
|
|
.send {
|
|
width: 140rpx;
|
|
height: 82rpx;
|
|
border-radius: 18rpx;
|
|
background: linear-gradient(135deg, #111827 0%, #3b82f6 100%);
|
|
color: #fff;
|
|
font-weight: 900;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.chips {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 12rpx;
|
|
margin: 8rpx 0 12rpx;
|
|
}
|
|
.chip {
|
|
padding: 12rpx 16rpx;
|
|
border-radius: 999rpx;
|
|
background: rgba(17, 24, 39, 0.06);
|
|
font-size: 26rpx;
|
|
font-weight: 700;
|
|
}
|
|
</style>
|
|
|