280 lines
6.7 KiB
Vue
280 lines
6.7 KiB
Vue
<template>
|
||
<d-modal
|
||
v-model="visible"
|
||
title="AI问答"
|
||
@ok="handleClose"
|
||
:cancel="handleClose"
|
||
style="width: 900px;"
|
||
>
|
||
<div class="log-content">
|
||
<McLayout>
|
||
<!-- <McLayoutHeader>
|
||
<McHeader :logoImg="'/logo.svg'" :title="'MateChat'"></McHeader>
|
||
</McLayoutHeader>-->
|
||
<McLayoutContent v-if="visible" style="margin: 16px 0;">
|
||
<div ref="conversationRef" class="conversation-area">
|
||
<!-- 欢迎提示 -->
|
||
<div v-if="messages.length === 0" class="welcome-message">
|
||
|
||
<div class="welcome-page">
|
||
<div class="home-title">
|
||
<img src="@/assets/imgs/jyh/aiLogo2.png" alt="logo" />
|
||
<h1>可信开源代码库AI助手</h1>
|
||
</div>
|
||
<div class="home-detail">
|
||
<div class="home-detail-1">Hi,欢迎使用可信开源代码库AI助手</div>
|
||
<div class="home-detail-2">可信开源代码库AI助手 可以辅助研发人员进行软件查询、批量校验、查看软件详情等。</div>
|
||
<div class="home-detail-2">作为AI模型,可信开源代码库AI助手 提供的答案可能不总是确定或准确的,但您的反馈可以帮助 可信开源代码库AI助手 做得更好。</div>
|
||
</div>
|
||
</div>
|
||
<!-- <div class="welcome-icon">-->
|
||
<!-- <i class="fa fa-comment-dots"></i>-->
|
||
<!-- </div>-->
|
||
<!-- <div class="welcome-text">-->
|
||
<!-- <h3>请输入你想问的内容</h3>-->
|
||
<!-- <p>与AI助手开始对话,获取智能回答</p>-->
|
||
<!-- </div>-->
|
||
</div>
|
||
|
||
<template v-for="(msg, idx) in messages" :key="idx">
|
||
<McBubble v-if="msg.role === 'user'" :content="msg.content" :align="'right'"></McBubble>
|
||
<McBubble v-else :loading="msg.loading ?? false">
|
||
<McMarkdownCard :content="msg.content" :theme="theme"></McMarkdownCard>
|
||
</McBubble>
|
||
</template>
|
||
</div>
|
||
</McLayoutContent>
|
||
<McLayoutSender>
|
||
<McInput :value="inputValue" :maxLength="2000" @submit="onSubmit" showCount></McInput>
|
||
</McLayoutSender>
|
||
</McLayout>
|
||
</div>
|
||
</d-modal>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, watch,nextTick } from 'vue';
|
||
import { Message } from 'vue-devui';
|
||
import axios from 'axios';
|
||
import {uvdV1Chat} from "@/api/inboundTask/inboundTaskApi";
|
||
|
||
const messages = ref([]);
|
||
const history = ref([]);
|
||
const resultInfo = ref(null);
|
||
const inputValue = ref('');
|
||
const conversationRef = ref(null);
|
||
|
||
// 组件状态
|
||
const visible = ref(false);
|
||
const VITE_AI_API_HOST = import.meta.env.VITE_AI_API_HOST;
|
||
// 打开弹框时调用接口
|
||
const open = async (id) => {
|
||
if (!id) return Message.error('缺少result_id参数');
|
||
visible.value = true;
|
||
resultInfo.value = id
|
||
};
|
||
|
||
const onSubmit = (e) => {
|
||
if(e === '') {
|
||
return;
|
||
}
|
||
if(messages.value[messages.value.length-1]?.loading) return;
|
||
inputValue.value = '';
|
||
messages.value.push({
|
||
role: 'user',
|
||
content: e,
|
||
});
|
||
nextTick(() => {
|
||
conversationRef.value?.scrollTo({
|
||
top: conversationRef.value.scrollHeight,
|
||
behavior: 'smooth',
|
||
});
|
||
});
|
||
getAIAnswer(e);
|
||
};
|
||
|
||
const getAIAnswer = async (content) => {
|
||
messages.value.push({
|
||
role: 'assistant',
|
||
content: '',
|
||
loading: true,
|
||
});
|
||
|
||
const res = await uvdV1Chat({
|
||
"resultInfo": resultInfo.value,
|
||
"query": content,
|
||
"history": history.value || []
|
||
});
|
||
|
||
let reply = res?.data?.data?.reply || '';
|
||
history.value.push({role: "user", content: content});
|
||
history.value.push({role: "assistant", content: reply});
|
||
messages.value[messages.value.length - 1].content = reply;
|
||
messages.value[messages.value.length - 1].loading = false;
|
||
nextTick(() => {
|
||
conversationRef.value?.scrollTo({
|
||
top: conversationRef.value.scrollHeight
|
||
});
|
||
});
|
||
/* 模拟流式数据返回 */
|
||
// setTimeout(async () => {
|
||
// messages.value.at(-1).loading = false;
|
||
// for (let i = 0; i < reply.length;) {
|
||
// await new Promise(r => setTimeout(r, 300 * Math.random()));
|
||
// messages.value[messages.value.length - 1].content = reply.slice(0, i += Math.random() * 10);
|
||
// nextTick(() => {
|
||
// conversationRef.value?.scrollTo({
|
||
// top: conversationRef.value.scrollHeight
|
||
// });
|
||
// });
|
||
// }
|
||
// }, 1000);
|
||
};
|
||
|
||
// 暴露给父组件的方法
|
||
defineExpose({
|
||
open // 通过ref调用open方法传递resultId
|
||
});
|
||
|
||
// 关闭弹框
|
||
const handleClose = () => {
|
||
messages.value = [];
|
||
history.value = [];
|
||
result_id.value = null;
|
||
inputValue.value = '';
|
||
visible.value = false;
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.log-content {
|
||
padding: 24px;
|
||
line-height: 1.6;
|
||
white-space: pre-wrap; // 保留日志换行
|
||
word-break: break-all; // 自动换行长单词
|
||
min-height: 200px;
|
||
|
||
p {
|
||
margin: 0;
|
||
color: #333;
|
||
}
|
||
}
|
||
|
||
.conversation-area,
|
||
.welcome-page {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: auto;
|
||
padding: 0 12px;
|
||
min-height: 400px;
|
||
max-height: 600px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.conversation-area {
|
||
gap: 8px;
|
||
}
|
||
|
||
.welcome-message {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 100%;
|
||
padding: 32px;
|
||
text-align: center;
|
||
|
||
.welcome-icon {
|
||
width: 64px;
|
||
height: 64px;
|
||
background-color: #e6f4ff;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 16px;
|
||
|
||
i {
|
||
font-size: 28px;
|
||
color: #1677ff;
|
||
}
|
||
}
|
||
|
||
.welcome-text {
|
||
h3 {
|
||
font-size: 18px;
|
||
font-weight: 500;
|
||
color: #333;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
p {
|
||
font-size: 14px;
|
||
color: #666;
|
||
}
|
||
}
|
||
}
|
||
|
||
.home-title {
|
||
display: flex;
|
||
flex-shrink: 0;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 20px;
|
||
h1 {
|
||
color: #191919;
|
||
font-family: Huawei Sans;
|
||
font-weight: bold;
|
||
font-size: 32px;
|
||
line-height: 48px;
|
||
letter-spacing: 0px;
|
||
text-align: center;
|
||
|
||
}
|
||
|
||
img {
|
||
width: 64px;
|
||
//filter: brightness(0);
|
||
}
|
||
}
|
||
|
||
.home-detail {
|
||
.home-detail-1 {
|
||
color: #191919;
|
||
font-family: HarmonyOS Sans SC;
|
||
font-weight: medium;
|
||
font-size: 24px;
|
||
line-height: 32px;
|
||
letter-spacing: 0px;
|
||
text-align: center;
|
||
margin-bottom: 24px;
|
||
}
|
||
.home-detail-2 {
|
||
color: #777777;
|
||
font-family: HarmonyOS Sans SC;
|
||
font-weight: regular;
|
||
font-size: 14px;
|
||
line-height: 22px;
|
||
letter-spacing: 0px;
|
||
text-align: center;
|
||
|
||
}
|
||
}
|
||
|
||
.welcome-page {
|
||
//height: 90%;
|
||
width: 100%;
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: auto;
|
||
padding: 0 12px;
|
||
}
|
||
|
||
.welcome-page {
|
||
gap: 16px;
|
||
justify-content: center;
|
||
}
|
||
</style>
|