本次前端采用了vue,具体呈现的效果如下
1. 组件结构
<template>
<div class="chatHome">
<div class="chatLeft">
<div class="title">
<br/>
<br/>
<h1>Question Generation</h1>
</div>
<div class="options">
<input v-model="newOption" placeholder="输入选项" />
<button class = "addOption" @click="addOption">提交</button>
<div class="options-wrapper">
<div
class="optionList"
v-for="option in options"
:key="option.id"
:class="{ selected: option.id === currentOption }"
@click="selectOption(option)"
@mouseover="showDeleteButton(option.id)"
@mouseleave="hideDeleteButton(option.id)"
>
{{ option.knowledge }}
<button
v-if="option.showDelete"
class="delete-button"
@click.stop="deleteOption(option.id)"
>
删除
</button>
</div>
</div>
<div class="button-group">
<button class="regenerate-button" @click="reGenerate">重新生成</button>
<button
class="collect-button"
@click="collect"
:class="{ collected: isOptionCollected }"
>
{{ collectButtonText }}
</button>
</div>
</div>
</div>
<div class="chatRight">
<div v-if="showChatWindow">
<ChatWindow :frinedInfo="chatWindowInfo"></ChatWindow>
</div>
<div class="showIcon" v-else>
<span class="iconfont icon-snapchat"></span>
</div>
</div>
</div>
</template>
2. 脚本逻辑
import OptionCard from "@/components/OptionCard.vue";
import ChatWindow from "./chatwindow.vue";
import { generateUUID } from "@/util/util.js";
import { chatgpt_generate } from "@/api/getData";
import Showdown from "showdown";
import { options } from "axios";
export default {
name: "App",
components: {
OptionCard,
ChatWindow,
},
data() {
return {
newOption: "",
options: [],
currentOption: "",
showChatWindow: false,
chatWindowInfo: "",
isSend: false,
};
},
mounted() {
if(localStorage.getItem("question") != null) {
let question = JSON.parse(localStorage.getItem("question"));
let collection = JSON.parse(localStorage.getItem("collection"));
for(let i = 0; i < question.length; i++) {
question[i].showDelete = false;
question[i].isCollected = collection.some(item => item.id === question[i].id);
this.options.push(question[i]);
}
}
},
methods: {
addOption() {
if (this.newOption.trim() !== "") {
let new_option = {
knowledge: this.newOption.trim(),
content: "",
analysis: "",
answer: "",
id: generateUUID(),
showDelete: false,
isCollected: false,
};
let data = { msg: new_option.knowledge.toString() };
this.newOption = '';
chatgpt_generate(data).then((res) => {
let converter = new Showdown.Converter();
let htmlStr = res.msg;
let convertedHtml = converter.makeHtml(htmlStr);
this.isSend = false;
let question = JSON.parse(localStorage.getItem("question"));
let threePart = this.extractContent(convertedHtml);
new_option.content = threePart[0];
new_option.analysis = threePart[1];
new_option.answer = threePart[2];
this.options.unshift(new_option);
this.showChatWindow = true;
this.chatWindowInfo = new_option;
this.isSend = true;
question.unshift(new_option);
localStorage.setItem("question", JSON.stringify(question));
this.currentOption = new_option.id;
});
} else {
this.$message({
message: "消息为空",
type: "warning",
});
}
},
showDeleteButton(id) {
const option = this.options.find(opt => opt.id === id);
if (option) option.showDelete = true;
},
hideDeleteButton(id) {
const option = this.options.find(opt => opt.id === id);
if (option) option.showDelete = false;
},
deleteOption(id) {
this.options = this.options.filter(option => option.id !== id);
localStorage.setItem("question", JSON.stringify(this.options));
this.showChatWindow = false;
},
extractContent(htmlString) {
let parser = new DOMParser();
let doc = parser.parseFromString(htmlString, 'text/html');
let contentSections = ["题目内容:", "解析:", "答案:"];
let result = [];
contentSections.forEach((section, index) => {
let element = Array.from(doc.body.querySelectorAll('p')).find(p => p.innerHTML.includes(section));
if (element) {
let content = element.innerHTML.replace(section, "").trim();
let sibling = element.nextElementSibling;
while (sibling && !contentSections.some(sec => sibling.innerHTML.includes(sec))) {
content += " " + sibling.innerHTML.trim();
sibling = sibling.nextElementSibling;
}
content = content.replace("<strong>", "").replace("</strong>", "");
result.push(content);
} else {
result.push("");
}
});
return result;
},
selectOption(option) {
this.showChatWindow = true;
this.chatWindowInfo = option;
this.currentOption = option.id;
},
reGenerate() {
if (!this.showChatWindow) return;
const option = this.options.find(opt => opt.id === this.currentOption);
if (option) {
let data = { "msg": option.knowledge.toString() };
chatgpt_generate(data).then((res) => {
let converter = new Showdown.Converter();
let htmlStr = res.msg;
let convertedHtml = converter.makeHtml(htmlStr);
let threePart = this.extractContent(convertedHtml);
option.content = threePart[0];
option.analysis = threePart[1];
option.answer = threePart[2];
let question = JSON.parse(localStorage.getItem("question"));
const index = question.findIndex(q => q.id === option.id);
if (index !== -1) {
question[index] = option;
localStorage.setItem("question", JSON.stringify(question));
}
this.chatWindowInfo = option;
});
}
},
collect() {
if (!this.showChatWindow) return;
let collection = JSON.parse(localStorage.getItem("collection"));
const option = this.options.find(opt => opt.id === this.currentOption);
if (option.isCollected) {
collection = collection.filter(item => item.id !== option.id);
} else {
collection.push(option);
}
option.isCollected = !option.isCollected;
localStorage.setItem("collection", JSON.stringify(collection));
},
},
computed: {
isOptionCollected() {
if (!this.showChatWindow) return false;
const option = this.options.find(opt => opt.id === this.currentOption);
return option ? option.isCollected : false;
},
collectButtonText() {
return this.isOptionCollected ? '已收藏' : '收藏';
},
},
};
</script>
3. 样式
<style scoped>
.chatHome {
display: flex;
.chatLeft {
width: 190px;
.title {
color: #fff;
padding-left: 10px;
}
.options {
margin-top: 45px;
input {
border-width: 0;
border-radius: 10px;
padding: 10px;
background-color: #fff;
color: rgb(176, 178, 189);
cursor: pointer;
&:hover {
background-color: rgb(176, 178, 189);
}
}
.addOption {
margin-top: 20px;
border-width: 0;
border-radius: 10px;
background-color: rgb(50, 54, 68);
color: #fff;
width: 185px;
height: 30px;
cursor: pointer;
&:hover {
background-color: #1d90f5;
}
}
.options-wrapper {
padding-left: 10px;
height: 40vh;
margin-top: 20px;
overflow: hidden;
border-radius: 10px;
background-color: rgb(50, 54, 68);
overflow-y: scroll;
.optionList {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
border-radius: 10px;
background-color: rgb(79, 84, 103);
margin: 10px 10px 0 0;
cursor: pointer;
color: white;
position: relative;
&:hover {
background-color: #1d90f5;
}
}
.optionList.selected {
background-color: #1d90f5;
}
.delete-button {
position: absolute;
top: 10px;
right: 10px;
display: none;
background-color: #932828;
color: white;
border: none;
border-radius: 5px;
padding: 5px;
cursor: pointer;
}
.optionList:hover .delete-button {
display: block;
}
&::-webkit-scrollbar {
width: 0;
height: 0;
display: none;
}
}
.button-group {
display: flex;
justify-content: space-between;
margin-top: 10px;
.regenerate-button, .collect-button {
width: 85px;
height: 30px;
border: none;
border-radius: 10px;
background-color: rgb(50, 54, 68);
color: #fff;
cursor: pointer;
&:hover {
background-color: #1d90f5;
}
}
.collected {
background-color: #28a745;
&hover {
background-color: #218838;
}
}
}
}
}
.chatRight {
flex: 1;
padding-right: 30px;
.showIcon {
position: absolute;
top: calc(50% - 150px);
left: calc(50% - 50px);
.icon-snapchat {
width: 300px;
height: 300px;
font-size: 300px;
}
}
}
}
</style>
详细解释
模板部分
-
左侧选项管理部分(chatLeft)
- 包含一个标题“Question Generation”。
- 一个输入框用于添加新选项。
- 一个“提交”按钮,点击后调用
addOption
方法。 - 选项列表通过
v-for
指令遍历options
数组生成,每个选项可以显示其内容并有一个删除按钮。 - “重新生成”和“收藏”按钮用于重新生成问题和收藏问题。
-
右侧聊天窗口部分(chatRight)
- 根据
showChatWindow
变量的值,显示聊天窗口或一个默认图标。
- 根据
脚本部分
-
导入组件和工具
OptionCard
和ChatWindow
组件。generateUUID
用于生成唯一ID。chatgpt_generate
用于调用API生成问题内容。Showdown
用于将Markdown转换为HTML。
-
数据定义
newOption
:存储用户输入的新选项。options
:选项列表。currentOption
:当前选中的选项ID。showChatWindow
:控制聊天窗口的显示。chatWindowInfo
:当前聊天窗口显示的信息。isSend
:控制发送状态。
-
生命周期钩子
mounted
:组件加载时,从localStorage
获取问题列表和收藏状态,并初始化选项列表。
-
方法
addOption
:添加新选项,并通过chatgpt_generate
获取生成内容,解析并存储在本地。showDeleteButton
和hideDeleteButton
:控制删除按钮的显示和隐藏。deleteOption
:删除选项,并更新localStorage
。extractContent
:解析生成的HTML内容,提取题目、解析和答案。selectOption
:选择选项并显示在聊天窗口。reGenerate
:重新生成当前选项的内容,并更新localStorage
。collect
:收藏或取消收藏当前选项,并更新localStorage
。
-
计算属性
isOptionCollected
:判断当前选项是否被收藏。collectButtonText
:根据收藏状态显示按钮文本。
样式部分
-
总体布局
.chatHome
使用flex
布局,分为左侧和右侧。
-
左侧样式
.chatLeft
:设置宽度和内边距。.options
:包括输入框、提交按钮、选项列表及按钮组的样式。.options-wrapper
:设置选项列表的滚动条样式和选项卡样式。
-
右侧样式
.chatRight
:设置填充和默认图标的样式。
总结
用户可以添加、删除、重新生成和收藏问题,数据通过localStorage
进行持久化。