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:
duanshuwen
2026-05-28 23:29:26 +08:00
parent 35b4eb3cca
commit 5296fbccbc
5 changed files with 50 additions and 223 deletions

View File

@@ -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-asdRealSpeechapikey 请在 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>