本次针对上一周留下的问题进行解决
文件上传bug
之前的文件上传有一个bug,当用户上传完一次后,第二次上传会出现第一次上传中的文件。
排查了原因发现,这是由于处理上传文件中,自带的组件没有清空,导致出现bug。
这是修改后的代码
<el-dialog title="上传文件" :visible.sync="uploadDialogVisible" width="600px" @close="clearUploadFiles"
class="upload-dialog">
<el-upload class="upload-area" drag action="#" ref="fileUpload" multiple :auto-upload="false"
:on-change="handleFileChange" :show-file-list="false">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">
支持上传pdf/docx/txt等格式文件,单个文件不超过50MB
</div>
</el-upload>
<div class="selected-files" v-if="selectedFiles.length > 0">
<h4>已选择文件:</h4>
<ul>
<li v-for="(file, index) in selectedFiles" :key="index">
{{ file.name }} ({{ formatFileSize(file.size) }})
<el-button type="text" icon="el-icon-close" @click="removeSelectedFile(index)"
class="file-remove-btn"></el-button>
</li>
</ul>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="uploadDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="uploadFiles">确 定</el-button>
</span>
</el-dialog>
文件上传对话框组件,包含拖拽上传区域、已选文件列表显示和取消/确认按钮,用于让用户选择并上传文件。
顺手改了一下界面,更加简洁了
滚动条问题
小组成员对这一问题进行了修改调整
修改聊条界面
问题
- 聊天界面太老,没有现代化的感觉
- 滚动条的全局设置
AI聊天界面
由于全局布局的原因,我对整个框架进行了包裹。所以在开发ai聊天这个界面时,会与现在主流的界面有所不同,这也带给了我极大的难题。
气泡问题
首先要解决的是气泡问题,过长的气泡的间距会让用户不适应ai的回答,这里我对气泡进行了些许调整(有点像QQ聊天的感觉),并且加长了padding使他更加紧凑,后续可能还会进行修改。
<div v-for="(message, index) in messageListForShow" :key="index"
:class="['message', message.role === 'user' ? 'user-message' : 'ai-message']"
@mouseenter="message.showActions = true" @mouseleave="message.showActions = false">
<div class="message-time">{{ formatTime(message.timestamp) }}</div>
<div v-if="message.content.files.length > 0" class="processed-files">
<el-card v-for="(file, index) in message.content.files" :key="index" class="file-card" shadow="hover">
<div class="file-card-content">
<i class="el-icon-document" style="margin-right: 8px;"></i>
<span :title="file.fileName">{{ truncateFilename(file.fileName) }}</span>
</div>
</el-card>
</div>
<div class="message-content-wrapper">
<div class="message-content">
<div v-if="!message.editing" class="message-text"
v-html="renderMarkdown(message.content.text, message.role)">
</div>
<el-input v-else type="textarea" :rows="3" v-model="message.editText" class="edit-input"></el-input>
</div>
<div v-if="message.role === 'user'" class="message-actions" v-show="message.showActions || message.editing">
<el-button v-if="!message.editing" type="text" icon="el-icon-edit"
@click="startEditMessage(message)"></el-button>
<el-button v-if="!message.editing" type="text" icon="el-icon-refresh"
@click="regenerateMessage(message)"></el-button>
<div v-else class="edit-actions">
<el-button size="mini" type="primary" @click="sendModifiedMessage(message)">发送</el-button>
<el-button size="mini" @click="cancelEdit(message)">取消</el-button>
</div>
</div>
</div>
<!-- 添加分支切换按钮,放置在容器右侧 -->
<div class="branch-switch-container">
<div v-if="isFirstMessageInBranch(message) && checkSiblings(message) && !isLoading" class="branch-switch"
:class="{ 'user-branch': message.role === 'user' }">
<el-button type="text" @click="toggleShowBranch(message)">
<i :class="message.showBranchTag ? 'el-icon-caret-top' : 'el-icon-caret-bottom'"></i>
{{ message.showBranchTag ? '收起分支' : '显示分支' }}
</el-button>
</div>
</div>
<!-- 分支编辑面板 -->
<div v-if="message.showBranchTag && !isLoading" class="branch-edit-panel"
:class="{ 'user-branch-panel': message.role === 'user' }">
<div class="branch-tag-list">
<div v-for="(sibling, idx) in siblingNodes.find(node => node.branchId === message.branchId)?.siblings || []"
:key="sibling.index" class="branch-tag-item"
:class="{ 'active-branch': sibling.index === currentBranchIndex }">
<!-- 显示模式 -->
<template v-if="!sibling.editing">
<span class="branch-tag-text" @click="switchBranch(sibling.index)">{{ sibling.tag }}</span>
<el-button type="text" size="mini" @click="startEditBranchTag(sibling)" class="edit-branch-btn">
<i class="el-icon-edit"></i>
</el-button>
</template>
<!-- 编辑模式 -->
<template v-else>
<el-input v-model="sibling.tag" size="mini" class="branch-tag-input"
@keyup.enter.native="saveBranchTag(sibling, message)"></el-input>
<div class="branch-tag-actions">
<el-button type="text" size="mini" @click="cancelEditBranchTag(sibling)">取消</el-button>
<el-button type="text" size="mini" @click="saveBranchTag(sibling, message)">保存</el-button>
</div>
</template>
</div>
</div>
</div>
</div>
消息展示:区分用户和AI消息,显示消息时间、文本内容(支持Markdown渲染)和附件文件
交互功能:提供消息编辑、重新生成、分支切换等操作按钮(鼠标悬停显示)
分支管理:支持创建和切换对话分支,可以编辑分支标签名称
核心特点是实现了可编辑的消息内容和可管理的对话分支系统,适合需要多轮复杂对话的场景。
聊天框问题
其次是聊天框的问题,对于原有的聊天框,我们发现他的图标位置不美观,同时太小了不易让用户发现,于是我吸收了其他ai界面的方案,选择在聊天区域的左上角进行放置,同时写明作用,方便用户使用。
对于发送按钮和输入框也做了一定程度的修改,使得布局更加完善
<div class="chat-input">
<!-- 已处理文件显示区域 -->
<div v-if="processedFiles.length > 0 && uploadOnWhichMessage === -1" class="processed-files">
<el-card v-for="(file, index) in processedFiles" :key="index" class="file-card" shadow="hover">
<div class="file-card-content">
<i class="el-icon-document" style="margin-right: 8px;"></i>
<span :title="file.name">{{ truncateFilename(file.name) }}</span>
<el-button type="text" icon="el-icon-close" @click="removeProcessedFile(index)"
class="file-remove-btn"></el-button>
</div>
</el-card>
</div>
<div class="input-tools">
<el-button type="text" icon="el-icon-upload" @click="showUploadDialog" title="上传文件">
<span class="button-text">上传文件</span>
</el-button>
<el-button type="text" icon="el-icon-microphone" @click="startVoiceInput" title="语音输入">
<span class="button-text">语音输入</span>
</el-button>
</div>
<div class="input-container">
<el-input type="textarea" :rows="3" placeholder="请输入您的问题..." v-model="inputMessage"
@keyup.enter.native="handleKeyEnter" class="message-input"></el-input>
<el-button type="primary" @click="sendMessageWithPolling()" :disabled="!inputMessage.trim() || isLoading"
class="send-button">
<i class="el-icon-s-promotion"></i>
<span v-if="!isLoading">发送</span>
<span v-else>发送中...</span>
</el-button>
</div>
</div>
对于上传文件的功能,参考知识库中的文件上传代码。
- 文件管理:显示已上传文件列表(带删除功能)和文件上传按钮
- 输入控制:提供多行文本输入框,支持回车键发送消息,带发送按钮(发送状态禁用)
- 辅助功能:包含语音输入(待开发)和文件上传快捷按钮
特点说明:
• 文件上传区域仅在processedFiles
有内容且uploadOnWhichMessage
为-1时显示
• 发送按钮在输入框为空或加载状态时禁用
• 同时支持点击发送和回车键发送两种触发方式
成品如下
编辑和重新生成按钮、显示分支功能
先看成品
<div class="branch-switch-container">
<div v-if="isFirstMessageInBranch(message) && checkSiblings(message) && !isLoading" class="branch-switch"
:class="{ 'user-branch': message.role === 'user' }">
<el-button type="text" @click="toggleShowBranch(message)">
<i :class="message.showBranchTag ? 'el-icon-caret-top' : 'el-icon-caret-bottom'"></i>
{{ message.showBranchTag ? '收起分支' : '显示分支' }}
</el-button>
</div>
</div>
<!-- 分支编辑面板 -->
<div v-if="message.showBranchTag && !isLoading" class="branch-edit-panel"
:class="{ 'user-branch-panel': message.role === 'user' }">
<div class="branch-tag-list">
<div v-for="(sibling, idx) in siblingNodes.find(node => node.branchId === message.branchId)?.siblings || []"
:key="sibling.index" class="branch-tag-item"
:class="{ 'active-branch': sibling.index === currentBranchIndex }">
<!-- 显示模式 -->
<template v-if="!sibling.editing">
<span class="branch-tag-text" @click="switchBranch(sibling.index)">{{ sibling.tag }}</span>
<el-button type="text" size="mini" @click="startEditBranchTag(sibling)" class="edit-branch-btn">
<i class="el-icon-edit"></i>
</el-button>
</template>
<!-- 编辑模式 -->
<template v-else>
<el-input v-model="sibling.tag" size="mini" class="branch-tag-input"
@keyup.enter.native="saveBranchTag(sibling, message)"></el-input>
<div class="branch-tag-actions">
<el-button type="text" size="mini" @click="cancelEditBranchTag(sibling)">取消</el-button>
<el-button type="text" size="mini" @click="saveBranchTag(sibling, message)">保存</el-button>
</div>
</template>
</div>
</div>
</div>
编辑按钮:位于用户消息右上角,鼠标悬停时显示,点击后切换为文本编辑框和"发送/取消"按钮组,采用简约的图标按钮样式(el-icon-edit)。、
重新生成按钮:与编辑按钮并列显示,图标为el-icon-refresh,点击后自动进入编辑状态并保留原消息内容,方便用户重新发送修改后的消息。
分支显示界面:分支切换按钮位于消息右下角,显示"展开/收起分支"文字和上下箭头图标(el-icon-caret-top/bottom),展开后显示横向排列的分支标签卡片,包含分支名称和编辑按钮,当前活跃分支有蓝色高亮背景(active-branch类),编辑状态时变为输入框和"保存/取消"按钮组,保持圆角卡片样式
总的来说
这次的界面设计仍然不够美观,后续可能会接着进行开发,或者去修一些潜在bug。
其次是,通过修改这些代码,我更加了解了element-ui的组件的运行逻辑。同时也明确了css的布局设计。通过不断的写方法逻辑,对前后端接口更加得心应手。
下周工作
- 编辑和重新生成按钮的悬停bug
- 对论坛加入检索或者标签功能
- 辅助小组成员开发其他任务