ai-c/pages/chat-detail/chat-detail.wxml
2026-02-02 18:21:32 +08:00

407 lines
17 KiB
Plaintext
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.

<!-- AI智能体聊天详情页面 - 基于Figma设计 -->
<view class="page-container">
<!-- 状态栏 -->
<view class="status-bar-area">
<view class="status-bar" style="height: {{statusBarHeight}}px;"></view>
</view>
<!-- 顶部导航栏 -->
<view class="nav-header" style="top: {{statusBarHeight}}px;">
<view class="nav-content">
<!-- 返回按钮 -->
<view class="nav-back" bindtap="onBack">
<image src="/images/icon-back-arrow.png" class="back-icon" mode="aspectFit"></image>
<text class="back-text">返回</text>
</view>
<!-- 中间角色信息 - 点击头像跳转到角色详情 -->
<view class="nav-center" bindtap="onGoToCharacterDetail">
<view class="nav-avatar-wrap">
<image wx:if="{{character.avatar}}" class="nav-avatar" src="{{character.avatar}}" mode="aspectFill"></image>
<view wx:else class="nav-avatar-placeholder">
<text>{{character.name[0] || 'AI'}}</text>
</view>
</view>
<text class="nav-name">{{character.name}}</text>
<view class="online-dot" wx:if="{{character.isOnline}}"></view>
</view>
<!-- 右侧占位,避免中间内容偏移 -->
<view class="nav-right-placeholder"></view>
</view>
</view>
<!-- 聊天内容区域 -->
<view class="chat-area-wrapper" style="top: {{navHeight + statusBarHeight}}px;" bindtap="onTapChatArea">
<scroll-view
scroll-y
class="chat-scroll"
scroll-into-view="{{scrollIntoView}}"
scroll-with-animation="{{true}}"
enhanced="{{true}}"
show-scrollbar="{{false}}"
bindscroll="onScroll"
scroll-top="{{scrollTop}}"
>
<!-- 加载更多提示 -->
<view class="load-more-hint" wx:if="{{hasMore && !isFirstLoad}}">
<view class="load-more-content" wx:if="{{loadingMore}}">
<view class="loading-spinner"></view>
<text>加载中...</text>
</view>
<text wx:else class="load-more-text">向上滑动加载更多</text>
</view>
<view class="no-more-hint" wx:if="{{!hasMore && messages.length > 20}}">
<text>没有更多消息了</text>
</view>
<!-- 加密对话提示 -->
<view class="encrypt-hint">
<text>与 {{character.name}} 的加密对话</text>
</view>
<!-- 聊天消息列表 -->
<view class="chat-list">
<view
class="chat-item {{item.isMe ? 'me' : 'other'}}"
wx:for="{{messages}}"
wx:key="id"
id="msg-{{index}}"
>
<!-- AI消息左侧 -->
<block wx:if="{{!item.isMe}}">
<view class="avatar-wrap">
<image wx:if="{{character.avatar}}" class="chat-avatar" src="{{character.avatar}}" mode="aspectFill"></image>
<view wx:else class="avatar-placeholder">
<text class="avatar-text">{{character.name[0] || 'AI'}}</text>
</view>
</view>
<view class="message-content">
<!-- 文字消息 -->
<view class="chat-bubble other" wx:if="{{!item.type || item.type === 'text'}}" bindlongpress="onMessageLongPress" data-item="{{item}}">
<text class="chat-text" decode="{{true}}">{{item.text}}</text>
</view>
<!-- 图片消息 -->
<view class="chat-bubble-image other" wx:elif="{{item.type === 'image'}}">
<image class="message-image" src="{{item.imageUrl}}" mode="widthFix" bindtap="onPreviewImage" data-url="{{item.imageUrl}}"></image>
</view>
<!-- 语音消息 -->
<view class="chat-bubble voice other {{playingVoiceId === item.id ? 'playing' : ''}}" wx:elif="{{item.type === 'voice'}}" bindtap="onPlayVoice" data-id="{{item.id}}" data-url="{{item.audioUrl}}">
<view class="voice-waves">
<view class="voice-wave-bar"></view>
<view class="voice-wave-bar"></view>
<view class="voice-wave-bar"></view>
</view>
<text class="voice-duration">{{item.duration || 1}}″</text>
</view>
<view class="message-actions" wx:if="{{!item.type || item.type === 'text'}}">
<text class="message-time">{{item.time}}</text>
<!-- AI语音播放按钮 -->
<view class="play-voice-btn" wx:if="{{character.voiceId}}" bindtap="onPlayAIVoice" data-id="{{item.id}}" data-text="{{item.text}}">
<image src="/images/icon-voice.png" class="play-voice-icon" mode="aspectFit"></image>
</view>
</view>
<text class="message-time" wx:else>{{item.time}}</text>
</view>
</block>
<!-- 用户消息(右侧) -->
<block wx:else>
<view class="message-content me">
<!-- 文字消息 -->
<view class="chat-bubble me" wx:if="{{!item.type || item.type === 'text'}}" bindlongpress="onMessageLongPress" data-item="{{item}}">
<text class="chat-text" decode="{{true}}">{{item.text}}</text>
</view>
<!-- 图片消息 -->
<view class="chat-bubble-image me" wx:elif="{{item.type === 'image'}}">
<image class="message-image" src="{{item.imageUrl}}" mode="widthFix" bindtap="onPreviewImage" data-url="{{item.imageUrl}}"></image>
</view>
<!-- 语音消息 -->
<view class="chat-bubble voice me {{playingVoiceId === item.id ? 'playing' : ''}}" wx:elif="{{item.type === 'voice'}}" bindtap="onPlayVoice" data-id="{{item.id}}" data-url="{{item.audioUrl}}">
<text class="voice-duration">{{item.duration || 1}}″</text>
<view class="voice-waves">
<view class="voice-wave-bar"></view>
<view class="voice-wave-bar"></view>
<view class="voice-wave-bar"></view>
</view>
</view>
<!-- 语音识别文字 -->
<view class="voice-recognized-text" wx:if="{{item.type === 'voice' && (item.recognizing || item.recognizedText)}}">
<text wx:if="{{item.recognizing}}" class="recognizing-hint">识别中...</text>
<text wx:else class="recognized-text">{{item.recognizedText}}</text>
</view>
<!-- 礼物消息 -->
<view class="gift-message me" wx:elif="{{item.type === 'gift'}}">
<view class="gift-message-content">
<image class="gift-message-image" src="{{item.giftInfo.image}}" mode="aspectFit"></image>
<text class="gift-message-text">{{item.text}}</text>
</view>
</view>
<text class="message-time">{{item.time}}</text>
</view>
<view class="avatar-wrap user-avatar">
<image wx:if="{{myAvatar}}" class="chat-avatar" src="{{myAvatar}}" mode="aspectFill" binderror="onAvatarError"></image>
<view wx:else class="avatar-placeholder user">
<text class="avatar-text">我</text>
</view>
</view>
</block>
</view>
<!-- 正在输入提示 -->
<view class="chat-item other" wx:if="{{isTyping}}">
<view class="avatar-wrap">
<image wx:if="{{character.avatar}}" class="chat-avatar" src="{{character.avatar}}" mode="aspectFill"></image>
<view wx:else class="avatar-placeholder">
<text class="avatar-text">{{character.name[0] || 'AI'}}</text>
</view>
</view>
<view class="message-content">
<view class="chat-bubble other typing">
<view class="typing-dot"></view>
<view class="typing-dot"></view>
<view class="typing-dot"></view>
</view>
</view>
</view>
</view>
<!-- 底部占位 -->
<view class="chat-bottom-space"></view>
<!-- 底部锚点 - 用于滚动定位 -->
<view id="chat-bottom-anchor"></view>
</scroll-view>
</view>
<!-- 面板打开时的透明遮罩层 - 点击关闭面板 -->
<view class="panel-overlay" wx:if="{{showEmoji || showMorePanel}}" bindtap="onClosePanels"></view>
<!-- 底部输入区域 - Figma设计样式 -->
<view class="bottom-input-area {{showEmoji || showMorePanel ? 'panel-open' : ''}}">
<view class="input-container figma-input-container">
<!-- 语音/键盘切换按钮 - Figma样式 -->
<view class="figma-voice-btn" bindtap="onVoiceMode">
<image src="{{isVoiceMode ? '/images/icon-keyboard.png' : '/images/chat-input-voice.png'}}" class="figma-btn-icon" mode="aspectFit"></image>
</view>
<!-- 语音模式:按住说话按钮 -->
<view
wx:if="{{isVoiceMode}}"
class="voice-record-btn {{isRecording ? 'recording' : ''}}"
bindtouchstart="onVoiceTouchStart"
bindtouchmove="onVoiceTouchMove"
bindtouchend="onVoiceTouchEnd"
bindtouchcancel="onVoiceTouchCancel"
>
<text>{{isRecording ? (voiceCancelHint ? '松开 取消' : '松开 发送') : '按住 说话'}}</text>
</view>
<!-- 文字模式:输入框 - Figma样式 -->
<view wx:else class="figma-input-wrap">
<input
class="figma-text-input"
placeholder="{{isUnlocked ? '发消息...' : (freeTime.isActive ? '限时免费畅聊中...' : '发消息...')}}"
placeholder-class="figma-input-placeholder"
value="{{inputText}}"
bindinput="onInput"
confirm-type="send"
bindconfirm="onSend"
focus="{{inputFocus}}"
adjust-position="{{!showEmoji}}"
hold-keyboard="{{true}}"
/>
</view>
<!-- 表情按钮 - Figma样式 -->
<view class="figma-emoji-btn {{showEmoji ? 'active' : ''}}" bindtap="onEmojiToggle">
<image src="{{showEmoji ? '/images/icon-keyboard.png' : '/images/chat-input-emoji.png'}}" class="figma-btn-icon" mode="aspectFit"></image>
</view>
<!-- 发送/更多按钮 - Figma样式 -->
<view class="figma-send-btn" wx:if="{{inputText.length > 0 && !isVoiceMode}}" bindtap="onSend">
<image src="/images/icon-send.png" class="figma-btn-icon" mode="aspectFit"></image>
</view>
<view class="figma-add-btn {{showMorePanel ? 'active' : ''}}" wx:else bindtap="onAddMore">
<image src="/images/chat-input-plus.png" class="figma-btn-icon" mode="aspectFit"></image>
</view>
</view>
<!-- 表情面板 -->
<view class="emoji-panel" wx:if="{{showEmoji}}">
<scroll-view scroll-y class="emoji-scroll">
<view class="emoji-grid">
<view
class="emoji-item"
wx:for="{{emojis}}"
wx:key="*this"
data-emoji="{{item}}"
bindtap="onEmojiSelect"
>
<text class="emoji-text">{{item}}</text>
</view>
</view>
</scroll-view>
</view>
<!-- 更多功能面板 - AI角色聊天只显示基础功能Figma设计样式 -->
<view class="more-panel" wx:if="{{showMorePanel}}">
<view class="more-panel-content">
<!-- AI角色聊天只显示照片、拍摄、礼物按Figma顺序 -->
<view class="more-grid ai-chat-grid">
<!-- 照片(相册) -->
<view class="more-item" bindtap="onChooseImage">
<view class="more-icon-wrap figma-style">
<image class="figma-action-icon" src="/images/chat-action-photo.png" mode="aspectFit"></image>
</view>
<text class="more-text">照片</text>
</view>
<!-- 拍摄(拍照) -->
<view class="more-item" bindtap="onTakePhoto">
<view class="more-icon-wrap figma-style">
<image class="figma-action-icon" src="/images/chat-action-camera.png" mode="aspectFit"></image>
</view>
<text class="more-text">拍摄</text>
</view>
<!-- 礼物 -->
<view class="more-item" bindtap="onSendGift">
<view class="more-icon-wrap figma-style">
<image class="figma-action-icon" src="/images/chat-action-gift.png" mode="aspectFit"></image>
</view>
<text class="more-text">礼物</text>
</view>
</view>
</view>
<!-- 底部安全区域 -->
<view class="more-panel-safe"></view>
</view>
</view>
<!-- 语音录制提示浮层 -->
<view class="voice-recording-mask" wx:if="{{isRecording}}">
<view class="voice-recording-popup {{voiceCancelHint ? 'cancel' : ''}}">
<view class="voice-wave" wx:if="{{!voiceCancelHint}}">
<view class="wave-bar"></view>
<view class="wave-bar"></view>
<view class="wave-bar"></view>
<view class="wave-bar"></view>
<view class="wave-bar"></view>
</view>
<image wx:else class="cancel-icon" src="/images/icon-close.png" mode="aspectFit"></image>
<text class="voice-tip">{{voiceCancelHint ? '松开手指,取消发送' : '手指上划,取消发送'}}</text>
<text class="voice-duration-tip" wx:if="{{!voiceCancelHint}}">{{recordingDuration}}″</text>
</view>
</view>
<!-- 礼物选择弹窗 -->
<view class="gift-popup-mask" wx:if="{{showGiftPopup}}" bindtap="onCloseGiftPopup">
<view class="gift-popup" catchtap="preventMove">
<view class="gift-popup-header">
<text class="gift-popup-title">选择礼物</text>
<view class="gift-popup-close" bindtap="onCloseGiftPopup">
<image src="/images/icon-close.png" class="close-icon" mode="aspectFit"></image>
</view>
</view>
<scroll-view scroll-y class="gift-list-scroll">
<view class="gift-grid">
<view
class="gift-item {{selectedGift && selectedGift.id === item.id ? 'selected' : ''}}"
wx:for="{{giftList}}"
wx:key="id"
bindtap="onSelectGift"
data-gift="{{item}}"
>
<image class="gift-image" src="{{item.image}}" mode="aspectFit"></image>
<text class="gift-name">{{item.name}}</text>
<view class="gift-price">
<image src="/images/icon-heart-filled.png" class="price-icon" mode="aspectFit"></image>
<text>{{item.price}}</text>
</view>
</view>
</view>
</scroll-view>
<view class="gift-popup-footer">
<view class="gift-balance">
<text class="balance-label">我的花朵:</text>
<image src="/images/icon-heart-filled.png" class="balance-icon" mode="aspectFit"></image>
<text class="balance-value">{{userFlowers || 0}}</text>
</view>
<view class="gift-send-btn {{selectedGift ? 'active' : ''}}" bindtap="onConfirmSendGift">
<text>赠送</text>
</view>
</view>
</view>
</view>
<!-- 解锁角色弹窗 - 与首页样式一致 -->
<view class="unlock-popup-mask" wx:if="{{showUnlockPopup}}" bindtap="closeUnlockPopup" catchtouchmove="preventTouchMove">
<view class="unlock-popup" catchtap="preventBubble">
<!-- 关闭按钮 -->
<view class="unlock-popup-close" bindtap="closeUnlockPopup">
<text>×</text>
</view>
<!-- 顶部白色区域 -->
<view class="unlock-popup-header">
<!-- 角色头像 -->
<view class="unlock-avatar-container">
<view class="unlock-avatar-wrap">
<image class="unlock-avatar" src="{{character.avatar}}" mode="aspectFill"></image>
</view>
<!-- 锁图标 -->
<view class="unlock-lock-icon">
<image src="/images/icon-lock.png" mode="aspectFit"></image>
</view>
</view>
<!-- 标题 -->
<view class="unlock-title">
<text>解锁与 </text>
<text class="highlight">{{character.name}}</text>
<text> 的专属聊天</text>
</view>
</view>
<!-- 解锁选项列表 -->
<view class="unlock-options">
<!-- 100爱心解锁 -->
<view class="unlock-option-item hearts-option" bindtap="onExchangeHearts">
<view class="option-left">
<view class="option-icon hearts-icon">
<image src="/images/icon-heart-filled.png" mode="aspectFit"></image>
</view>
<view class="option-info">
<text class="option-title">{{unlockHeartsCost}} 爱心</text>
<text class="option-desc">{{heartCount >= unlockHeartsCost ? '爱心值充足 立即兑换' : '爱心值不足 去充值'}}</text>
</view>
</view>
<view class="option-btn hearts-btn">
<text>兑换</text>
</view>
</view>
<!-- 现金购买选项 -->
<view class="unlock-option-item money-option" bindtap="onPurchaseDirect">
<view class="option-left">
<view class="option-icon money-icon">
<text class="money-symbol">¥</text>
</view>
<view class="option-info light">
<text class="option-title">9.9元</text>
<text class="option-desc">限时特惠 立即购买</text>
</view>
</view>
<view class="option-btn money-btn">
<text>购买</text>
</view>
</view>
<!-- 暂不需要 -->
<view class="unlock-cancel" bindtap="closeUnlockPopup">
<text>暂不需要</text>
</view>
</view>
</view>
</view>
</view>