WeKnora产品文档系统:基于Vue3的前端界面开发指南
WeKnora产品文档系统:基于Vue3的前端界面开发指南
1. 开发环境准备
在开始WeKnora前端开发之前,我们需要先搭建好开发环境。Vue3作为当前最流行的前端框架之一,提供了更好的性能和开发体验。
首先确保你的系统已经安装Node.js(建议版本16以上),然后通过以下命令创建Vue项目:
# 使用Vite创建Vue3项目 npm create vite@latest weknora-frontend -- --template vue cd weknora-frontend # 安装依赖 npm install # 安装必要的UI库和工具 npm install element-plus @element-plus/icons-vue axios pinia vue-router项目结构通常如下:
weknora-frontend/ ├── public/ ├── src/ │ ├── components/ # 可复用组件 │ ├── views/ # 页面组件 │ ├── store/ # 状态管理 │ ├── router/ # 路由配置 │ ├── api/ # API接口 │ └── assets/ # 静态资源 ├── package.json └── vite.config.js2. 项目架构设计
WeKnora前端采用模块化架构设计,确保代码的可维护性和可扩展性。整个系统分为以下几个核心模块:
- 用户认证模块:处理登录、注册、权限验证
- 知识库管理模块:文档上传、分类、检索界面
- 对话交互模块:问答界面和聊天功能
- 系统设置模块:模型配置、系统参数设置
使用Pinia进行状态管理,可以很好地处理复杂的应用状态:
// store/auth.js import { defineStore } from 'pinia' export const useAuthStore = defineStore('auth', { state: () => ({ user: null, token: localStorage.getItem('token'), isAuthenticated: false }), actions: { async login(credentials) { const response = await api.post('/auth/login', credentials) this.token = response.data.token this.user = response.data.user this.isAuthenticated = true localStorage.setItem('token', this.token) }, logout() { this.token = null this.user = null this.isAuthenticated = false localStorage.removeItem('token') } } })3. 核心组件开发
3.1 导航栏组件
导航栏是用户界面的重要组成部分,需要提供清晰的导航和用户操作入口:
<template> <el-menu mode="horizontal" :default-active="$route.path"> <el-menu-item index="/">首页</el-menu-item> <el-menu-item index="/knowledge">知识库</el-menu-item> <el-menu-item index="/chat">智能问答</el-menu-item> <div class="user-section"> <el-dropdown v-if="auth.isAuthenticated"> <span class="el-dropdown-link"> <el-avatar :size="32" :src="auth.user?.avatar" /> {{ auth.user?.username }} </span> <template #dropdown> <el-dropdown-menu> <el-dropdown-item>个人设置</el-dropdown-item> <el-dropdown-item @click="handleLogout">退出登录</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> <div v-else> <el-button link @click="$router.push('/login')">登录</el-button> <el-button type="primary" @click="$router.push('/register')">注册</el-button> </div> </div> </el-menu> </template> <script setup> import { useAuthStore } from '@/store/auth' import { useRouter } from 'vue-router' const auth = useAuthStore() const router = useRouter() const handleLogout = () => { auth.logout() router.push('/login') } </script>3.2 文档上传组件
文档上传是知识库系统的核心功能,需要支持多种文件格式和批量上传:
<template> <el-upload class="upload-demo" drag multiple :action="uploadUrl" :headers="headers" :on-success="handleSuccess" :before-upload="beforeUpload" > <el-icon class="el-icon--upload"><upload-filled /></el-icon> <div class="el-upload__text"> 拖拽文件到此处或<em>点击上传</em> </div> <template #tip> <div class="el-upload__tip"> 支持 PDF、Word、TXT、Markdown 格式文件,单个文件不超过10MB </div> </template> </el-upload> </template> <script setup> import { UploadFilled } from '@element-plus/icons-vue' import { useAuthStore } from '@/store/auth' import { ElMessage } from 'element-plus' const auth = useAuthStore() const uploadUrl = `${import.meta.env.VITE_API_BASE_URL}/api/documents/upload` const headers = { Authorization: `Bearer ${auth.token}` } const beforeUpload = (file) => { const allowedTypes = [ 'application/pdf', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'text/plain', 'text/markdown' ] const isAllowed = allowedTypes.includes(file.type) const isLt10M = file.size / 1024 / 1024 < 10 if (!isAllowed) { ElMessage.error('不支持的文件格式!') return false } if (!isLt10M) { ElMessage.error('文件大小不能超过10MB!') return false } return true } const handleSuccess = (response) => { ElMessage.success('上传成功!') emit('upload-success', response.data) } </script>4. 页面路由配置
使用Vue Router管理页面路由,实现单页面应用的导航:
// router/index.js import { createRouter, createWebHistory } from 'vue-router' import { useAuthStore } from '@/store/auth' const routes = [ { path: '/', name: 'Home', component: () => import('@/views/HomeView.vue'), meta: { requiresAuth: true } }, { path: '/login', name: 'Login', component: () => import('@/views/LoginView.vue'), meta: { guestOnly: true } }, { path: '/knowledge', name: 'Knowledge', component: () => import('@/views/KnowledgeView.vue'), meta: { requiresAuth: true } }, { path: '/knowledge/:id', name: 'KnowledgeDetail', component: () => import('@/views/KnowledgeDetailView.vue'), meta: { requiresAuth: true } }, { path: '/chat', name: 'Chat', component: () => import('@/views/ChatView.vue'), meta: { requiresAuth: true } } ] const router = createRouter({ history: createWebHistory(), routes }) // 路由守卫 router.beforeEach((to, from, next) => { const auth = useAuthStore() if (to.meta.requiresAuth && !auth.isAuthenticated) { next('/login') } else if (to.meta.guestOnly && auth.isAuthenticated) { next('/') } else { next() } }) export default router5. API接口调用
封装统一的API调用模块,便于管理和维护接口请求:
// api/index.js import axios from 'axios' import { useAuthStore } from '@/store/auth' import { ElMessage } from 'element-plus' // 创建axios实例 const api = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 10000 }) // 请求拦截器 api.interceptors.request.use( (config) => { const auth = useAuthStore() if (auth.token) { config.headers.Authorization = `Bearer ${auth.token}` } return config }, (error) => { return Promise.reject(error) } ) // 响应拦截器 api.interceptors.response.use( (response) => { return response.data }, (error) => { if (error.response?.status === 401) { const auth = useAuthStore() auth.logout() ElMessage.error('登录已过期,请重新登录') window.location.href = '/login' } else if (error.response?.data?.message) { ElMessage.error(error.response.data.message) } else { ElMessage.error('网络错误,请稍后重试') } return Promise.reject(error) } ) // 知识库相关API export const knowledgeApi = { // 获取知识库列表 getList: (params) => api.get('/knowledge', { params }), // 创建知识库 create: (data) => api.post('/knowledge', data), // 更新知识库 update: (id, data) => api.put(`/knowledge/${id}`, data), // 删除知识库 delete: (id) => api.delete(`/knowledge/${id}`), // 上传文档 uploadDocument: (formData) => api.post('/documents/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' } }) } // 对话相关API export const chatApi = { // 发送消息 sendMessage: (data) => api.post('/chat/message', data), // 获取对话历史 getHistory: (knowledgeId) => api.get(`/chat/history/${knowledgeId}`), // 清空对话历史 clearHistory: (knowledgeId) => api.delete(`/chat/history/${knowledgeId}`) } export default api6. 响应式布局优化
为了确保WeKnora在不同设备上都有良好的用户体验,我们需要实现响应式布局:
<template> <div class="layout-container"> <header class="header"> <NavigationBar /> </header> <main class="main-content"> <aside class="sidebar" v-if="!isMobile || sidebarVisible"> <KnowledgeSidebar /> </aside> <section class="content-area"> <router-view /> </section> </main> </div> </template> <script setup> import { ref, onMounted, onUnmounted } from 'vue' import NavigationBar from '@/components/NavigationBar.vue' import KnowledgeSidebar from '@/components/KnowledgeSidebar.vue' const isMobile = ref(false) const sidebarVisible = ref(false) const checkScreenSize = () => { isMobile.value = window.innerWidth < 768 sidebarVisible.value = !isMobile.value } onMounted(() => { checkScreenSize() window.addEventListener('resize', checkScreenSize) }) onUnmounted(() => { window.removeEventListener('resize', checkScreenSize) }) </script> <style scoped> .layout-container { display: flex; flex-direction: column; height: 100vh; } .header { flex-shrink: 0; } .main-content { display: flex; flex: 1; overflow: hidden; } .sidebar { width: 300px; border-right: 1px solid #e4e7ed; overflow-y: auto; } .content-area { flex: 1; overflow-y: auto; padding: 20px; } @media (max-width: 768px) { .sidebar { position: fixed; left: -300px; top: 0; height: 100%; z-index: 1000; background: white; transition: left 0.3s ease; } .sidebar.active { left: 0; } .content-area { padding: 10px; } } </style>7. 性能优化策略
为了提高前端应用的性能,我们可以采取以下优化措施:
7.1 组件懒加载
使用Vue的defineAsyncComponent实现组件懒加载:
// 在路由配置中使用懒加载 const routes = [ { path: '/knowledge', component: defineAsyncComponent(() => import('@/views/KnowledgeView.vue') ) } ] // 或者在组件中懒加载子组件 const LazyComponent = defineAsyncComponent(() => import('@/components/LazyComponent.vue') )7.2 列表虚拟滚动
对于长列表数据,使用虚拟滚动提升性能:
<template> <el-table-v2 :columns="columns" :data="knowledgeList" :width="800" :height="400" :row-height="60" fixed /> </template> <script setup> import { ElTableV2 } from 'element-plus' const columns = [ { key: 'name', dataKey: 'name', title: '知识库名称', width: 200 }, { key: 'documentCount', dataKey: 'documentCount', title: '文档数量', width: 120 }, { key: 'createdAt', dataKey: 'createdAt', title: '创建时间', width: 150 } ] const knowledgeList = ref([]) // 获取知识库列表数据 const fetchKnowledgeList = async () => { const response = await knowledgeApi.getList() knowledgeList.value = response.data } </script>8. 错误处理和用户体验
良好的错误处理和用户反馈是提升用户体验的关键:
<template> <div class="chat-container"> <div class="messages"> <div v-for="message in messages" :key="message.id" class="message"> {{ message.content }} </div> </div> <div class="input-area"> <el-input v-model="inputMessage" placeholder="输入问题..." @keyup.enter="sendMessage" :disabled="isLoading" /> <el-button type="primary" @click="sendMessage" :loading="isLoading" > 发送 </el-button> </div> <el-dialog v-model="errorDialogVisible" title="错误" width="30%" > <span>{{ errorMessage }}</span> <template #footer> <el-button @click="errorDialogVisible = false">确定</el-button> </template> </el-dialog> </div> </template> <script setup> import { ref } from 'vue' import { ElMessage } from 'element-plus' const inputMessage = ref('') const messages = ref([]) const isLoading = ref(false) const errorDialogVisible = ref(false) const errorMessage = ref('') const sendMessage = async () => { if (!inputMessage.value.trim()) return isLoading.value = true messages.value.push({ id: Date.now(), content: inputMessage.value, type: 'user' }) try { const response = await chatApi.sendMessage({ message: inputMessage.value, knowledgeId: currentKnowledgeId.value }) messages.value.push({ id: Date.now() + 1, content: response.data.answer, type: 'assistant' }) inputMessage.value = '' } catch (error) { errorMessage.value = error.response?.data?.message || '发送消息失败' errorDialogVisible.value = true } finally { isLoading.value = false } } </script>9. 总结
通过本指南,我们详细介绍了如何使用Vue3开发WeKnora产品文档系统的前端界面。从环境搭建、项目架构设计到核心组件开发,每个环节都提供了具体的实现代码和最佳实践。
实际开发中,前端界面需要与后端API紧密配合,确保数据流的一致性和用户体验的流畅性。建议在开发过程中注重以下几点:
- 组件化开发:保持组件的单一职责和高复用性
- 状态管理:合理使用Pinia管理应用状态,避免props drilling
- 错误处理:提供友好的错误提示和恢复机制
- 性能优化:关注首屏加载时间和运行时性能
- 响应式设计:确保在不同设备上都有良好的用户体验
前端技术发展迅速,建议持续关注Vue生态的最新动态,及时应用新的特性和优化方案,不断提升WeKnora前端界面的质量和用户体验。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
