Files
YGChatCS/src/pages/index/components/chat/ChatInputArea/index.vue

249 lines
5.8 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="input-area-wrapper">
<view v-if="!visibleWaveBtn" class="area-input">
<!-- 语音/键盘切换 -->
<view class="input-container-voice" @click="toggleVoiceMode">
<image
class="voice-icon"
v-if="!isVoiceMode"
src="https://oss.nianxx.cn/mp/static/version_101/home/input_voice_icon.png"
/>
<image
class="voice-icon"
v-else
src="https://oss.nianxx.cn/mp/static/version_101/home/input_keyboard_icon.png"
/>
</view>
<!-- 输入框/语音按钮容器 -->
<view class="input-button-container">
<textarea
ref="textareaRef"
v-if="!isVoiceMode"
class="textarea"
type="text"
cursor-spacing="20"
confirm-type="send"
v-model="inputMessage"
auto-height
:confirm-hold="true"
:placeholder="placeholder"
:show-confirm-bar="false"
:hold-keyboard="holdKeyboard"
:adjust-position="true"
:disable-default-padding="true"
maxlength="300"
@confirm="sendMessage"
@focus="handleFocus"
@blur="handleBlur"
@touchstart="handleTouchStart"
@touchend="handleTouchEnd"
/>
<view
v-if="isVoiceMode"
class="hold-to-talk-button"
@longpress="handleVoiceTouchStart"
@touchend="handleVoiceTouchEnd"
@touchcancel="handleVoiceTouchEnd"
>
按住 说话
</view>
</view>
<view class="input-container-send">
<view class="input-container-send-btn" @click="sendMessage">
<view v-if="props.isSessionActive" class="send-stop"> </view>
<image
v-else
class="send-icon"
src="https://oss.nianxx.cn/mp/static/version_101/home/input_send_icon.png"
/>
</view>
</view>
</view>
<view class="color-99A0AE font-size-9 text-center text-gray-400">
内容由AI大模型生成请仔细鉴别
</view>
<!-- 录音按钮 -->
<RecordingWaveBtn v-if="visibleWaveBtn" ref="recordingWaveBtnRef" />
</view>
</template>
<script setup>
import { ref, computed, watch, nextTick, onMounted, defineExpose } from "vue";
import RecordingWaveBtn from "@/components/Speech/RecordingWaveBtn.vue";
import { getCurrentConfig } from "@/constant/base";
const plugin = requirePlugin("WechatSI");
const manager = plugin.getRecordRecognitionManager();
const props = defineProps({
modelValue: String,
holdKeyboard: Boolean,
isSessionActive: Boolean,
stopRequest: Function,
});
const emit = defineEmits([
"update:modelValue",
"send",
"noHideKeyboard",
"keyboardShow",
"keyboardHide",
"sendVoice",
]);
const textareaRef = ref(null);
const recordingWaveBtnRef = ref(null);
const placeholder = computed(() => getCurrentConfig().placeholder);
const inputMessage = ref(props.modelValue || "");
const isFocused = ref(false);
const keyboardHeight = ref(0);
const isVoiceMode = ref(false);
const visibleWaveBtn = ref(false);
// 保持和父组件同步
watch(
() => props.modelValue,
(val) => {
inputMessage.value = val;
}
);
// 当子组件的 inputMessage 变化时,通知父组件(但要避免循环更新)
watch(inputMessage, (val) => {
// 只有当值真正不同时才emit避免循环更新
if (val !== props.modelValue) {
emit("update:modelValue", val);
}
});
// 切换语音/文本模式
const toggleVoiceMode = () => {
isVoiceMode.value = !isVoiceMode.value;
};
// 处理语音按钮长按开始
const handleVoiceTouchStart = () => {
manager.start({ lang: "zh_CN" });
visibleWaveBtn.value = true;
// 启动音频条动画
nextTick(() => {
if (recordingWaveBtnRef.value) {
recordingWaveBtnRef.value.startAnimation();
}
});
};
// 处理语音按钮长按结束
const handleVoiceTouchEnd = () => {
manager.stop();
// 停止音频条动画
if (recordingWaveBtnRef.value) {
recordingWaveBtnRef.value.stopAnimation();
}
visibleWaveBtn.value = false;
};
// 处理发送原语音
const initRecord = () => {
manager.onRecognize = (res) => {
let text = res.result;
inputMessage.value = text;
};
// 识别结束事件
manager.onStop = (res) => {
console.log(res, 37);
let text = res.result;
if (text == "") {
console.log("没有说话");
return;
}
inputMessage.value = text;
// 在语音识别完成后发送消息
emit("send", text);
};
};
// 监听键盘高度变化
onMounted(() => {
// 监听键盘弹起
uni.onKeyboardHeightChange((res) => {
keyboardHeight.value = res.height;
if (res.height) {
emit("keyboardShow", res.height);
} else {
emit("keyboardHide");
}
});
initRecord();
});
const sendMessage = () => {
if (props.isSessionActive) {
// 如果会话进行中,调用停止请求函数
if (props.stopRequest) {
props.stopRequest();
}
} else {
// 否则发送新消息
if (!inputMessage.value.trim()) return;
emit("send", inputMessage.value);
// 发送后保持焦点(可选)
if (props.holdKeyboard && textareaRef.value) {
nextTick(() => {
textareaRef.value.focus();
});
}
}
};
const handleFocus = () => {
isFocused.value = true;
emit("noHideKeyboard");
};
const handleBlur = () => {
isFocused.value = false;
};
const handleTouchStart = () => {
emit("noHideKeyboard");
};
const handleTouchEnd = () => {
emit("noHideKeyboard");
};
// 手动聚焦输入框
const focusInput = () => {
if (textareaRef.value) {
textareaRef.value.focus();
}
};
// 手动失焦输入框
const blurInput = () => {
if (textareaRef.value) {
textareaRef.value.blur();
}
};
// 暴露方法给父组件
defineExpose({ focusInput });
</script>
<style scoped lang="scss">
@import "./styles/index.scss";
</style>