|
|
|
|
@ -2,38 +2,31 @@
|
|
|
|
|
<div class="app-containere">
|
|
|
|
|
<el-scrollbar ref="scrollbar" height="530px">
|
|
|
|
|
<div class="one-AI" v-if="textOne">
|
|
|
|
|
<img src="@/assets/images/ai.png" alt="" />
|
|
|
|
|
<div class="AI-item">
|
|
|
|
|
<h2>你好,我是AI助手</h2>
|
|
|
|
|
<p>作为你的智能伙伴,我既能写文章、想点子,又能陪你聊天、答疑解惑。</p>
|
|
|
|
|
<p>想知道我还能做什么?点这里快速上手!可以浏览器收藏网站,下次使用更高效哦 ~</p>
|
|
|
|
|
<div class="examples">
|
|
|
|
|
<div style="flex-basis: 100%;">
|
|
|
|
|
<p>你可以试着问我:</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="example-item" v-for="item in itemData" :key="item" @click="sendExample(item)">
|
|
|
|
|
<div style="display:flex">
|
|
|
|
|
<img :src="getImg(item.img)" alt="">
|
|
|
|
|
<span>{{item.title}}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<p>{{item.text}}</p>
|
|
|
|
|
<img src="@/assets/images/ai.png" alt="" />
|
|
|
|
|
<div class="AI-item">
|
|
|
|
|
<h2>你好,我是AI助手</h2>
|
|
|
|
|
<p>作为你的智能伙伴,我既能写文章、想点子,又能陪你聊天、答疑解惑。</p>
|
|
|
|
|
<p>想知道我还能做什么?点这里快速上手!可以浏览器收藏网站,下次使用更高效哦 ~</p>
|
|
|
|
|
<div class="examples">
|
|
|
|
|
<div style="flex-basis: 100%">
|
|
|
|
|
<p>你可以试着问我:</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="example-item" v-for="item in itemData" :key="item" @click="sendExample(item)">
|
|
|
|
|
<div style="display: flex">
|
|
|
|
|
<img :src="getImg(item.img)" alt="" />
|
|
|
|
|
<span>{{ item.title }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<p>{{ item.text }}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
v-for="(message, index) in messages"
|
|
|
|
|
:key="index"
|
|
|
|
|
:class="['message', message.type]"
|
|
|
|
|
>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-for="(message, index) in messages" :key="index" :class="['message', message.type]">
|
|
|
|
|
<div class="items-end" v-if="message.type === 'ai-main'">
|
|
|
|
|
<img src="@/assets/images/ai.png" alt="" />
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
:class="['items-text', message.type]"
|
|
|
|
|
:id="'text-container-' + message.id"
|
|
|
|
|
>
|
|
|
|
|
<p>{{ message.displayContent }}</p>
|
|
|
|
|
<div :class="['items-text', message.type]" :id="'text-container-' + message.id">
|
|
|
|
|
<MdPreview :editorId="id" :modelValue="message.displayContent" :codeFoldable="false" theme="dark" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-scrollbar>
|
|
|
|
|
@ -41,19 +34,8 @@
|
|
|
|
|
<form @submit.prevent="sendText">
|
|
|
|
|
<el-scrollbar height="140px">
|
|
|
|
|
<div class="inputCode" style="display: flex">
|
|
|
|
|
<el-input
|
|
|
|
|
@keydown="handleKeyDown"
|
|
|
|
|
v-model="input"
|
|
|
|
|
type="textarea"
|
|
|
|
|
rows="6"
|
|
|
|
|
resize="none"
|
|
|
|
|
placeholder="文件拖拽进来,使用“/”调用指令,可通过shift+回车换行"
|
|
|
|
|
/>
|
|
|
|
|
<el-button
|
|
|
|
|
class="sendButton"
|
|
|
|
|
:disabled="input === '' || butFlag"
|
|
|
|
|
@click="sendText"
|
|
|
|
|
>
|
|
|
|
|
<el-input @keydown="handleKeyDown" v-model="input" type="textarea" rows="6" resize="none" placeholder="文件拖拽进来,使用“/”调用指令,可通过shift+回车换行" />
|
|
|
|
|
<el-button class="sendButton" :disabled="input === '' || butFlag" @click="sendText">
|
|
|
|
|
<img src="@/assets/images/发送.png" />
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
@ -62,60 +44,66 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { ref, reactive } from "vue";
|
|
|
|
|
<script setup>
|
|
|
|
|
import { MdPreview, MdCatalog } from "md-editor-v3";
|
|
|
|
|
import "md-editor-v3/lib/preview.css";
|
|
|
|
|
import * as AI from "@/api/AI.js";
|
|
|
|
|
import {getAssetsFile} from "@/utils/index.js"
|
|
|
|
|
import { marked } from "marked";
|
|
|
|
|
import "highlight.js/lib/common";
|
|
|
|
|
import hljs from "highlight.js";
|
|
|
|
|
import { getAssetsFile } from "@/utils/index.js";
|
|
|
|
|
const input = ref("");
|
|
|
|
|
const butFlag = ref(false);
|
|
|
|
|
const messages = reactive([]);
|
|
|
|
|
const scrollbar = ref(null);
|
|
|
|
|
const isRequestPending = ref(false);
|
|
|
|
|
const textOne=ref(true)
|
|
|
|
|
const getImg=(img)=>{
|
|
|
|
|
return getAssetsFile(img)
|
|
|
|
|
}
|
|
|
|
|
const itemData=[
|
|
|
|
|
const textOne = ref(true);
|
|
|
|
|
const getImg = (img) => {
|
|
|
|
|
return getAssetsFile(img);
|
|
|
|
|
};
|
|
|
|
|
const itemData = [
|
|
|
|
|
{
|
|
|
|
|
title:'热点文章编写',
|
|
|
|
|
text:'你是一位编辑,请从数学老师王闰秋鼓励支持其发挥数学…',
|
|
|
|
|
img:'热点.png'
|
|
|
|
|
title: "热点文章编写",
|
|
|
|
|
text: "你是一位编辑,请从数学老师王闰秋鼓励支持其发挥数学…",
|
|
|
|
|
img: "热点.png",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title:'暑期实践活动安排',
|
|
|
|
|
text:'仿照这些优秀范例,设计一份初中生2024年暑期社会实践…',
|
|
|
|
|
img:'安排.png'
|
|
|
|
|
title: "暑期实践活动安排",
|
|
|
|
|
text: "仿照这些优秀范例,设计一份初中生2024年暑期社会实践…",
|
|
|
|
|
img: "安排.png",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title:'PPT内容框架',
|
|
|
|
|
text:'现在你需要制作一份PPT,你需要按照我给出的主题来准备…',
|
|
|
|
|
img:'框架.png'
|
|
|
|
|
},{
|
|
|
|
|
title:'最新学术论文检索',
|
|
|
|
|
text:'帮我检索一下今年6月的人工智能领域的最新论文,记得给…',
|
|
|
|
|
img:'检索.png'
|
|
|
|
|
},{
|
|
|
|
|
title:'期末备考计划',
|
|
|
|
|
text:'你是大学期末备考专家,我是一名学【工商管理】的学生…',
|
|
|
|
|
img:'科研论文.png'
|
|
|
|
|
},{
|
|
|
|
|
title:'科普2024世界人工智能大会',
|
|
|
|
|
text:'阅读下面这些链接,科普一下:2024世界人工智能大会究…',
|
|
|
|
|
img:'新闻01.png'
|
|
|
|
|
title: "PPT内容框架",
|
|
|
|
|
text: "现在你需要制作一份PPT,你需要按照我给出的主题来准备…",
|
|
|
|
|
img: "框架.png",
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
const sendExample=(item)=>{
|
|
|
|
|
textOne.value=false
|
|
|
|
|
input.value=item.text
|
|
|
|
|
sendText()
|
|
|
|
|
}
|
|
|
|
|
const sendText = async() => {
|
|
|
|
|
if (input.value.trim() === '' || isRequestPending.value) return;
|
|
|
|
|
{
|
|
|
|
|
title: "最新学术论文检索",
|
|
|
|
|
text: "帮我检索一下今年6月的人工智能领域的最新论文,记得给…",
|
|
|
|
|
img: "检索.png",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "期末备考计划",
|
|
|
|
|
text: "你是大学期末备考专家,我是一名学【工商管理】的学生…",
|
|
|
|
|
img: "科研论文.png",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "科普2024世界人工智能大会",
|
|
|
|
|
text: "阅读下面这些链接,科普一下:2024世界人工智能大会究…",
|
|
|
|
|
img: "新闻01.png",
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
const sendExample = (item) => {
|
|
|
|
|
textOne.value = false;
|
|
|
|
|
input.value = item.text;
|
|
|
|
|
sendText();
|
|
|
|
|
};
|
|
|
|
|
const sendText = async () => {
|
|
|
|
|
if (input.value.trim() === "" || isRequestPending.value) return;
|
|
|
|
|
if (input.value.trim() === "") return;
|
|
|
|
|
isRequestPending.value = true;
|
|
|
|
|
isRequestPending.value = true;
|
|
|
|
|
let text = input.value;
|
|
|
|
|
textOne.value=false
|
|
|
|
|
textOne.value = false;
|
|
|
|
|
input.value = "";
|
|
|
|
|
|
|
|
|
|
// 添加用户消息
|
|
|
|
|
@ -134,34 +122,52 @@ const sendText = async() => {
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
const messageId = Date.now();
|
|
|
|
|
const newMessage = reactive({ id: messageId, type: 'ai-main', displayContent: '' });
|
|
|
|
|
0;
|
|
|
|
|
const newMessage = reactive({ id: messageId, type: "ai-main", displayContent: "" });
|
|
|
|
|
messages.push(newMessage);
|
|
|
|
|
const resp=await fetch('http://192.168.2.16:9868/api/bigModule/createArticleByAi',{
|
|
|
|
|
method: 'POST',
|
|
|
|
|
const resp = await fetch("http://192.168.2.28:9868/api/bigModule/createArticleByAi", {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify(sendData),
|
|
|
|
|
})
|
|
|
|
|
const reader=resp.body.getReader()
|
|
|
|
|
while(1){
|
|
|
|
|
const {done,value}=await reader.read()
|
|
|
|
|
if(done){
|
|
|
|
|
break
|
|
|
|
|
});
|
|
|
|
|
const reader = resp.body.getReader();
|
|
|
|
|
while (1) {
|
|
|
|
|
const { done, value } = await reader.read();
|
|
|
|
|
if (done) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
const decoder=new TextDecoder()
|
|
|
|
|
const tet=decoder.decode(value)
|
|
|
|
|
newMessage.displayContent+=tet
|
|
|
|
|
const decoder = new TextDecoder("utf-8");
|
|
|
|
|
const tet = decoder.decode(value, { stream: true });
|
|
|
|
|
|
|
|
|
|
await typeText(newMessage, tet);
|
|
|
|
|
}
|
|
|
|
|
isRequestPending.value = false;
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
const scrollContainer = scrollbar.value?.$el.querySelector('.el-scrollbar__wrap');
|
|
|
|
|
if (scrollContainer) {
|
|
|
|
|
scrollContainer.scrollTop = scrollContainer.scrollHeight;
|
|
|
|
|
}
|
|
|
|
|
}, 10);
|
|
|
|
|
});
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
const scrollContainer = scrollbar.value?.$el.querySelector(".el-scrollbar__wrap");
|
|
|
|
|
if (scrollContainer) {
|
|
|
|
|
scrollContainer.scrollTop = scrollContainer.scrollHeight;
|
|
|
|
|
}
|
|
|
|
|
}, 10);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
const typeText = (message, text) => {
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
let index = 0;
|
|
|
|
|
function type() {
|
|
|
|
|
if (index < text.length) {
|
|
|
|
|
const nextChar = text[index];
|
|
|
|
|
message.displayContent += nextChar;
|
|
|
|
|
index++;
|
|
|
|
|
setTimeout(type, 30); // 调整打字速度
|
|
|
|
|
} else {
|
|
|
|
|
resolve();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
type();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
const handleKeyDown = (event) => {
|
|
|
|
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
|
|
|
@ -171,11 +177,11 @@ const handleKeyDown = (event) => {
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.app-containere {
|
|
|
|
|
margin: 10px 4px 10px 10px;
|
|
|
|
|
box-shadow: 0px 0px 13px 0px #0452c6;
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
border: 1px solid #0452c6;
|
|
|
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
|
|
|
padding: 25px;
|
|
|
|
|
}
|
|
|
|
|
.message {
|
|
|
|
|
@ -247,50 +253,69 @@ const handleKeyDown = (event) => {
|
|
|
|
|
margin-right: 15px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.one-AI{
|
|
|
|
|
.one-AI {
|
|
|
|
|
display: flex;
|
|
|
|
|
width: 100%;
|
|
|
|
|
img{
|
|
|
|
|
img {
|
|
|
|
|
width: 31px;
|
|
|
|
|
height: 31px;
|
|
|
|
|
}
|
|
|
|
|
.AI-item{
|
|
|
|
|
.AI-item {
|
|
|
|
|
width: calc(100% - 200px);
|
|
|
|
|
margin-left: 10px;
|
|
|
|
|
background: #011031;
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
border: 1px solid #003E7C;
|
|
|
|
|
border: 1px solid #003e7c;
|
|
|
|
|
padding: 10px 15px;
|
|
|
|
|
color:#FFFFFF;
|
|
|
|
|
p{
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
p {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
.examples{
|
|
|
|
|
.examples {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
margin-top: 60px;
|
|
|
|
|
width: calc(100% - 300px);
|
|
|
|
|
.example-item{
|
|
|
|
|
.example-item {
|
|
|
|
|
margin: 0 25px 15px 0;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
width: 349px;
|
|
|
|
|
height: 72px;
|
|
|
|
|
background: #002C5B;
|
|
|
|
|
background: #002c5b;
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
padding: 10px;
|
|
|
|
|
img{
|
|
|
|
|
img {
|
|
|
|
|
width: 20px;
|
|
|
|
|
height: 20px;
|
|
|
|
|
}
|
|
|
|
|
span{
|
|
|
|
|
span {
|
|
|
|
|
margin-left: 5px;
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
color: #FFFFFF;
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.md-editor-dark,
|
|
|
|
|
.md-editor-modal-container[data-theme="dark"] {
|
|
|
|
|
--md-color: #ffffff;
|
|
|
|
|
--md-bk-color: transparent;
|
|
|
|
|
--md-border-color: transparent;
|
|
|
|
|
--md-border-color-hover: transparent;
|
|
|
|
|
--md-border-color-focus: transparent;
|
|
|
|
|
--md-border-color-active: transparent;
|
|
|
|
|
--md-border-color-disabled: transparent;
|
|
|
|
|
--md-border-color-focus-inner: transparent;
|
|
|
|
|
--md-border-color-hover-inner: transparent;
|
|
|
|
|
--md-border-color-active-inner: transparent;
|
|
|
|
|
--md-border-color-disabled-inner: transparent;
|
|
|
|
|
--md-border-color-focus-outer: transparent;
|
|
|
|
|
--md-border-color-hover-outer: transparent;
|
|
|
|
|
--md-border-color-active-outer: transparent;
|
|
|
|
|
}
|
|
|
|
|
.md-editor-preview hr {
|
|
|
|
|
border-color: #ffffff;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|