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);
});
};
const onInput = async () => {
adjustHeight();
if (message.value) {
suggestions.value = await fetchSuggestions(message.value);
} else {
suggestions.value = [];
}
};
const adjustHeight = () => {
const inputBox = refs.inputBox as HTMLTextAreaElement;
inputBox.style.height = 'auto';
inputBox.style.height = inputBox.scrollHeight + 'px';
};
const selectSuggestion = (suggestion: string) => {
message.value = suggestion;
suggestions.value = [];
};
const sendMessage = async () => {
if (!message.value.trim()) return;
sending.value = true;
try {
//
console.log("发送消息: ", message.value);
message.value = "";
suggestions.value = [];
adjustHeight();
} catch (error) {
console.error("发送消息失败: ", error);
} finally {
sending.value = false;
}
};
</script>
<style scoped>
.chat-container {
display: flex;
flex-direction: column;
height: 100%;
} }
return hljs.highlight(str, { language: lang }).value; .chat-messages {
}; flex: 1;
mdIt.use(markdownKatex, { overflow-y: auto;
throwOnError: false, padding: 1rem;
errorColor: "#cc0000", }
output: "html",
});
// async function initMD() { .chat-input-container {
// mdIt.use( display: flex;
// await Shiki({ flex-direction: column;
// themes: { border-top: 1px solid #ccc;
// light: "vitesse-light", padding: 0.5rem;
// dark: "vitesse-dark", }
// },
// })
// );
// mdIt.use(markdownKatex, { .chat-input {
// throwOnError: false, width: 100%;
// errorColor: "#cc0000", resize: none;
// output: "html", padding: 0.5rem;
// }); margin-bottom: 0.5rem;
border: 1px solid #ccc;
border-radius: 0.5rem;
}
// mdInited.value = true; .suggestions {
// } border: 1px solid #ccc;
const userStore = useUserStore(); border-radius: 0.5rem;
background-color: #fff;
max-height: 200px;
overflow-y: auto;
}
const props = defineProps({ .suggestions ul {
chat_messages: { list-style: none;
required: true, padding: 0;
type: Array as () => EntityChatMessage[], margin: 0;
}, }
});
const chat_messages = toRef(props, "chat_messages") as Ref<EntityChatMessage[]>; .suggestions li {
const fileBaseUrl = config.backend + "/api/v1/files"; padding: 0.5rem;
cursor: pointer;
}
onMounted(() => { .suggestions li:hover {
// initMD(); background-color: #f0f0f0;
}); }
</script> </style>