手把手拆解:使用vue3打造超酷AI对话页面_vue ai
手把手拆解:使用vue3打造超酷AI对话页面
一、HTML部分:搭建页面框架
咱们先从HTML部分开始看起,这就好比是搭房子,HTML搭建的就是整个页面的框架。
(一)整体布局容器
<template> <div class=\"container\"> <div class=\"bailian-demo\">
这里最外层的标签,它是Vue.js特有的,用来包裹页面的模板内容。然后是
,这个
container
类就像是一个大箱子,把整个页面的内容都装在里面,并且通过CSS设置了一些通用的样式,比如说最大宽度、外边距、内边距啥的,让页面内容在不同屏幕大小下都能有个合适的布局。再里面的
,它是整个AI对话页面主体的容器,设定了一些样式来确定它的整体外观,比如背景颜色、边框圆角、阴影等等,让页面看起来更美观、更有质感。
(二)左侧历史记录面板
<div class=\"history-panel\" :class=\"{ \'history-collapsed\': isHistoryCollapsed }\"> <div class=\"history-header\"> <h3>对话历史</h3> <button class=\"toggle-history\" @click=\"toggleHistory\"> <i class=\"icon-collapse\"></i> {{ isHistoryCollapsed? \'展开\' : \'收起\' }} </button> </div> <div class=\"history-list\"> <div v-for=\"(item, index) in history\" :key=\"index\" class=\"history-item\" @click=\"loadHistoryItem(index)\" :class=\"{ \'active-history\': activeHistoryIndex === index }\" > <div class=\"history-question\">{{ truncateText(item.question) }}</div> <div class=\"history-time\">{{ formatTime(item.time) }}</div> </div> <div v-if=\"history.length === 0\" class=\"empty-history\"> <i class=\"icon-history\"></i> <p>暂无历史记录</p> </div> </div></div>
这一大块就是左侧的历史记录面板啦。
,这里的history - panel
类定义了这个面板的基本样式,像宽度、背景渐变、边框等等。而后面的:class
绑定是Vue.js的语法,它会根据isHistoryCollapsed
这个变量的值来动态添加或移除history - collapsed
类。啥意思呢?就是说如果isHistoryCollapsed
为true
,就会添加history - collapsed
类,这样面板就能实现收起的效果啦,反之则是展开状态。
再看里面的
,这是历史记录面板的头部,包含了一个标题
对话历史
,让用户一眼就知道这是干啥的。还有一个按钮
,这个按钮可重要啦,@click=\"toggleHistory\"
表示当用户点击这个按钮时,会触发Vue.js组件里定义的toggleHistory
函数,这个函数就是用来切换历史记录面板展开或收起状态的。按钮里面有个小图标
,还有一段文字{{ isHistoryCollapsed? \'展开\' : \'收起\' }}
,这段文字也是根据isHistoryCollapsed
的值动态显示的,很智能吧!
接下来是
,这里面放的就是具体的历史记录列表啦。通过v - for
指令,也就是
,它会循环遍历history
数组,这个数组里面存的就是所有的历史对话记录。每一条记录都会生成一个
,这里的history - item
类定义了每个历史记录项的样式,像背景颜色、边框半径、鼠标悬停效果等等。@click=\"loadHistoryItem(index)\"
表示当用户点击某条历史记录时,会触发loadHistoryItem
函数,并且把这条记录的索引index
传进去,这样就能加载这条历史记录的内容到输入框和结果展示区域啦。:class=\"{ \'active - history\': activeHistoryIndex === index }\"
这个绑定呢,会根据activeHistoryIndex
这个变量的值来判断当前这条记录是否是激活状态,如果是,就会添加active - history
类,给这条记录加上特殊的样式,比如改变背景颜色啥的,让用户知道当前选中的是哪条记录。
每个历史记录项里面又有两个小的
,
{{ truncateText(item.question) }}
用来显示问题内容,这里的truncateText
函数会把问题内容截断显示,防止太长了影响页面布局。
{{ formatTime(item.time) }}
则是用来显示这条记录的时间,formatTime
函数会把时间戳格式化成我们常见的日期时间格式。
最后还有一个
,当history
数组长度为0,也就是没有历史记录的时候,就会显示这个
,里面有个小图标和一段文字提示用户“暂无历史记录”。
(三)历史记录展开按钮(当收起时显示)
<button class=\"expand - history - btn\" @click=\"toggleHistory\" v - if=\"isHistoryCollapsed\"> <i class=\"icon - expand\"></i></button>
这个按钮很简单,当历史记录面板处于收起状态,也就是isHistoryCollapsed
为true
的时候,它就会显示出来。同样,点击这个按钮会触发toggleHistory
函数,用来展开历史记录面板。按钮里面有个
小图标,告诉用户点击这里可以展开历史记录。
(四)主内容区域
<div class=\"main - content\" :class=\"{ \'content - expanded\': isHistoryCollapsed }\"> <div class=\"header\"> <h1><span style=\"cursor: pointer;color: #641acf\" @click=\"router.push(\'/\')\">月木</span>AI助手</h1> <p class=\"subtitle\">智能AI对话体验</p> </div> <div class=\"content - area\"> <div class=\"result - section\" v - if=\"result || isLoading\"> <div class=\"result - header\"> <h3>AI回答</h3> <button class=\"copy - button\" @click=\"copyResult\" :disabled=\"!result\"> <i class=\"icon - copy\"></i> 复制 </button> </div> <div class=\"markdown - content\" ref=\"resultContainer\" v - html=\"renderedResult\"></div> </div> <div class=\"error - message\" v - if=\"error\"> <i class=\"icon - error\"></i> {{ error }} </div> <transition name=\"fade\"> <div class=\"copy - notification\" v - if=\"showCopyNotification\"> <i class=\"icon - check\"></i> 复制成功 </div> </transition> </div> <div class=\"input - container\"> <div class=\"input - section\"> <textarea v - model=\"prompt\" placeholder=\"输入你的问题...\" @keydown.enter.exact.prevent=\"generateText\" @keydown.shift.enter=\"handleShiftEnter\" ></textarea> <div class=\"button - container\"> <div class=\"hint\">按Enter发送,Shift+Enter换行</div> <button @click=\"generateText\" :disabled=\"isLoading\"> <span v - if=\"isLoading\"> <span class=\"spinner\"></span> 生成中... </span> <span v - else>发送 <i class=\"icon - send\"></i></span> </button> </div> </div> </div></div>
这一大块就是主内容区域啦。
,main - content
类定义了主内容区域的基本样式,:class
绑定也是根据isHistoryCollapsed
的值来动态添加或移除content - expanded
类,当历史记录面板收起时,主内容区域会有一些样式上的变化,比如可能会占据更多的屏幕宽度啥的。
再看里面的
,这是主内容区域的头部,有一个大标题
月木AI助手
,这个标题里面的“月木”两个字是有链接效果的,当用户点击时,会触发router.push(\'/\')
,这是Vue Router的语法,会把用户导航到根路径。旁边还有一个副标题
智能AI对话体验
,简单介绍了这个AI助手的功能。
接下来是
,这里面放的就是主要的内容展示区域啦。
,这个result - section
会在result
有值(也就是AI有回答了)或者isLoading
为true
(也就是正在加载AI回答)的时候显示出来。
是结果区域的头部,有一个标题
AI回答
,还有一个复制按钮
,@click=\"copyResult\"
表示点击这个按钮会触发copyResult
函数,用来复制AI的回答内容。:disabled=\"!result\"
表示当result
没有值的时候,也就是AI还没有回答时,这个按钮是禁用状态,用户不能点击。按钮里面有个小图标
和文字“复制”。
再下面的
,这里的markdown - content
类定义了显示结果的样式,ref=\"resultContainer\"
给这个
起了个引用名,方便在JavaScript代码里获取这个元素。v - html=\"renderedResult\"
则是把renderedResult
这个变量的值以HTML的形式渲染到这个
里面,renderedResult
是通过计算属性把Markdown格式的结果转换为HTML格式的,后面我们在JavaScript部分会详细讲到。
然后是