一、项目搭建与基础依赖安装
技术栈:Vite、Vue2、Typescript、Element-plus、Scss.
参考字节Vite实战课程juejin.cn/course/byte…
1、创建Vite项目
首先安装pnpm,这里注意把npm国外下载源换成国内的不然可能下载不了,pnpm的下载源也可以换成国内的。
npm i -g pnpm
使用pnpm创建vite项目,这里输入命令会让你输入项目名称以及使用的框架等,选择适合自己的就行。
pnpm create vite
pnpm install
pnpm run dev
2、项目结构
-
src目录:存放项目的源代码。assets:用于存放图片、字体等静态资源。components:存放 Vue 组件。utils: 存放工具类、工具组件如防抖功能等。views: 单独的页面视图。App.vue:根组件。main.ts:项目的入口文件,用于创建 Vue 应用实例并挂载到 DOM 上。
-
public目录:存放一些公共的静态资源,这些资源会直接被复制到构建目录中,不会经过处理。 -
index.html:项目的 HTML 模板文件。 -
tsconfig.json:TypeScript 的配置文件,用于配置 TypeScript 编译器的相关选项。
二、组件布局
ChatView
这一部分是应对项目要求的独立对话框模式视图。 主要的模板代码:
<template>
<div class="common-layout">
<el-container>
<el-aside class="chat-sidebar" width="200px"></el-aside>
<el-container>
<el-header>
<el-avatar :src="aiAvatar" :size="40" />
<el-text type="primary" :size="large">Xcalibur AI</el-text>
<el-icon :size="30" style="margin: 0 10px 0 10px;"><Operation /></el-icon>
<el-icon :size="30">
<Close class="el-icon-close" @click="close"/>
</el-icon>
</el-header>
<el-main style="padding: 0;">
<ChatWindow class="chat-window" />
</el-main>
</el-container>
</el-container>
</div>
</template>
主要应用了side-bar、header、main布局方式,使用了 el-container、el-aside、el-header、el-main 等 Element Plus的组件来搭建页面布局,构建出类似侧边栏、头部、主体内容区域的布局效果。
效果展示
ChatInput
集成用户消息输入、文件、图片上传功能的子组件
在实现的时候,我加入了很多优化细节。
- 按键防抖的功能,核心是用到了setTimeout函数,用于控制shift+enter换行的触发防抖。 这里我写了一个Debouncer类,以提供防抖函数的可扩展性和复用性。
class Debouncer {
timer: null | ReturnType<typeof setTimeout>;
constructor() {
this.timer = null; // 定义 timer
}
debounce(func: (...args: any[]) => void, delay: number , immediate = false) {
return (...args: any[]) => {
// 清除之前的 timer
if (this.timer) {
clearTimeout(this.timer);
}
if (immediate && !this.timer) {
func.apply(this, args);
}
// 设置新的 timer
this.timer = setTimeout(() => {
func.apply(this, args); // 执行传入的函数
}, delay);
};
}
}
export default new Debouncer();
将它绑定到我的按键处理函数上,即实现了按键防抖。
- 在使用豆包的时候,我注意到它的输入框可以随着内容溢出自动向上生长扩张,因此我在代码中,将chat-input组件设置为了flex:1样式并把chat-window设置为了display:flex弹性盒子样式,让chat-input组件视图自动向上生长。
.chat-input {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
flex: 1;
margin-bottom: 10px;
max-height: 22%;
width: 40%;
border: 1px solid;
border-radius: 5px;
}
- 空白内容自动清除的优化:我在使用一些类似的LLM聊天输入框时,发现如果我输入了多行空行,就无法使用enter发送,需要自己手动清除空行,于是我在代码中加入了消息检测,以使得用户输入无效内容就自动清空内容,并将输入框返回初始高度
const processedValue = this.message.replace(/\s/g, '');
if (processedValue === '') {
this.placeholderSign = '请输入有效内容';
this.message = '';
this.setTextAreaHeight();
return;
}
setTextAreaHeight() {
const textarea = this.$refs.textArea;
textarea.style.height = 36 + "px"; // 设置为初始高度
textarea.style.height = textarea.scrollHeight + "px";
},