refactor: migrate styles to Tailwind and clean up components
- remove SCSS style files for ChatQuickAccess and ChatInputArea, replace with inline Tailwind utilities - uncomment ChatQuickAccess component in ChatMainList to re-enable the quick access bar - simplify speech recognition logic in ChatInputArea, update input placeholder text - replace direct uni.showToast calls with a shared helper function - remove unused keyboard height change listener and redundant keyboard hiding code
This commit is contained in:
@@ -1,58 +1,49 @@
|
||||
<template>
|
||||
<div class="input-area-wrapper" @touchend="handleVoiceTouchEndFromContainer"
|
||||
@touchcancel="handleVoiceTouchEndFromContainer">
|
||||
<div v-if="!visibleWaveBtn || speechProvider !== 'wechat'" class="area-input">
|
||||
<div v-if="!visibleWaveBtn || speechProvider !== 'wechat'"
|
||||
class="mx-[8px] mb-[6px] flex min-h-[42px] items-end rounded-[21px] bg-white px-[8px] py-[6px] shadow-[0_2px_12px_rgba(18,39,75,0.06)]">
|
||||
<!-- 语音/键盘切换 -->
|
||||
<div v-if="isSpeechRecognitionSupported" class="input-container-voice" @click="toggleVoiceMode">
|
||||
<img class="voice-icon" v-if="!isVoiceMode"
|
||||
<div v-if="isSpeechRecognitionSupported" class="flex h-[30px] w-[30px] shrink-0 items-center justify-center"
|
||||
@click="toggleVoiceMode">
|
||||
<img class="h-[22px] w-[22px]" v-if="!isVoiceMode"
|
||||
src="https://oss.nianxx.cn/mp/static/version_101/home/input_voice_icon.png" />
|
||||
<img class="voice-icon" v-else src="https://oss.nianxx.cn/mp/static/version_101/home/input_keyboard_icon.png" />
|
||||
<img class="h-[22px] w-[22px]" v-else
|
||||
src="https://oss.nianxx.cn/mp/static/version_101/home/input_keyboard_icon.png" />
|
||||
</div>
|
||||
|
||||
<!-- 输入框/语音按钮容器 -->
|
||||
<div class="input-button-container"
|
||||
:class="{ 'input-button-container--no-voice': !isSpeechRecognitionSupported }">
|
||||
<textarea ref="textareaRef" v-if="!isVoiceMode" class="textarea" type="text" cursor-spacing="20"
|
||||
confirm-type="send" v-model="inputMessage" auto-height :focus="isFocused" :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" />
|
||||
<div class="min-w-0 flex-1" :class="{ 'pl-[4px]': !isSpeechRecognitionSupported }">
|
||||
<textarea ref="textareaRef" v-if="!isVoiceMode" v-model="inputMessage" rows="1" maxlength="300"
|
||||
:placeholder="placeholder"
|
||||
class="block h-[22px] max-h-[92px] min-h-[22px] w-full resize-none overflow-x-hidden overflow-y-auto border-0 bg-transparent p-0 text-[14px] leading-[22px] text-[#333] outline-none appearance-none placeholder:text-[#A5AAB4] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
||||
@input="adjustTextareaHeight" @focus="handleFocus" @blur="handleBlur" @touchstart="handleTouchStart"
|
||||
@touchend="handleTouchEnd" />
|
||||
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<div v-if="isVoiceMode" class="hold-to-talk-button" @longpress="handleVoiceTouchStart"
|
||||
@touchend="handleVoiceTouchEnd" @touchcancel="handleVoiceTouchEnd">
|
||||
|
||||
<div v-if="isVoiceMode"
|
||||
class="flex h-[36px] w-full select-none items-center justify-center bg-transparent text-[14px] leading-[22px] text-[#333] active:bg-gray-100"
|
||||
@longpress="handleVoiceTouchStart" @touchend="handleVoiceTouchEnd" @touchcancel="handleVoiceTouchEnd">
|
||||
按住 说话
|
||||
</div>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<div v-if="isVoiceMode" class="hold-to-talk-button" @touchstart="handleVoiceTouchStart"
|
||||
@touchend="handleVoiceTouchEnd" @touchcancel="handleVoiceTouchEnd">
|
||||
<RecordingWaveBtn v-if="visibleWaveBtn" class="recording-wave-inline" ref="recordingWaveBtnRef" />
|
||||
<text v-else>按住 说话</text>
|
||||
</div>
|
||||
<!-- #endif -->
|
||||
</div>
|
||||
|
||||
<div class="input-container-send">
|
||||
<div class="input-container-send-btn" @click="sendMessage">
|
||||
<div v-if="isSessionActive" class="send-stop"> </div>
|
||||
<img v-else class="send-icon" src="https://oss.nianxx.cn/mp/static/version_101/home/input_send_icon.png" />
|
||||
<div class="flex h-[30px] w-[34px] shrink-0 items-center justify-end">
|
||||
<div
|
||||
class="flex h-[30px] w-[30px] items-center justify-center rounded-full [background:radial-gradient(39%_39%_at_97%_81%,#79dffb_0%,rgba(138,227,252,0)_100%),radial-gradient(54%_54%_at_3%_70%,#8afcf8_0%,rgba(138,252,248,0)_100%),#0CCD58] shadow-[0_4px_10px_rgba(12,205,88,0.22)]"
|
||||
@click="sendMessage">
|
||||
<div v-if="isSessionActive" class="h-[10px] w-[10px] rounded-[3px] bg-white"> </div>
|
||||
<img v-else class="h-[20px] w-[20px]"
|
||||
src="https://oss.nianxx.cn/mp/static/version_101/home/input_send_icon.png" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
|
||||
<!-- 录音按钮 -->
|
||||
<RecordingWaveBtn v-if="visibleWaveBtn" ref="recordingWaveBtnRef" />
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<yao-asdRealSpeech v-if="isSpeechRecognitionSupported && appSpeechVisible" :key="appSpeechKey" ref="appSpeechRef"
|
||||
:options="appSpeechOptions" @result="handleAppSpeechResult" @change="handleAppSpeechChange" />
|
||||
<!-- #endif -->
|
||||
|
||||
<div class="color-99A0AE font-size-9 text-center text-gray-400">
|
||||
<div class="text-center text-[9px] leading-[12px] text-[#A5AAB4]">
|
||||
内容由AI大模型生成,请仔细鉴别
|
||||
</div>
|
||||
</div>
|
||||
@@ -66,24 +57,9 @@ import RecordingWaveBtn from "@/components/Speech/RecordingWaveBtn.vue";
|
||||
let manager = null;
|
||||
let speechProvider = "";
|
||||
const isSpeechRecognitionEnabled = ref(true);
|
||||
const isSpeechRecognitionSupported = ref(false);
|
||||
const isSpeechRecognitionSupported = ref(true);
|
||||
let appSpeechOptions = {};
|
||||
|
||||
|
||||
// WechatSI 是微信小程序插件,App 原生基座没有 requirePlugin。
|
||||
// #ifdef MP-WEIXIN
|
||||
const plugin = requirePlugin("WechatSI");
|
||||
manager = plugin.getRecordRecognitionManager();
|
||||
isSpeechRecognitionSupported.value = isSpeechRecognitionEnabled.value && !!manager;
|
||||
speechProvider = manager ? "wechat" : "";
|
||||
// #endif
|
||||
|
||||
// App 端使用 yao-asdRealSpeech;apikey 请在 src/constant/speech.js 中配置。
|
||||
// #ifdef APP-PLUS
|
||||
isSpeechRecognitionSupported.value = isSpeechRecognitionEnabled.value;
|
||||
speechProvider = "app";
|
||||
// #endif
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: String,
|
||||
holdKeyboard: Boolean,
|
||||
@@ -104,8 +80,9 @@ const recordingWaveBtnRef = ref(null);
|
||||
const appSpeechRef = ref(null);
|
||||
const appSpeechKey = ref(0);
|
||||
const appSpeechVisible = ref(true);
|
||||
const placeholder = ref('请输入');
|
||||
const placeholder = ref("快告诉小七您在想什么~");
|
||||
const inputMessage = ref(props.modelValue || "");
|
||||
const maxTextareaHeight = 92;
|
||||
const isFocused = ref(false);
|
||||
const keyboardHeight = ref(0);
|
||||
const isVoiceMode = ref(false);
|
||||
@@ -148,6 +125,14 @@ const startWatchDog = (timeout = 10000) => {
|
||||
}, timeout);
|
||||
};
|
||||
|
||||
const adjustTextareaHeight = () => {
|
||||
const textarea = textareaRef.value;
|
||||
if (!textarea) return;
|
||||
|
||||
textarea.style.height = "22px";
|
||||
textarea.style.height = `${Math.min(textarea.scrollHeight, maxTextareaHeight)}px`;
|
||||
};
|
||||
|
||||
const clearAppStopFallback = () => {
|
||||
if (appStopFallbackTimer) {
|
||||
clearTimeout(appStopFallbackTimer);
|
||||
@@ -202,6 +187,7 @@ watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
inputMessage.value = val;
|
||||
nextTick(adjustTextareaHeight);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -211,6 +197,7 @@ watch(inputMessage, (val) => {
|
||||
if (val !== props.modelValue) {
|
||||
emit("update:modelValue", val);
|
||||
}
|
||||
nextTick(adjustTextareaHeight);
|
||||
});
|
||||
|
||||
// 切换语音/文本模式
|
||||
@@ -236,10 +223,7 @@ const handleVoiceTouchStart = () => {
|
||||
showRecordingUI();
|
||||
} else if (speechProvider === "app") {
|
||||
if (!appSpeechOptions.apikey) {
|
||||
uni.showToast({
|
||||
title: "请先配置语音识别API Key",
|
||||
icon: "none",
|
||||
});
|
||||
showToast("请先配置语音识别API Key");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -253,10 +237,7 @@ const handleVoiceTouchStart = () => {
|
||||
const appSpeech = appSpeechRef.value;
|
||||
if (!appSpeech || typeof appSpeech.start !== "function") {
|
||||
isAppSpeechStarting.value = false;
|
||||
uni.showToast({
|
||||
title: "语音组件未初始化",
|
||||
icon: "none",
|
||||
});
|
||||
showToast("语音组件未初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -488,17 +469,8 @@ const handleAppSpeechChange = (msg) => {
|
||||
|
||||
// 监听键盘高度变化
|
||||
onMounted(() => {
|
||||
// 监听键盘弹起
|
||||
uni.onKeyboardHeightChange((res) => {
|
||||
keyboardHeight.value = res.height;
|
||||
if (res.height) {
|
||||
emit("keyboardShow", res.height);
|
||||
} else {
|
||||
emit("keyboardHide");
|
||||
}
|
||||
});
|
||||
|
||||
initRecord();
|
||||
nextTick(adjustTextareaHeight);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
@@ -533,10 +505,6 @@ const hideKeyboardAfterSend = () => {
|
||||
if (textarea && typeof textarea.blur === "function") {
|
||||
textarea.blur();
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
uni.hideKeyboard();
|
||||
});
|
||||
};
|
||||
|
||||
const sendMessage = () => {
|
||||
@@ -590,16 +558,8 @@ const blurInput = () => {
|
||||
if (textarea && typeof textarea.blur === "function") {
|
||||
textarea.blur();
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
uni.hideKeyboard();
|
||||
});
|
||||
};
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({ focusInput, blurInput });
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./styles/index.scss";
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user