1
0
forked from Leaf/amber-ui

更新 src/components/chat/MessageList.vue

This commit is contained in:
jwai 2024-10-15 11:48:36 +00:00
parent 75b4d63b62
commit e94697e245

View File

@ -1,188 +1,138 @@
<template> <template>
<div class="text-base"> <div class="chat-container">
<n-divider> <small class="select-none">这是聊天的开头</small> </n-divider> <div class="chat-messages">
<div v-for="(message, index) in chat_messages" :key="index">
<div class="mt-10 mb-10"></div>
<div v-if="message.role === 'file'">
<!-- 文件类型 -->
<n-flex justify="end">
<div class="flex items-center flex-nowrap">
<div class="flex items-end flex-col">
<div>
<n-divider class="!p-0 !m-0" title-placement="right">
{{ userStore.user.name }}
</n-divider>
</div>
<n-image
v-if="
message.file && message.file.mime_type?.startsWith('image/')
"
width="100"
:src="fileBaseUrl + '/download/' + message.file.file_hash"
/>
<n-text italic depth="3" v-else> 你上传了一个文件 </n-text>
</div> </div>
<div class="relative h-full"> //advice and textarea
<n-avatar <div class="chat-input-container">
round <textarea
size="large" v-model="message"
:src="userStore.user.avatar" @input="onInput"
class="ml-3 min-w-10 absolute top-0" ref="inputBox"
/> rows="1"
</div> class="chat-input"
</div> placeholder="输入你的消息..."
</n-flex> ></textarea>
</div> <button @click="sendMessage" :disabled="sending">发送</button>
<div v-else-if="(message.role === 'user' || message.role === 'user_hide' || message.role === 'user_later') && message.content"> <div v-if="suggestions.length" class="suggestions">
<!-- 用户消息 --> <ul>
<n-flex justify="end"> <li
<div class="flex items-center flex-nowrap"> v-for="(suggestion, index) in suggestions"
<!-- <vue-markdown-it :key="index"
:source="message.content" @click="selectSuggestion(suggestion)"
:options="markdownOptions"
:plugins="markdownPlugins"
/> -->
<!-- <v-md-preview :text="message.content" height="500px"></v-md-preview> -->
<div class="flex items-end flex-col">
<div>
<n-divider class="!p-0 !m-0" title-placement="right">
{{ userStore.user.name }}
</n-divider>
</div>
<div
v-if="mdInited"
class="break-all break-words markdown-body"
v-html="mdIt.render(message.content)"
></div>
</div>
<div class="relative h-full">
<n-avatar
round
size="large"
:src="userStore.user.avatar"
class="ml-3 min-w-10 absolute top-0"
/>
</div>
</div>
</n-flex>
</div>
<div v-else-if="message.role === 'assistant' && message.content">
<!-- 助理消息 -->
<n-flex justify="start" class="!flex-nowrap">
<div class="relative h-full">
<n-avatar
round
size="large"
:src="leaflowPng"
class="min-w-10 min-h-10 p-1.5 absolute top-0 !bg-transparent"
/>
</div>
<div class="flex items-center flex-nowrap">
<!-- <vue-markdown-it
:source="message.content"
:options="markdownOptions"
:plugins="[markdownPlugins]"
/> -->
<!-- <div v-html="mdIt.render('# Math Rulez! \n $\\sqrt{3x-1}+(1+x)^2$')"></div> -->
<!-- message.content 变化时重新渲染 -->
<div>
<div
v-if="
message.assistant_id &&
message.assistant !== null &&
message.assistant?.name !== ''
"
> >
<n-divider class="!p-0 !m-0" title-placement="left"> {{ suggestion }}
{{ message.assistant?.name }} </li>
</n-divider> </ul>
</div>
<div
v-if="mdInited"
class="break-all break-words markdown-body"
v-html="mdIt.render(message.content)"
></div>
</div>
<!-- <div v-html="mdIt.render(message.content)"></div> -->
<!-- <v-md-preview :text="message.content" height="500px"></v-md-preview> -->
</div>
</n-flex>
</div> </div>
</div> </div>
</div> </div>
<!-- 这里要加上输入框占的部分但是那个占了很多所以设置大一点 -->
<div style="margin-bottom: 10rem"></div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { Ref } from "vue"; import { ref } from "vue";
import { EntityChatMessage } from "@/api";
import { useUserStore } from "@/stores/user";
import leaflowPng from "@/assets/images/leaflow.png";
import markdownKatex from "@traptitech/markdown-it-katex";
import markdownIt from "markdown-it";
// highlightjs
import hljs from "highlight.js";
import config from "@/config/config";
const mdIt = markdownIt(); const message = ref("");
const mdInited = ref(true); const suggestions = ref<string[]>([]);
const sending = ref(false);
const unsupportedLanguages = ["assembly", "blade", "vue"]; //
mdIt.options.highlight = function (str: string, lang: string) { const fetchSuggestions = (query: string) => {
// TODO: //
lang = "text" return new Promise<string[]>((resolve) => {
if (!lang || unsupportedLanguages.includes(lang)) { setTimeout(() => {
// return str; resolve(["建议1", "建议2", "建议3"].filter((suggestion) => suggestion.includes(query)));
lang = "text" }, 300);
} });
return hljs.highlight(str, { language: lang }).value;
}; };
mdIt.use(markdownKatex, {
throwOnError: false,
errorColor: "#cc0000",
output: "html",
});
// async function initMD() { const onInput = async () => {
// mdIt.use( adjustHeight();
// await Shiki({ if (message.value) {
// themes: { suggestions.value = await fetchSuggestions(message.value);
// light: "vitesse-light", } else {
// dark: "vitesse-dark", suggestions.value = [];
// }, }
// }) };
// );
// mdIt.use(markdownKatex, { const adjustHeight = () => {
// throwOnError: false, const inputBox = refs.inputBox as HTMLTextAreaElement;
// errorColor: "#cc0000", inputBox.style.height = 'auto';
// output: "html", inputBox.style.height = inputBox.scrollHeight + 'px';
// }); };
// mdInited.value = true; const selectSuggestion = (suggestion: string) => {
// } message.value = suggestion;
const userStore = useUserStore(); suggestions.value = [];
};
const props = defineProps({ const sendMessage = async () => {
chat_messages: { if (!message.value.trim()) return;
required: true,
type: Array as () => EntityChatMessage[],
},
});
const chat_messages = toRef(props, "chat_messages") as Ref<EntityChatMessage[]>; sending.value = true;
const fileBaseUrl = config.backend + "/api/v1/files"; try {
//
onMounted(() => { console.log("发送消息: ", message.value);
// initMD(); message.value = "";
}); suggestions.value = [];
adjustHeight();
} catch (error) {
console.error("发送消息失败: ", error);
} finally {
sending.value = false;
}
};
</script> </script>
<style scoped>
.chat-container {
display: flex;
flex-direction: column;
height: 100%;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 1rem;
}
.chat-input-container {
display: flex;
flex-direction: column;
border-top: 1px solid #ccc;
padding: 0.5rem;
}
.chat-input {
width: 100%;
resize: none;
padding: 0.5rem;
margin-bottom: 0.5rem;
border: 1px solid #ccc;
border-radius: 0.5rem;
}
.suggestions {
border: 1px solid #ccc;
border-radius: 0.5rem;
background-color: #fff;
max-height: 200px;
overflow-y: auto;
}
.suggestions ul {
list-style: none;
padding: 0;
margin: 0;
}
.suggestions li {
padding: 0.5rem;
cursor: pointer;
}
.suggestions li:hover {
background-color: #f0f0f0;
}
</style>