851 lines
20 KiB
Vue
851 lines
20 KiB
Vue
<template>
|
||
<view class="chat-container">
|
||
<!-- 顶部导航栏 -->
|
||
<view class="top_bar flex w-full" v-if="isMobile">
|
||
<view class="chat-header">
|
||
<view class="header-left" @click="goBack">
|
||
<i class="fas fa-arrow-left"></i>
|
||
</view>
|
||
<view class="header-title">聊天</view>
|
||
<view class="header-right" @click="goToChatDetail">
|
||
<i class="fas fa-ellipsis-v"></i>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 浏览器环境下的导航栏 -->
|
||
<view class="chat-header" v-else>
|
||
<view class="header-left" @click="goBack">
|
||
<i class="fas fa-arrow-left"></i>
|
||
</view>
|
||
<view class="header-title">聊天</view>
|
||
<view class="header-right" @click="goToChatDetail">
|
||
<i class="fas fa-ellipsis-v"></i>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 聊天消息列表 -->
|
||
<scroll-view
|
||
class="message-list"
|
||
scroll-y
|
||
:scroll-top="scrollTop"
|
||
:scroll-into-view="scrollIntoView"
|
||
>
|
||
<block v-for="(message, index) in messages" :key="index">
|
||
<view
|
||
:id="'message-' + index"
|
||
:class="[
|
||
'message-item',
|
||
message.type === 'sent' ? 'sent' : 'received',
|
||
]"
|
||
>
|
||
<!-- 头像 -->
|
||
<view class="avatar" @click="goToUserDetail">
|
||
<image
|
||
:src="
|
||
message.type === 'sent'
|
||
? '/static/imgs/default_avatar.png'
|
||
: '/static/imgs/default_avatar.png'
|
||
"
|
||
mode="aspectFill"
|
||
></image>
|
||
</view>
|
||
|
||
<!-- 消息内容 -->
|
||
<view class="message-content">
|
||
<view class="message-bubble">
|
||
<!-- 文本消息 -->
|
||
<text class="message-text">{{
|
||
parseEmoji(message.content)
|
||
}}</text>
|
||
</view>
|
||
<!-- 消息时间 -->
|
||
<view class="message-time" v-if="message.time">
|
||
{{ formatTime(message.time) }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</block>
|
||
</scroll-view>
|
||
|
||
<!-- 输入区域 -->
|
||
<view class="input-area">
|
||
<!-- 表情选择器 -->
|
||
<view class="emoji-picker-container" v-if="showEmojiPicker">
|
||
<EmojiPicker
|
||
:visible="showEmojiPicker"
|
||
@select="onEmojiSelect"
|
||
@close="showEmojiPicker = false"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 输入工具栏 -->
|
||
<view class="input-toolbar">
|
||
<!-- 左侧切换按钮 -->
|
||
<view class="left-switch">
|
||
<view
|
||
class="switch-btn"
|
||
@click="switchInputMode"
|
||
:class="{ active: inputMode === 'voice' }"
|
||
>
|
||
<i
|
||
:class="
|
||
inputMode === 'voice' ? 'fas fa-keyboard' : 'fas fa-microphone'
|
||
"
|
||
></i>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 中间输入区域 -->
|
||
<view class="input-wrapper">
|
||
<!-- 语音模式:按住说话按钮 -->
|
||
<view
|
||
v-if="inputMode === 'voice'"
|
||
class="voice-input"
|
||
:class="{ recording: isRecording }"
|
||
@touchstart="startVoiceRecord"
|
||
@touchend="endVoiceRecord"
|
||
@touchcancel="cancelVoiceRecord"
|
||
>
|
||
<text class="voice-text">{{
|
||
isRecording ? "松开结束" : "按住说话"
|
||
}}</text>
|
||
<view v-if="isRecording" class="recording-indicator">
|
||
<view class="recording-dot"></view>
|
||
<view class="recording-dot"></view>
|
||
<view class="recording-dot"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 文本模式:输入框 -->
|
||
<view v-else class="text-input-container">
|
||
<textarea
|
||
v-model="inputMessage"
|
||
placeholder="输入消息..."
|
||
class="message-input"
|
||
:style="{ height: inputHeight + 'rpx' }"
|
||
:maxlength="500"
|
||
@confirm="sendMessage"
|
||
@focus="onInputFocus"
|
||
@blur="onInputBlur"
|
||
@input="onInputChange"
|
||
@linechange="onLineChange"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 右侧操作区 -->
|
||
<view class="right-actions">
|
||
<!-- 文本模式:表情按钮 -->
|
||
<view
|
||
v-if="inputMode === 'text'"
|
||
class="emoji-btn"
|
||
@click="toggleEmojiPicker"
|
||
:class="{ active: showEmojiPicker }"
|
||
>
|
||
<i class="fas fa-smile"></i>
|
||
</view>
|
||
|
||
<!-- 文本模式:发送按钮(有内容时显示) -->
|
||
<view
|
||
v-if="inputMode === 'text' && inputMessage.trim()"
|
||
class="send-btn"
|
||
@click="sendMessage"
|
||
>
|
||
发送
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import EmojiPicker from "../../src/components/EmojiPicker.vue";
|
||
import { parseEmoji } from "../../src/utils/emojiParser.js";
|
||
|
||
export default {
|
||
components: {
|
||
EmojiPicker,
|
||
},
|
||
data() {
|
||
return {
|
||
messages: [
|
||
{
|
||
type: "received",
|
||
content: "欢迎使用聊天功能!有什么可以帮您?",
|
||
time: new Date(Date.now() - 3000 * 60 * 1000), // 30分钟前
|
||
},
|
||
{
|
||
type: "received",
|
||
content: "您可以咨询产品信息、下单流程等相关问题。",
|
||
time: new Date(Date.now() - 29 * 60 * 1000), // 29分钟前
|
||
},
|
||
{
|
||
type: "sent",
|
||
content: "请问你们有哪些热门产品?",
|
||
time: new Date(Date.now() - 28 * 60 * 1000), // 28分钟前
|
||
},
|
||
{
|
||
type: "received",
|
||
content: "我们的热门产品有A、B、C三款,您想了解哪一款?",
|
||
time: new Date(Date.now() - 27 * 60 * 1000), // 27分钟前
|
||
},
|
||
{
|
||
type: "received",
|
||
content: "点击发送按钮或按回车键发送消息哦~",
|
||
time: new Date(Date.now() - 26 * 60 * 1000), // 26分钟前
|
||
},
|
||
],
|
||
inputMessage: "",
|
||
showEmojiPicker: false,
|
||
inputMode: "text", // 'text' 或 'voice'
|
||
isRecording: false,
|
||
recordingTimer: null,
|
||
inputHeight: 80, // 输入框高度,默认1行
|
||
scrollTop: 0, // 消息列表滚动位置
|
||
scrollIntoView: "", // 滚动到指定元素
|
||
};
|
||
},
|
||
mounted() {
|
||
// 确保输入框初始高度为1行
|
||
this.inputHeight = 80;
|
||
},
|
||
computed: {
|
||
// 从全局数据获取设备信息
|
||
isMobile() {
|
||
return getApp().globalData.isMobile;
|
||
}
|
||
},
|
||
watch: {
|
||
// 监听消息数组变化,自动滚动到最新消息
|
||
messages: {
|
||
handler(newMessages, oldMessages) {
|
||
if (newMessages.length > (oldMessages ? oldMessages.length : 0)) {
|
||
// 有新消息时,延迟滚动确保DOM更新完成
|
||
this.$nextTick(() => {
|
||
setTimeout(() => {
|
||
this.scrollToBottom();
|
||
}, 200);
|
||
});
|
||
}
|
||
},
|
||
deep: true,
|
||
immediate: false,
|
||
},
|
||
},
|
||
methods: {
|
||
parseEmoji(text) {
|
||
// 使用工具函数解析emoji
|
||
return parseEmoji(text);
|
||
},
|
||
sendMessage() {
|
||
if (this.inputMessage.trim() !== "") {
|
||
// 添加用户发送的消息
|
||
const newMessage = {
|
||
type: "sent",
|
||
content: this.inputMessage,
|
||
time: new Date(),
|
||
};
|
||
this.messages.push(newMessage);
|
||
|
||
// 强制更新视图
|
||
this.$forceUpdate();
|
||
|
||
// 清空输入框并重置高度
|
||
const message = this.inputMessage;
|
||
this.inputMessage = "";
|
||
this.inputHeight = 80; // 重置为1行高度
|
||
this.showEmojiPicker = false;
|
||
|
||
// 立即滚动到最新消息
|
||
this.$nextTick(() => {
|
||
this.scrollToBottom();
|
||
});
|
||
|
||
// 模拟回复
|
||
setTimeout(() => {
|
||
this.simulateReply(message);
|
||
}, 1000);
|
||
}
|
||
},
|
||
simulateReply(message) {
|
||
// 简单的回复逻辑
|
||
let reply = "感谢你的消息!";
|
||
if (message.includes("你好")) {
|
||
reply = "你好!很高兴见到你!";
|
||
} else if (message.includes("产品")) {
|
||
reply = "我们的产品非常棒,你可以查看我们的官网了解更多信息。";
|
||
}
|
||
|
||
const replyMessage = {
|
||
type: "received",
|
||
content: reply,
|
||
time: new Date(),
|
||
};
|
||
|
||
this.messages.push(replyMessage);
|
||
|
||
// 强制更新视图
|
||
this.$forceUpdate();
|
||
|
||
// 立即滚动到最新消息
|
||
this.$nextTick(() => {
|
||
this.scrollToBottom();
|
||
});
|
||
},
|
||
toggleEmojiPicker() {
|
||
this.showEmojiPicker = !this.showEmojiPicker;
|
||
},
|
||
onEmojiSelect(emoji) {
|
||
this.inputMessage += emoji.unicode;
|
||
},
|
||
onInputFocus() {
|
||
this.showEmojiPicker = false;
|
||
},
|
||
onInputBlur() {
|
||
// 输入框失焦时的处理
|
||
},
|
||
onInputChange(e) {
|
||
// 输入内容变化时的处理
|
||
this.inputMessage = e.detail.value;
|
||
|
||
// 根据内容长度估算行数并调整高度
|
||
this.adjustInputHeight();
|
||
},
|
||
onLineChange(e) {
|
||
// 行数变化时调整高度,限制最大10行
|
||
const lineCount = e.detail.lineCount;
|
||
const minHeight = 80; // 1行高度
|
||
const lineHeight = 40; // 每行高度
|
||
const maxLines = 10; // 最大10行
|
||
|
||
let newHeight = minHeight + (lineCount - 1) * lineHeight;
|
||
newHeight = Math.min(
|
||
Math.max(newHeight, minHeight),
|
||
minHeight + (maxLines - 1) * lineHeight
|
||
);
|
||
|
||
this.inputHeight = newHeight;
|
||
},
|
||
adjustInputHeight() {
|
||
// 根据输入内容调整高度
|
||
const content = this.inputMessage;
|
||
if (!content.trim()) {
|
||
// 如果内容为空,设置为1行高度
|
||
this.inputHeight = 80;
|
||
return;
|
||
}
|
||
|
||
const lines = content.split("\n").length;
|
||
const minHeight = 80;
|
||
const lineHeight = 40;
|
||
const maxLines = 10;
|
||
|
||
let newHeight = minHeight + (lines - 1) * lineHeight;
|
||
newHeight = Math.min(
|
||
Math.max(newHeight, minHeight),
|
||
minHeight + (maxLines - 1) * lineHeight
|
||
);
|
||
|
||
this.inputHeight = newHeight;
|
||
},
|
||
// 切换输入模式
|
||
switchInputMode() {
|
||
if (this.inputMode === "text") {
|
||
this.inputMode = "voice";
|
||
this.showEmojiPicker = false;
|
||
} else {
|
||
this.inputMode = "text";
|
||
}
|
||
},
|
||
// 切换更多选项
|
||
toggleMoreOptions() {
|
||
// 这里可以添加更多选项的弹窗
|
||
},
|
||
// 语音录制相关
|
||
startVoiceRecord() {
|
||
this.isRecording = true;
|
||
// 这里可以添加实际的录音逻辑
|
||
// 例如调用 uni.getRecorderManager()
|
||
},
|
||
endVoiceRecord() {
|
||
this.isRecording = false;
|
||
// 这里可以添加录音结束的处理逻辑
|
||
// 例如发送语音消息
|
||
},
|
||
cancelVoiceRecord() {
|
||
this.isRecording = false;
|
||
// 这里可以添加取消录音的处理逻辑
|
||
},
|
||
scrollToBottom() {
|
||
// 滚动到消息列表底部
|
||
// 方法1:使用scroll-top
|
||
this.scrollTop = 99999;
|
||
|
||
// 方法2:使用scroll-into-view滚动到最后一个消息
|
||
if (this.messages.length > 0) {
|
||
const lastIndex = this.messages.length - 1;
|
||
this.scrollIntoView = "message-" + lastIndex;
|
||
|
||
// 重置scrollIntoView以允许重复滚动
|
||
setTimeout(() => {
|
||
this.scrollIntoView = "";
|
||
}, 100);
|
||
}
|
||
},
|
||
/**
|
||
* 返回
|
||
*/
|
||
goBack() {
|
||
uni.navigateBack();
|
||
},
|
||
/**
|
||
* 跳转到聊天详情
|
||
*/
|
||
goToChatDetail() {
|
||
uni.navigateTo({
|
||
url: "/pages/message/chatdetail",
|
||
});
|
||
},
|
||
/**
|
||
* 跳转到用户详情
|
||
*/
|
||
goToUserDetail() {
|
||
uni.navigateTo({
|
||
url: "/pages/message/userdetail",
|
||
});
|
||
},
|
||
/**
|
||
* 格式化时间
|
||
*/
|
||
formatTime(time) {
|
||
const date = new Date(time);
|
||
const now = new Date();
|
||
|
||
// 如果是今天,只显示时间
|
||
if (date.toDateString() === now.toDateString()) {
|
||
return date.toLocaleTimeString("zh-CN", {
|
||
hour: "2-digit",
|
||
minute: "2-digit",
|
||
});
|
||
}
|
||
|
||
// 如果是昨天,显示"昨天"
|
||
const yesterday = new Date(now);
|
||
yesterday.setDate(yesterday.getDate() - 1);
|
||
if (date.toDateString() === yesterday.toDateString()) {
|
||
return (
|
||
"昨天 " +
|
||
date.toLocaleTimeString("zh-CN", {
|
||
hour: "2-digit",
|
||
minute: "2-digit",
|
||
})
|
||
);
|
||
}
|
||
|
||
// 其他情况显示完整日期
|
||
return (
|
||
date.toLocaleDateString("zh-CN") +
|
||
" " +
|
||
date.toLocaleTimeString("zh-CN", {
|
||
hour: "2-digit",
|
||
minute: "2-digit",
|
||
})
|
||
);
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.chat-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100vh;
|
||
background-color: var(--background);
|
||
}
|
||
|
||
/* 移动设备顶部状态栏 */
|
||
.top_bar {
|
||
background: var(--gradient-primary);
|
||
box-shadow: var(--shadow-lg);
|
||
z-index: 9999;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: calc(var(--status-bar-height) + 88rpx);
|
||
display: flex;
|
||
align-items: flex-end;
|
||
padding-top: var(--status-bar-height);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* 支持安全区域的设备 */
|
||
@supports (padding: max(0px)) {
|
||
.top_bar {
|
||
height: calc(var(--status-bar-height) + 88rpx + env(safe-area-inset-top));
|
||
padding-top: calc(var(--status-bar-height) + env(safe-area-inset-top));
|
||
}
|
||
|
||
.top_bar + .message-list {
|
||
margin-top: calc(var(--status-bar-height) + 88rpx + env(safe-area-inset-top));
|
||
}
|
||
}
|
||
|
||
/* 顶部导航栏 */
|
||
.chat-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
height: 88rpx;
|
||
background-color: var(--surface);
|
||
border-bottom: 1rpx solid var(--border);
|
||
padding: 0 20rpx;
|
||
box-sizing: border-box;
|
||
box-shadow: var(--shadow);
|
||
}
|
||
|
||
/* 移动设备下的导航栏样式 */
|
||
.top_bar .chat-header {
|
||
background: transparent;
|
||
border-bottom: none;
|
||
box-shadow: none;
|
||
width: 100%;
|
||
height: 88rpx;
|
||
padding: 0 20rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.top_bar .header-title {
|
||
color: var(--white);
|
||
font-weight: 600;
|
||
font-size: 36rpx;
|
||
}
|
||
|
||
.top_bar .header-left i,
|
||
.top_bar .header-right i {
|
||
color: var(--white);
|
||
font-size: 40rpx;
|
||
}
|
||
|
||
.header-left,
|
||
.header-right {
|
||
width: 80rpx;
|
||
height: 88rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: background-color 0.2s ease;
|
||
}
|
||
|
||
.header-right:active {
|
||
background-color: var(--surface-hover);
|
||
border-radius: 8rpx;
|
||
}
|
||
|
||
/* 移动设备下的按钮悬停效果 */
|
||
.top_bar .header-left:active,
|
||
.top_bar .header-right:active {
|
||
background-color: rgba(255, 255, 255, 0.2);
|
||
border-radius: 8rpx;
|
||
}
|
||
|
||
.header-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: var(--title-color);
|
||
}
|
||
|
||
.iconfont {
|
||
font-family: "iconfont" !important;
|
||
font-size: 36rpx;
|
||
font-style: normal;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
/* 消息列表 */
|
||
.message-list {
|
||
flex: 1;
|
||
padding: 20rpx;
|
||
overflow-y: auto;
|
||
background-color: var(--background);
|
||
}
|
||
|
||
/* 移动设备下为消息列表添加顶部间距 */
|
||
.top_bar + .message-list {
|
||
margin-top: calc(var(--status-bar-height) + 88rpx);
|
||
}
|
||
|
||
.message-item {
|
||
display: flex;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.message-item.sent {
|
||
flex-direction: row-reverse;
|
||
}
|
||
|
||
/* 头像 */
|
||
.avatar {
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
border-radius: 10rpx;
|
||
overflow: hidden;
|
||
flex-shrink: 0;
|
||
margin: 0 20rpx;
|
||
}
|
||
|
||
.avatar image {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
/* 消息内容 */
|
||
.message-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
max-width: 70%;
|
||
}
|
||
|
||
.message-item.sent .message-content {
|
||
align-items: flex-end;
|
||
}
|
||
|
||
.message-item.received .message-content {
|
||
align-items: flex-start;
|
||
}
|
||
|
||
/* 消息气泡 */
|
||
.message-bubble {
|
||
position: relative;
|
||
padding: 20rpx;
|
||
border-radius: 12rpx;
|
||
word-wrap: break-word;
|
||
word-break: break-all;
|
||
font-size: 28rpx;
|
||
line-height: 1.4;
|
||
max-width: 100%;
|
||
}
|
||
|
||
.message-item.sent .message-bubble {
|
||
background: var(--gradient-primary);
|
||
color: var(--white);
|
||
border-radius: 12rpx 2rpx 12rpx 12rpx;
|
||
}
|
||
|
||
.message-item.received .message-bubble {
|
||
background-color: var(--surface);
|
||
color: var(--text-color);
|
||
border-radius: 2rpx 12rpx 12rpx 12rpx;
|
||
box-shadow: var(--shadow);
|
||
}
|
||
|
||
/* 消息时间 */
|
||
.message-time {
|
||
font-size: 20rpx;
|
||
color: var(--text-muted);
|
||
margin-top: 10rpx;
|
||
}
|
||
|
||
/* 输入区域 */
|
||
.input-area {
|
||
background-color: var(--surface);
|
||
border-top: 1rpx solid var(--border);
|
||
box-shadow: var(--shadow-md);
|
||
}
|
||
|
||
/* 表情选择器容器 */
|
||
.emoji-picker-container {
|
||
border-bottom: 1rpx solid var(--border-light);
|
||
}
|
||
|
||
/* 输入工具栏 */
|
||
.input-toolbar {
|
||
display: flex;
|
||
align-items: flex-end;
|
||
padding: 20rpx 16rpx;
|
||
gap: 16rpx;
|
||
min-height: 100rpx;
|
||
}
|
||
|
||
/* 左侧切换按钮 */
|
||
.left-switch {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.switch-btn {
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 50%;
|
||
background-color: transparent;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.switch-btn.active {
|
||
background: var(--gradient-primary);
|
||
}
|
||
|
||
.switch-btn i {
|
||
font-size: 40rpx;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.switch-btn.active i {
|
||
color: var(--white);
|
||
}
|
||
|
||
/* 中间输入区域 */
|
||
.input-wrapper {
|
||
flex: 1;
|
||
position: relative;
|
||
}
|
||
|
||
/* 文本输入容器 */
|
||
.text-input-container {
|
||
background-color: var(--surface);
|
||
border-radius: 8rpx;
|
||
border: 1rpx solid var(--border);
|
||
overflow: hidden;
|
||
box-shadow: var(--shadow);
|
||
}
|
||
|
||
.message-input {
|
||
width: 100%;
|
||
height: 80rpx; /* 固定默认高度为1行 */
|
||
min-height: 80rpx;
|
||
max-height: 440rpx; /* 10行高度:80rpx + 9 * 40rpx = 440rpx */
|
||
padding: 20rpx 24rpx;
|
||
border: none;
|
||
font-size: 32rpx;
|
||
line-height: 1.4;
|
||
background-color: transparent;
|
||
box-sizing: border-box;
|
||
resize: none;
|
||
word-wrap: break-word;
|
||
word-break: break-all;
|
||
overflow-y: auto; /* 超出10行时显示滚动条 */
|
||
}
|
||
|
||
.message-input:focus {
|
||
outline: none;
|
||
}
|
||
|
||
/* 语音输入 */
|
||
.voice-input {
|
||
width: 100%;
|
||
height: 80rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background-color: var(--gray-lighter);
|
||
border-radius: 8rpx;
|
||
transition: all 0.2s ease;
|
||
position: relative;
|
||
border: 1rpx solid var(--border);
|
||
}
|
||
|
||
.voice-input:active,
|
||
.voice-input.recording {
|
||
background-color: var(--gray);
|
||
}
|
||
|
||
.voice-text {
|
||
font-size: 32rpx;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.recording-indicator {
|
||
position: absolute;
|
||
right: 20rpx;
|
||
display: flex;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.recording-dot {
|
||
width: 12rpx;
|
||
height: 12rpx;
|
||
background-color: #ff4444;
|
||
border-radius: 50%;
|
||
animation: recordingPulse 1s infinite;
|
||
}
|
||
|
||
.recording-dot:nth-child(2) {
|
||
animation-delay: 0.2s;
|
||
}
|
||
|
||
.recording-dot:nth-child(3) {
|
||
animation-delay: 0.4s;
|
||
}
|
||
|
||
@keyframes recordingPulse {
|
||
0%,
|
||
100% {
|
||
opacity: 0.3;
|
||
transform: scale(0.8);
|
||
}
|
||
50% {
|
||
opacity: 1;
|
||
transform: scale(1.2);
|
||
}
|
||
}
|
||
|
||
/* 右侧操作区 */
|
||
.right-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.emoji-btn {
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 50%;
|
||
background-color: transparent;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.emoji-btn.active {
|
||
background: var(--gradient-primary);
|
||
}
|
||
|
||
.emoji-btn:active {
|
||
background-color: var(--surface-hover);
|
||
}
|
||
|
||
.emoji-btn i {
|
||
font-size: 40rpx;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.emoji-btn.active i {
|
||
color: var(--white);
|
||
}
|
||
|
||
.send-btn {
|
||
padding: 16rpx 32rpx;
|
||
background: var(--gradient-primary);
|
||
color: var(--white);
|
||
border-radius: 8rpx;
|
||
font-size: 28rpx;
|
||
font-weight: 500;
|
||
transition: all 0.2s ease;
|
||
min-width: 120rpx;
|
||
text-align: center;
|
||
box-shadow: var(--shadow);
|
||
}
|
||
|
||
.send-btn:active {
|
||
background: var(--primary-dark);
|
||
transform: scale(0.98);
|
||
}
|
||
</style>
|