山东大学软件学院项目实训-基于大模型的模拟面试系统-个人博客(四)

本次针对上一周留下的问题进行解决

文件上传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>

对于上传文件的功能,参考知识库中的文件上传代码。

  1. 文件管理:显示已上传文件列表(带删除功能)和文件上传按钮
  2. 输入控制:提供多行文本输入框,支持回车键发送消息,带发送按钮(发送状态禁用)
  3. 辅助功能:包含语音输入(待开发)和文件上传快捷按钮

特点说明:
• 文件上传区域仅在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
  • 对论坛加入检索或者标签功能
  • 辅助小组成员开发其他任务
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值