|
|
<template>
|
|
|
<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>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</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">
|
|
|
<MdPreview :editorId="id" :modelValue="message.displayContent" :codeFoldable="false" theme="dark" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-scrollbar>
|
|
|
<div class="main-input">
|
|
|
<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">
|
|
|
<img src="@/assets/images/发送.png" />
|
|
|
</el-button>
|
|
|
</div>
|
|
|
</el-scrollbar>
|
|
|
</form>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
<script setup>
|
|
|
import { MdPreview, MdCatalog } from "md-editor-v3";
|
|
|
import { getToken, getUserInfo } from "@/utils/auth";
|
|
|
import useUserStore from "@/store/modules/user";
|
|
|
import "md-editor-v3/lib/preview.css";
|
|
|
import * as AI from "@/api/AI.js";
|
|
|
import { getAssetsFile } from "@/utils/index.js";
|
|
|
import { computed, onMounted } from "vue";
|
|
|
const userStore = useUserStore();
|
|
|
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 = [
|
|
|
{
|
|
|
title: "热点文章编写",
|
|
|
text: "你是一位编辑,请从数学老师王闰秋鼓励支持其发挥数学…",
|
|
|
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",
|
|
|
},
|
|
|
];
|
|
|
const sendExample = (item) => {
|
|
|
textOne.value = false;
|
|
|
input.value = item.text;
|
|
|
sendText();
|
|
|
};
|
|
|
const aiUserid = computed(() => {
|
|
|
return getUserInfo() ? JSON.parse(getUserInfo()) : "";
|
|
|
});
|
|
|
const sendText = async () => {
|
|
|
if (input.value.trim() === "" || isRequestPending.value) return;
|
|
|
if (input.value.trim() === "") return;
|
|
|
isRequestPending.value = true;
|
|
|
let text = input.value;
|
|
|
textOne.value = false;
|
|
|
input.value = "";
|
|
|
|
|
|
// 添加用户消息
|
|
|
messages.push({ id: Date.now(), type: "user-main", displayContent: text });
|
|
|
const sendData = [
|
|
|
{
|
|
|
content: text,
|
|
|
functionCall: {
|
|
|
arguments: "",
|
|
|
name: "",
|
|
|
thoughts: "",
|
|
|
},
|
|
|
name: "",
|
|
|
role: "user",
|
|
|
userId: aiUserid.value.userId,
|
|
|
},
|
|
|
];
|
|
|
const messageId = Date.now();
|
|
|
const newMessage = reactive({ id: messageId, type: "ai-main", displayContent: "" });
|
|
|
messages.push(newMessage);
|
|
|
const resp = await fetch("https://szyx.sztzjy.com:8042/api/bigModule/createArticleByAi", {
|
|
|
method: "POST",
|
|
|
headers: {
|
|
|
"Content-Type": "application/json",
|
|
|
Authorization: `Bearer ${getToken()}`,
|
|
|
},
|
|
|
body: JSON.stringify(sendData),
|
|
|
});
|
|
|
const reader = resp.body.getReader();
|
|
|
while (1) {
|
|
|
const { done, value } = await reader.read();
|
|
|
if (done) {
|
|
|
break;
|
|
|
}
|
|
|
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);
|
|
|
});
|
|
|
};
|
|
|
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) {
|
|
|
event.preventDefault();
|
|
|
sendText();
|
|
|
}
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.app-containere {
|
|
|
margin: 10px 4px 10px 10px;
|
|
|
border: 1px solid #0452c6;
|
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
|
padding: 25px;
|
|
|
}
|
|
|
.message {
|
|
|
display: flex;
|
|
|
align-items: flex-start;
|
|
|
margin-bottom: 10px;
|
|
|
clear: both;
|
|
|
}
|
|
|
.main-input {
|
|
|
background: #011031;
|
|
|
border-radius: 10px;
|
|
|
border: 1px solid #003e7c;
|
|
|
padding: 10px;
|
|
|
}
|
|
|
.inputCode {
|
|
|
.el-button {
|
|
|
margin-top: auto;
|
|
|
background: #ffffff00;
|
|
|
border-color: #ffffff00;
|
|
|
}
|
|
|
.el-textarea {
|
|
|
--el-input-bg-color: #ffffff00 !important;
|
|
|
--el-input-border-color: #ffffff00;
|
|
|
--el-input-hover-border-color: #ffffff00;
|
|
|
--el-input-focus-border-color: #ffffff00;
|
|
|
--el-input-text-color: #ffffff;
|
|
|
}
|
|
|
}
|
|
|
.ai-main {
|
|
|
display: flex;
|
|
|
justify-content: flex-start;
|
|
|
gap: 1rem;
|
|
|
max-width: 94rem;
|
|
|
clear: both;
|
|
|
.items-end {
|
|
|
align-items: flex-start;
|
|
|
flex-direction: column;
|
|
|
flex-shrink: 0;
|
|
|
display: flex;
|
|
|
position: relative;
|
|
|
}
|
|
|
.items-text {
|
|
|
max-width: calc(100% - 400px);
|
|
|
color: #ffffff;
|
|
|
padding: 0px 20px 0px 10px;
|
|
|
flex-direction: column;
|
|
|
min-width: 0;
|
|
|
background: #011031;
|
|
|
border-radius: 10px;
|
|
|
border: 1px solid #003e7c;
|
|
|
}
|
|
|
}
|
|
|
.user-main {
|
|
|
justify-content: flex-end;
|
|
|
margin-top: 15px;
|
|
|
display: flex;
|
|
|
float: right;
|
|
|
gap: 1rem;
|
|
|
max-width: 94rem;
|
|
|
clear: both;
|
|
|
.items-text {
|
|
|
color: #ffffff;
|
|
|
padding: 0px 20px 0px 10px;
|
|
|
flex-direction: column;
|
|
|
min-width: 0;
|
|
|
background: #011031;
|
|
|
border-radius: 10px;
|
|
|
border: 1px solid #003e7c;
|
|
|
margin-right: 15px;
|
|
|
}
|
|
|
}
|
|
|
.one-AI {
|
|
|
display: flex;
|
|
|
width: 100%;
|
|
|
img {
|
|
|
width: 31px;
|
|
|
height: 31px;
|
|
|
}
|
|
|
.AI-item {
|
|
|
width: calc(100% - 200px);
|
|
|
margin-left: 10px;
|
|
|
background: #011031;
|
|
|
border-radius: 10px;
|
|
|
border: 1px solid #003e7c;
|
|
|
padding: 10px 15px;
|
|
|
color: #ffffff;
|
|
|
p {
|
|
|
font-size: 12px;
|
|
|
}
|
|
|
.examples {
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
margin-top: 60px;
|
|
|
width: calc(100% - 300px);
|
|
|
.example-item {
|
|
|
margin: 0 25px 15px 0;
|
|
|
cursor: pointer;
|
|
|
width: 349px;
|
|
|
height: 72px;
|
|
|
background: #002c5b;
|
|
|
border-radius: 10px;
|
|
|
padding: 10px;
|
|
|
img {
|
|
|
width: 20px;
|
|
|
height: 20px;
|
|
|
}
|
|
|
span {
|
|
|
margin-left: 5px;
|
|
|
font-weight: 400;
|
|
|
font-size: 16px;
|
|
|
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>
|