可信代码库V2版-安全智库页面原型开发、安全检测中心页面原型开发、关于我们页面原型开发
This commit is contained in:
279
src/views/Jyh/KnowledgeHub/Components/AIModal.vue
Normal file
279
src/views/Jyh/KnowledgeHub/Components/AIModal.vue
Normal file
@@ -0,0 +1,279 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user