提交 df81033b 编写于 作者: X xqx_12138

Auto Commit

上级 29e92c29
<template> <template>
<div class="container ivu-p"> <div class="container">
<div class="header">
<h2>AI 女友陪聊助手</h2>
<Button type="primary" @click="handleNewChat">开始新对话</Button>
</div>
<div class="dialog"> <div class="dialog">
<template v-for="(item, index) in dialogs" :key="index"> <template v-for="(item, index) in dialogs" :key="index">
<div class="dialog-item" :class="{ 'dialog-item-me': item.role === 'me', 'dialog-item-ai': item.role === 'ai' }"> <div class="dialog-item" :class="{'dialog-item-me': item.role === 'me', 'dialog-item-ai': item.role === 'ai'}">
<img :src="item.avatar" :class="{'avatar-me': item.role === 'me', 'avatar-ai': item.role === 'ai'}" />
<div class="dialog-item-main">{{ item.text }}</div> <div class="dialog-item-main">{{ item.text }}</div>
</div> </div>
</template> </template>
</div> </div>
<div class="question ivu-mt"> <div class="input-area">
<Input v-model="question" type="textarea" :autosize="{ minRows: 4, maxRows: 6 }" placeholder="输入你的问题" /> <Input v-model="question" type="textarea" :autosize="{ minRows: 3, maxRows: 5 }" placeholder="看看今天聊什么吧" />
<Row class="ivu-mt"> <Button type="primary" @click="handleSend">发送</Button>
<Col>
<Button type="primary" size="large" icon="md-send" :loading="loading" @click="handleSend">发送</Button>
</Col>
<Col>
<Button size="large" class="ivu-ml" icon="md-add" :disabled="loading" @click="handleNewChat">新对话</Button>
</Col>
</Row>
<Typography class="ivu-text-center ivu-m">
Powered By <img src="./assets/logo.png" class="logo"> <a href="https://inscode.net" target="_blank">InsCode.net</a>
</Typography>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { fetchEventSource } from '@microsoft/fetch-event-source'; import { fetchEventSource } from '@microsoft/fetch-event-source';
import { apiKey, apiUrl } from './api'; import { apiKey, apiUrl } from './api'; // 引入 GPT API 地址和密钥
export default { export default {
data() { data() {
return { return {
question: '', question: '',
dialogs: [],
loading: false, loading: false,
dialogs: [] userName: '小美', // 默认的AI女友名字
} userMood: '温柔', // 默认的AI女友个性
userAvatar: 'https://c-ssl.duitang.com/uploads/item/201702/07/20170207205844_G54rE.jpeg', // 你可以使用自己的头像 URL
aiAvatar: 'https://img.wxcha.com/m00/17/af/718aa0fd75674e749c8f4ab9875b8d78.jpg?down', // AI 女友的头像
aiTopics: [ // 随机话题列表
"你今天过得怎么样?",
"最近有看什么好看的电影吗?",
"我学到了一些新知识,你想听听吗?",
"你喜欢什么类型的音乐?",
"有什么让我帮忙的事情吗?"
]
};
},
mounted() {
// 页面加载时 AI 女友主动发送第一条随机话题
this.startConversation();
}, },
methods: { methods: {
handleSend() { handleSend() {
...@@ -43,115 +54,143 @@ export default { ...@@ -43,115 +54,143 @@ export default {
const question = this.question; const question = this.question;
this.question = ''; this.question = '';
// 添加用户问题到对话框
this.dialogs.push({ this.dialogs.push({
id: this.dialogs.length + 1,
role: 'me', role: 'me',
text: question text: question,
avatar: this.userAvatar // 使用用户的头像
}); });
const aiDialogID = this.dialogs.length + 1; // AI 思考中
const aiDialogID = this.dialogs.length;
this.dialogs.push({ this.dialogs.push({
id: aiDialogID,
role: 'ai', role: 'ai',
text: 'AI 思考中...' text: `${this.userName} 正在思考...`,
avatar: this.aiAvatar
}); });
const dialog = this.dialogs.find(item => item.id === aiDialogID);
/**
* 发送请求,InsCode 已经集成了 GPT 能力
* 在 vite.config.js 中已通过环境变量写入了 apiKey(该 key 是动态写入使用者的,在 IDE 中是作者,发布到社区是运行该作品的用户)
* 发布到社区后,将消耗运行者的额度
* 注意:如果部署应用,任何人通过部署后的域名访问应用时,都将消耗部署者的额度
*/
const body = { const body = {
messages: [ messages: [
{ { role: 'system', content: `你现在是一个温柔的虚拟女友,你的名字是 ${this.userName}。` },
role: 'user', { role: 'user', content: question }
content: question
}
], ],
apikey: apiKey apikey: apiKey
} };
const source = fetchEventSource(apiUrl, { const dialog = this.dialogs[aiDialogID];
// 请求 GPT API
fetchEventSource(apiUrl, {
method: 'POST', method: 'POST',
headers: { headers: { 'Content-Type': 'application/json' },
'Content-Type': 'application/json'
},
body: JSON.stringify(body), body: JSON.stringify(body),
onopen: (response) => { onopen: () => {
dialog.text = ''; dialog.text = '';
}, },
onmessage: (msg) => { onmessage: (msg) => {
if (msg.data === '[DONE]') { if (msg.data === '[DONE]') {
this.loading = false; this.loading = false;
return; return;
}; }
const data = JSON.parse(msg.data); const data = JSON.parse(msg.data);
const finish_reason = data.choices[0].finish_reason; const content = data.choices[0]?.delta?.content;
const finish = finish_reason === 'stop' || finish_reason === 'length'; if (content) {
const content = data.choices[0].delta.content; dialog.text += content;
if (finish) {
this.loading = false;
} else if (content) {
const text = content;
dialog.text += text;
} }
}, },
onerror: (err) => { onerror: (err) => {
console.log("error", err); console.error('错误:', err);
this.loading = false;
} }
}); });
}, },
handleNewChat() { handleNewChat() {
this.dialogs = []; this.dialogs = [];
this.startConversation();
},
startConversation() {
// 女友主动发送随机话题
const randomTopic = this.aiTopics[Math.floor(Math.random() * this.aiTopics.length)];
this.dialogs.push({
role: 'ai',
text: `你好,我是你的 AI 女友 ${this.userName}${randomTopic}`,
avatar: this.aiAvatar
});
} }
} }
} };
</script> </script>
<style>
<style scoped>
.container { .container {
height: 100%; padding: 20px;
max-width: 600px;
margin: 0 auto;
}
.header {
display: flex; display: flex;
flex-direction: column; justify-content: space-between;
align-items: center;
} }
.dialog { .dialog {
flex: 1; flex: 1;
overflow: auto; overflow-y: auto;
max-height: 400px;
margin-bottom: 20px;
} }
.dialog-item { .dialog-item {
display: flex; display: flex;
align-items: center;
margin: 10px 0;
}
.dialog-item-me {
justify-content: flex-end; /* 用户对话整体对齐到右侧 */
}
.dialog-item-ai {
justify-content: flex-start; /* AI 对话整体对齐到左侧 */
} }
.dialog-item-main { .dialog-item-main {
max-width: 80%; max-width: 60%;
padding: 8px; padding: 10px;
border-radius: 10px;
word-wrap: break-word; word-wrap: break-word;
margin-top: 16px;
border-radius: 4px;
}
.dialog-item-me {
justify-content: flex-end;
} }
.dialog-item-me .dialog-item-main { .dialog-item-me .dialog-item-main {
background-color: antiquewhite; margin-left: 10px; /* 用户对话内容左侧间距 */
margin-right: 0; /* 去掉右侧间距 */
background-color: #d1ffd1; /* 用户对话背景色 */
} }
.dialog-item-ai .dialog-item-main { .dialog-item-ai .dialog-item-main {
background-color: #eee; margin-right: 10px; /* AI 对话内容右侧间距 */
margin-left: 0; /* 去掉左侧间距 */
background-color: #eee; /* AI 对话背景色 */
} }
.logo{ .avatar-me {
width: 16px; width: 40px;
height: 16px; height: 40px;
vertical-align: middle; border-radius: 50%;
position: relative; margin-left: 10px; /* 用户头像右侧间距 */
top: -1px;
} }
</style> .avatar-ai {
\ No newline at end of file width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px; /* AI 头像左侧间距 */
}
.input-area {
display: flex;
align-items: center;
position: fixed; /* 将 input-area 固定 */
bottom: 0; /* 置于页面底部 */
left: 0; /* 使其与页面左侧对齐 */
right: 0; /* 使其与页面右侧对齐 */
padding: 10px;
background-color: white; /* 添加背景色 */
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
}
.input-area Input {
flex: 1;
margin-right: 10px;
}
</style>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册