Files
vueapp/pages/chat/chat.vue
T
2026-05-29 17:40:10 +08:00

822 lines
22 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>
<DiagnosisPage
v-if="showDiagnosisPage"
:case-item="caseItem"
@open-settings="emit('open-settings')"
@open-profile="emit('open-profile')"
@go-home="emit('go-home')"
/>
<view v-else class="chat-page">
<view class="chat-shell">
<view class="top-nav">
<button class="icon-button" aria-label="设置" @click="emit('open-settings')">
<view class="settings-icon"></view>
</button>
<button class="icon-button home-button" aria-label="首页" @click="emit('go-home')">
<view class="home-icon"></view>
</button>
<view class="nav-spacer"></view>
<button class="icon-button" aria-label="个人中心" @click="emit('open-profile')">
<view class="account-icon"></view>
</button>
</view>
<view class="case-header">
<view class="case-title-row">
<text class="case-heading">患者{{ session.patient.name }} ({{ complaintShort }})</text>
<button class="finish-button" @click="showDiagnosisPage = true">
<view class="check-icon"></view>
<text>完成采集</text>
</button>
</view>
<view class="patient-meta">
<text>姓名{{ session.patient.name }}</text>
<text>性别{{ session.patient.gender }}</text>
<text>年龄{{ session.patient.age }}</text>
<text>科室{{ session.patient.department }}</text>
</view>
</view>
<view class="stage-bar">
<view class="stage-line"></view>
<view class="stage-line-active"></view>
<view
v-for="stage in session.stages"
:key="stage.key"
class="stage-item"
:class="{ active: stage.active }"
>
<view class="stage-dot">
<view class="stage-icon" :class="`stage-icon-${stage.key}`"></view>
</view>
<text>{{ stage.label }}</text>
</view>
</view>
<scroll-view class="chat-body" scroll-y :scroll-top="scrollTop">
<view class="message-list">
<view
v-for="message in session.messages"
:key="message.id"
class="message-row"
:class="[`role-${message.role}`]"
>
<view v-if="message.role === 'patient'" class="avatar patient-avatar">
<text>{{ session.patient.name.slice(0, 1) }}</text>
</view>
<view class="message-bubble" :class="`${message.role}-bubble`">
<text class="message-content">"{{ message.content }}"</text>
<view class="message-label" :class="{ mentor: message.role === 'mentor' }">
<view v-if="message.role === 'mentor'" class="star-icon"></view>
<text>{{ message.label }}</text>
</view>
</view>
<view v-if="message.role === 'doctor'" class="avatar doctor-avatar">
<view class="person-icon"></view>
</view>
</view>
</view>
</scroll-view>
<view class="mentor-float">
<text class="mentor-badge">王主任</text>
<view class="mentor-avatar">
<image src="/static/config-doctor.png" mode="aspectFill"></image>
</view>
</view>
<view class="input-panel">
<scroll-view class="quick-actions" scroll-x>
<view class="quick-row">
<button class="quick-chip" @click="sendQuickAction('体格检查')">体格检查</button>
<button class="quick-chip" @click="sendQuickAction('辅助检查')">辅助检查</button>
</view>
</scroll-view>
<view class="input-row">
<input
class="chat-input"
v-model="draft"
type="text"
placeholder="输入你对患者的提问..."
placeholder-class="input-placeholder"
@confirm="handleSend"
/>
<button class="send-button" :disabled="sending" @click="handleSend">
<view class="send-icon"></view>
</button>
</view>
</view>
</view>
<view class="toast" :class="{ visible: toastVisible }">{{ toastMessage }}</view>
</view>
</template>
<script setup lang="ts">
import { computed, onMounted, onUnmounted, reactive, ref } from 'vue'
import type { ClinicalCase } from '../../api/cases'
import { createMockChatSession, sendMockChatMessage, type ChatSession } from '../../api/chat'
import DiagnosisPage from '../diagnosis/diagnosis.vue'
const props = defineProps<{
caseItem: ClinicalCase | null
}>()
const emit = defineEmits<{
(event: 'open-settings'): void
(event: 'open-profile'): void
(event: 'go-home'): void
}>()
const session = reactive<ChatSession>({
patient: {
name: '陈先生',
gender: '男',
age: 60,
department: '心血管内科',
chiefComplaint: '持续胸痛3小时'
},
stages: [
{ key: 'history', label: '病史采集', active: true },
{ key: 'diagnosis', label: '初步诊断', active: false },
{ key: 'treatment', label: '治疗方案', active: false }
],
messages: []
})
const draft = ref('')
const sending = ref(false)
const scrollTop = ref(0)
const toastMessage = ref('')
const toastVisible = ref(false)
const showDiagnosisPage = ref(false)
let toastTimer: ReturnType<typeof setTimeout> | null = null
const complaintShort = computed(() => {
if (session.patient.chiefComplaint.includes('胸痛')) return '胸痛'
return session.patient.chiefComplaint.slice(0, 6)
})
function loadSession() {
createMockChatSession(props.caseItem).then(result => {
Object.assign(session.patient, result.patient)
session.stages = result.stages
session.messages = result.messages
scrollToBottom()
})
}
function sendQuickAction(content: string) {
draft.value = content
handleSend()
}
function handleSend() {
const content = draft.value.trim()
if (!content || sending.value) return
sending.value = true
draft.value = ''
sendMockChatMessage(content).then(messages => {
session.messages.push(...messages)
scrollToBottom()
}).finally(() => {
sending.value = false
})
}
function scrollToBottom() {
setTimeout(() => {
scrollTop.value += 1000
}, 60)
}
function showToast(message: string) {
if (toastTimer) clearTimeout(toastTimer)
toastMessage.value = message
toastVisible.value = true
uni.showToast({
title: message,
icon: 'none'
})
toastTimer = setTimeout(() => {
toastVisible.value = false
}, 2200)
}
onMounted(loadSession)
onUnmounted(() => {
if (toastTimer) clearTimeout(toastTimer)
})
</script>
<style scoped>
page {
min-height: 100%;
background: #f9f9ff;
}
.chat-page {
min-height: 100vh;
background: #f9f9ff;
color: #191c21;
font-family: Inter, -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
-webkit-tap-highlight-color: transparent;
}
.chat-page view,
.chat-page text,
.chat-page button,
.chat-page input,
.chat-page scroll-view {
box-sizing: border-box;
}
.chat-shell {
position: relative;
height: 100vh;
overflow: hidden;
background: #f9f9ff;
display: flex;
flex-direction: column;
}
.top-nav {
position: fixed;
left: 0;
right: 0;
top: 0;
z-index: 50;
box-sizing: border-box;
height: 56px;
padding: 0 20px;
border-bottom: 1px solid rgba(194, 198, 212, 0.3);
background: #ffffff;
box-shadow: 0 2px 8px rgba(25, 28, 33, 0.04);
display: flex;
align-items: center;
}
.nav-spacer {
flex: 1;
}
.icon-button {
width: 40px;
height: 40px;
padding: 0;
border-radius: 50%;
background: transparent;
display: flex;
align-items: center;
justify-content: center;
}
.home-button {
margin-left: 4px;
}
.icon-button::after,
.finish-button::after,
.quick-chip::after,
.send-button::after {
border: 0;
}
.icon-button:active {
background: rgba(25, 28, 33, 0.05);
}
.settings-icon,
.home-icon,
.account-icon,
.check-icon,
.person-icon,
.star-icon,
.send-icon {
background: #424752;
}
.settings-icon {
width: 22px;
height: 22px;
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M19.43%2012.98c.04-.32.07-.65.07-.98s-.02-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.37-.31-.6-.22l-2.49%201c-.52-.4-1.08-.73-1.69-.98L14.5%202.42C14.47%202.18%2014.25%202%2014%202h-4c-.25%200-.46.18-.5.42l-.38%202.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.08-.48%200-.6.22l-2%203.46c-.13.22-.07.49.12.64l2.11%201.65c-.04.32-.08.65-.08.98s.03.66.08.98l-2.11%201.65c-.19.15-.24.42-.12.64l2%203.46c.12.22.37.31.6.22l2.49-1c.52.4%201.08.73%201.69.98l.38%202.65c.04.24.25.42.5.42h4c.25%200%20.46-.18.5-.42l.38-2.65c.61-.25%201.17-.58%201.69-.98l2.49%201c.23.08.48%200%20.6-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12%2015.5A3.5%203.5%200%201%201%2012%208a3.5%203.5%200%200%201%200%207.5z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M19.43%2012.98c.04-.32.07-.65.07-.98s-.02-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.37-.31-.6-.22l-2.49%201c-.52-.4-1.08-.73-1.69-.98L14.5%202.42C14.47%202.18%2014.25%202%2014%202h-4c-.25%200-.46.18-.5.42l-.38%202.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.08-.48%200-.6.22l-2%203.46c-.13.22-.07.49.12.64l2.11%201.65c-.04.32-.08.65-.08.98s.03.66.08.98l-2.11%201.65c-.19.15-.24.42-.12.64l2%203.46c.12.22.37.31.6.22l2.49-1c.52.4%201.08.73%201.69.98l.38%202.65c.04.24.25.42.5.42h4c.25%200%20.46-.18.5-.42l.38-2.65c.61-.25%201.17-.58%201.69-.98l2.49%201c.23.08.48%200%20.6-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12%2015.5A3.5%203.5%200%201%201%2012%208a3.5%203.5%200%200%201%200%207.5z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.home-icon {
width: 23px;
height: 23px;
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M10%2020v-6h4v6h5v-8h3L12%203%202%2012h3v8h5z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M10%2020v-6h4v6h5v-8h3L12%203%202%2012h3v8h5z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.account-icon {
width: 24px;
height: 24px;
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%202a10%2010%200%201%200%200%2020%2010%2010%200%200%200%200-20zm0%203a3.5%203.5%200%201%201%200%207%203.5%203.5%200%200%201%200-7zm0%2015a8%208%200%200%201-6.4-3.2c1.18-2.02%203.57-3.3%206.4-3.3s5.22%201.28%206.4%203.3A8%208%200%200%201%2012%2020z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%202a10%2010%200%201%200%200%2020%2010%2010%200%200%200%200-20zm0%203a3.5%203.5%200%201%201%200%207%203.5%203.5%200%200%201%200-7zm0%2015a8%208%200%200%201-6.4-3.2c1.18-2.02%203.57-3.3%206.4-3.3s5.22%201.28%206.4%203.3A8%208%200%200%201%2012%2020z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.case-header {
position: fixed;
left: 0;
right: 0;
top: 56px;
z-index: 40;
box-sizing: border-box;
height: 100px;
padding: 16px 20px;
background: #f9f9ff;
box-shadow: 0 2px 8px rgba(25, 28, 33, 0.04);
}
.case-title-row {
position: relative;
height: 32px;
display: block;
}
.case-heading {
display: block;
box-sizing: border-box;
width: 100%;
padding-right: 116px;
color: #191c21;
font-size: 18px;
line-height: 28px;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.finish-button {
position: absolute;
right: 0;
top: 0;
z-index: 2;
width: 112px;
height: 32px;
min-height: 32px;
margin: 0;
padding: 0 14px;
border: 0;
border-radius: 999px;
background: #00478d;
box-shadow: 0 4px 10px rgba(0, 71, 141, 0.22);
color: #ffffff;
font-size: 14px;
line-height: 20px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
}
.finish-button text {
white-space: nowrap;
}
.check-icon {
width: 18px;
height: 18px;
background: #ffffff;
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%202a10%2010%200%201%200%200%2020%2010%2010%200%200%200%200-20zm-1.2%2014.4-4-4L8.2%2011l2.6%202.6L16.8%207.6%2018.2%209l-7.4%207.4z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%202a10%2010%200%201%200%200%2020%2010%2010%200%200%200%200-20zm-1.2%2014.4-4-4L8.2%2011l2.6%202.6L16.8%207.6%2018.2%209l-7.4%207.4z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.patient-meta {
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid rgba(194, 198, 212, 0.3);
display: flex;
flex-wrap: wrap;
column-gap: 24px;
row-gap: 4px;
color: #424752;
font-size: 13px;
line-height: 20px;
}
.stage-bar {
position: fixed;
left: 0;
right: 0;
top: 156px;
z-index: 30;
box-sizing: border-box;
height: 72px;
padding: 8px 24px;
border-bottom: 1px solid rgba(194, 198, 212, 0.3);
background: #ffffff;
display: flex;
align-items: center;
justify-content: space-between;
}
.stage-line,
.stage-line-active {
position: absolute;
left: 24px;
right: 24px;
top: 28px;
height: 2px;
background: #e1e2ea;
}
.stage-line-active {
right: auto;
width: 10%;
background: #00478d;
}
.stage-item {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
color: #c2c6d4;
font-size: 11px;
line-height: 16px;
}
.stage-item.active {
color: #00478d;
font-weight: 700;
}
.stage-dot {
width: 24px;
height: 24px;
border-radius: 50%;
background: #e1e2ea;
display: flex;
align-items: center;
justify-content: center;
}
.stage-item.active .stage-dot {
background: #00478d;
box-shadow: 0 0 0 4px rgba(200, 218, 255, 0.5);
}
.stage-icon {
width: 14px;
height: 14px;
background: currentColor;
}
.stage-icon-history {
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M19%203H5a2%202%200%200%200-2%202v14a2%202%200%200%200%202%202h14a2%202%200%200%200%202-2V5a2%202%200%200%200-2-2zm-7%203a3%203%200%201%201%200%206%203%203%200%200%201%200-6zm6%2012H6v-1.2c0-2%204-3.1%206-3.1s6%201.1%206%203.1V18z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M19%203H5a2%202%200%200%200-2%202v14a2%202%200%200%200%202%202h14a2%202%200%200%200%202-2V5a2%202%200%200%200-2-2zm-7%203a3%203%200%201%201%200%206%203%203%200%200%201%200-6zm6%2012H6v-1.2c0-2%204-3.1%206-3.1s6%201.1%206%203.1V18z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.stage-icon-diagnosis {
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M19%203h-1V1h-2v2H8V1H6v2H5a2%202%200%200%200-2%202v14a2%202%200%200%200%202%202h14a2%202%200%200%200%202-2V5a2%202%200%200%200-2-2zM8%207h8v2H8V7zm0%204h8v2H8v-2zm0%204h5v2H8v-2z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M19%203h-1V1h-2v2H8V1H6v2H5a2%202%200%200%200-2%202v14a2%202%200%200%200%202%202h14a2%202%200%200%200%202-2V5a2%202%200%200%200-2-2zM8%207h8v2H8V7zm0%204h8v2H8v-2zm0%204h5v2H8v-2z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.stage-icon-treatment {
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='m4.22%2019.78-.01-.01a5.5%205.5%200%200%201%200-7.78L12%204.22a5.5%205.5%200%200%201%207.78%207.78L12%2019.78a5.5%205.5%200%200%201-7.78%200zM13.41%205.64%2010%209.05%2014.95%2014%2018.36%2010.59a3.5%203.5%200%200%200-4.95-4.95zM5.64%2013.41a3.5%203.5%200%200%200%204.95%204.95L14%2014.95%209.05%2010%205.64%2013.41z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='m4.22%2019.78-.01-.01a5.5%205.5%200%200%201%200-7.78L12%204.22a5.5%205.5%200%200%201%207.78%207.78L12%2019.78a5.5%205.5%200%200%201-7.78%200zM13.41%205.64%2010%209.05%2014.95%2014%2018.36%2010.59a3.5%203.5%200%200%200-4.95-4.95zM5.64%2013.41a3.5%203.5%200%200%200%204.95%204.95L14%2014.95%209.05%2010%205.64%2013.41z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.stage-item.active .stage-icon {
background: #ffffff;
}
.chat-body {
box-sizing: border-box;
height: 100vh;
padding: 244px 20px 188px;
}
.message-list {
display: flex;
flex-direction: column;
gap: 24px;
}
.message-row {
display: flex;
align-items: flex-start;
gap: 16px;
max-width: 85%;
}
.role-mentor {
align-self: flex-end;
}
.role-doctor {
align-self: flex-end;
flex-direction: row-reverse;
}
.avatar {
flex: 0 0 auto;
width: 40px;
height: 40px;
border: 1px solid #c2c6d4;
border-radius: 50%;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.patient-avatar {
background: linear-gradient(135deg, #ffb691, #9f4300);
color: #ffffff;
font-size: 18px;
font-weight: 700;
}
.doctor-avatar {
background: #7af1fc;
}
.person-icon {
width: 24px;
height: 24px;
background: #006e75;
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%202a10%2010%200%201%200%200%2020%2010%2010%200%200%200%200-20zm0%203a3.5%203.5%200%201%201%200%207%203.5%203.5%200%200%201%200-7zm0%2015a8%208%200%200%201-6.4-3.2c1.18-2.02%203.57-3.3%206.4-3.3s5.22%201.28%206.4%203.3A8%208%200%200%201%2012%2020z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M12%202a10%2010%200%201%200%200%2020%2010%2010%200%200%200%200-20zm0%203a3.5%203.5%200%201%201%200%207%203.5%203.5%200%200%201%200-7zm0%2015a8%208%200%200%201-6.4-3.2c1.18-2.02%203.57-3.3%206.4-3.3s5.22%201.28%206.4%203.3A8%208%200%200%201%2012%2020z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.message-bubble {
position: relative;
padding: 16px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(25, 28, 33, 0.04);
}
.message-bubble::after {
content: '';
position: absolute;
top: 12px;
width: 14px;
height: 14px;
transform: rotate(45deg);
}
.patient-bubble {
border: 1px solid #c2c6d4;
background: #f9f9ff;
}
.patient-bubble::after {
left: -8px;
border-left: 1px solid #c2c6d4;
border-bottom: 1px solid #c2c6d4;
background: #f9f9ff;
}
.mentor-bubble {
border: 1px solid rgba(0, 71, 141, 0.2);
background: #e7e8f0;
}
.mentor-bubble::after {
right: -8px;
border-right: 1px solid rgba(0, 71, 141, 0.2);
border-top: 1px solid rgba(0, 71, 141, 0.2);
background: #e7e8f0;
}
.doctor-bubble {
border: 1px solid rgba(0, 71, 141, 0.1);
background: #005eb8;
}
.doctor-bubble::after {
right: -8px;
background: #005eb8;
}
.message-content {
color: #191c21;
font-size: 16px;
line-height: 24px;
}
.mentor-bubble .message-content {
color: #00478d;
font-weight: 600;
font-style: italic;
}
.doctor-bubble .message-content {
color: #c8daff;
}
.message-label {
margin-top: 8px;
color: #727783;
font-size: 12px;
line-height: 16px;
font-weight: 500;
letter-spacing: 0;
text-transform: uppercase;
display: flex;
align-items: center;
gap: 4px;
}
.message-label.mentor {
color: #00478d;
font-weight: 700;
}
.doctor-bubble .message-label {
justify-content: flex-end;
color: rgba(200, 218, 255, 0.72);
}
.star-icon {
width: 16px;
height: 16px;
background: #00478d;
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='m12%202%202.7%206.6%207.1.6-5.4%204.6%201.7%206.9L12%2017l-6.1%203.7%201.7-6.9-5.4-4.6%207.1-.6L12%202z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='m12%202%202.7%206.6%207.1.6-5.4%204.6%201.7%206.9L12%2017l-6.1%203.7%201.7-6.9-5.4-4.6%207.1-.6L12%202z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.mentor-float {
position: fixed;
right: 20px;
bottom: 140px;
z-index: 45;
display: flex;
flex-direction: column;
align-items: center;
pointer-events: none;
}
.mentor-badge {
margin-bottom: 8px;
padding: 4px 12px;
border-radius: 999px;
background: #00478d;
box-shadow: 0 3px 8px rgba(0, 71, 141, 0.22);
color: #ffffff;
font-size: 12px;
line-height: 16px;
font-weight: 700;
animation: pulse-soft 1.6s ease-in-out infinite;
}
.mentor-avatar {
width: 96px;
height: 96px;
border: 2px solid #00478d;
border-radius: 50%;
box-shadow: 0 8px 18px rgba(25, 28, 33, 0.18);
overflow: hidden;
background: #ffffff;
animation: breathe 3s ease-in-out infinite;
}
.mentor-avatar image {
width: 100%;
height: 100%;
}
.input-panel {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 50;
box-sizing: border-box;
padding: 12px;
border-top: 1px solid #c2c6d4;
background: #f9f9ff;
display: flex;
flex-direction: column;
gap: 16px;
}
.quick-actions {
width: 100%;
white-space: nowrap;
}
.quick-row {
display: flex;
gap: 8px;
}
.quick-chip {
flex: 0 0 auto;
padding: 8px 16px;
border: 1px solid #006970;
border-radius: 999px;
background: #ffffff;
color: #006970;
font-size: 14px;
line-height: 20px;
font-weight: 600;
}
.input-row {
min-height: 48px;
padding: 4px;
border: 1px solid #c2c6d4;
border-radius: 12px;
background: #ecedf6;
display: flex;
align-items: center;
gap: 8px;
}
.chat-input {
flex: 1;
min-width: 0;
height: 40px;
padding: 0 8px;
border: 0;
background: transparent;
color: #191c21;
font-size: 16px;
line-height: 24px;
}
.input-placeholder {
color: #c2c6d4;
}
.send-button {
flex: 0 0 auto;
width: 40px;
height: 40px;
border-radius: 8px;
background: #00478d;
display: flex;
align-items: center;
justify-content: center;
}
.send-button:active {
transform: scale(0.95);
}
.send-icon {
width: 22px;
height: 22px;
background: #ffffff;
-webkit-mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M2%2021 23%2012 2%203v7l15%202-15%202v7z'/%3E%3C/svg%3E") center / contain no-repeat;
mask: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2024%2024'%20xmlns='http://www.w3.org/2000/svg'%3E%3Cpath%20d='M2%2021 23%2012 2%203v7l15%202-15%202v7z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.toast {
position: fixed;
left: 50%;
bottom: 148px;
z-index: 100;
max-width: 320px;
padding: 12px 24px;
border-radius: 999px;
background: #2e3037;
color: #eff0f8;
font-size: 14px;
line-height: 20px;
font-weight: 600;
text-align: center;
pointer-events: none;
opacity: 0;
transform: translate(-50%, 16px);
transition: opacity 0.3s ease, transform 0.3s ease;
}
.toast.visible {
opacity: 1;
transform: translate(-50%, 0);
}
@keyframes breathe {
0%,
100% {
transform: translateY(0) scale(1);
}
50% {
transform: translateY(-4px) scale(1.02);
}
}
@keyframes pulse-soft {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.72;
}
}
</style>