feat: 项目初始化、完成基本流式传输和语音识别功能
This commit is contained in:
130
web/src/views/CommunityView.vue
Normal file
130
web/src/views/CommunityView.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectGroupOption, SelectOption } from "naive-ui";
|
||||
import { ExclamationTriangleIcon, microphone, PaperAirplaneIcon, TrashIcon } from "@/assets/Icons";
|
||||
import markdown from "@/components/markdown.vue";
|
||||
import { useAsrStore, useChatStore } from "@/stores";
|
||||
|
||||
const chatStore = useChatStore();
|
||||
const asrStore = useAsrStore();
|
||||
|
||||
const { historyMessages, completing, modelList, modelInfo } = storeToRefs(chatStore);
|
||||
const { isRecording } = storeToRefs(asrStore);
|
||||
|
||||
const inputData = ref("");
|
||||
|
||||
const options = ref<Array<SelectGroupOption | SelectOption>>([]);
|
||||
|
||||
// 处理选中模型的 ID
|
||||
const selectedModelId = computed({
|
||||
get: () => modelInfo.value?.model_id ?? null,
|
||||
set: (id: string | null) => {
|
||||
for (const vendor of modelList.value) {
|
||||
const found = vendor.models.find(model => model.model_id === id);
|
||||
if (found) {
|
||||
modelInfo.value = found;
|
||||
return;
|
||||
}
|
||||
}
|
||||
modelInfo.value = null;
|
||||
},
|
||||
});
|
||||
|
||||
// 监听模型列表变化,更新选项
|
||||
watch(() => modelList.value, (newVal) => {
|
||||
if (newVal) {
|
||||
options.value = newVal.map(vendor => ({
|
||||
type: "group",
|
||||
label: vendor.vendor,
|
||||
key: vendor.vendor,
|
||||
children: vendor.models.map(model => ({
|
||||
label: model.model_name,
|
||||
value: model.model_id,
|
||||
type: model.model_type,
|
||||
})),
|
||||
}));
|
||||
|
||||
if (newVal.length > 0 && newVal[0].models.length > 0) {
|
||||
modelInfo.value = newVal[0].models[0];
|
||||
}
|
||||
console.log("Options updated:", options.value);
|
||||
}
|
||||
}, { immediate: true, deep: true });
|
||||
|
||||
// 发送消息
|
||||
const handleSendMessage = () => {
|
||||
if (inputData.value.trim() === "")
|
||||
return;
|
||||
chatStore.addMessageToHistory(inputData.value);
|
||||
inputData.value = "";
|
||||
};
|
||||
|
||||
// 开关语音输入
|
||||
const toggleRecording = () => {
|
||||
if (isRecording.value) {
|
||||
asrStore.stopRecording();
|
||||
}
|
||||
else {
|
||||
asrStore.startRecording();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
chatStore.getModelList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-8 !pr-4 h-full w-full flex flex-col gap-4 border-l-[24px] border-l-[#FAFAFA] text-base">
|
||||
<NScrollbar class="flex-1 pr-4">
|
||||
<div class="flex items-start mb-4">
|
||||
<span class="text-base w-14 min-w-14">助手:</span>
|
||||
<NTag type="success" class="text-base max-w-full !h-auto">
|
||||
<span class="text-base">你好,我是你的智能助手,请问有什么可以帮助你的吗?</span>
|
||||
</NTag>
|
||||
</div>
|
||||
<div v-for="(msg, idx) in historyMessages" :key="idx" class="flex items-start mb-4">
|
||||
<span v-if="msg.role === 'user'" class="text-base w-14 min-w-14">你:</span>
|
||||
<span v-else class="text-base w-14 min-w-14">助手:</span>
|
||||
<NTag :type="msg.role === 'user' ? 'info' : 'success'" class="max-w-full !h-auto">
|
||||
<markdown :content="msg.content || ''" />
|
||||
</NTag>
|
||||
</div>
|
||||
</NScrollbar>
|
||||
<NInput v-model:value="inputData" type="textarea" placeholder="在这里输入消息" @keyup.enter="handleSendMessage" />
|
||||
<!-- 操作区 -->
|
||||
<div class="flex justify-between items-center gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<NSelect
|
||||
v-model:value="selectedModelId" label-field="label" value-field="value" children-field="children"
|
||||
filterable :options="options"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<NPopconfirm
|
||||
:positive-button-props="{ type: 'error' }" positive-text="清除" negative-text="取消"
|
||||
@positive-click="chatStore.clearHistoryMessages" @negative-click="() => { }"
|
||||
>
|
||||
<template #icon>
|
||||
<ExclamationTriangleIcon class="!w-6 !h-6 text-[#d03050]" />
|
||||
</template>
|
||||
<template #trigger>
|
||||
<NButton :disabled="isRecording || completing" type="warning">
|
||||
清除历史
|
||||
<TrashIcon class="!w-4 !h-4 ml-1" />
|
||||
</NButton>
|
||||
</template>
|
||||
<span>确定要清除历史消息吗?</span>
|
||||
</NPopconfirm>
|
||||
|
||||
<NButton :disabled="completing" @click="toggleRecording">
|
||||
{{ isRecording ? "停止输入" : "语音输入" }}
|
||||
<microphone class="!w-4 !h-4 ml-1" />
|
||||
</NButton>
|
||||
<NButton :disabled="isRecording" :loading="completing" @click="handleSendMessage">
|
||||
发送
|
||||
<PaperAirplaneIcon class="!w-4 !h-4 ml-1" />
|
||||
</NButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user