在线教育系统开发实战(四):前端界面设计与用户体验优化
前言
在前面三篇文章中,我们详细介绍了系统架构、数据库设计和后端核心功能实现。本文将重点讲解前端界面的设计与实现,包括Vue组件开发、Element UI的使用、用户体验优化等内容。
前端技术架构
技术栈选择
-
Vue 2.6.12:渐进式JavaScript框架,易学易用
-
Element UI 2.15.6:基于Vue的企业级UI组件库
-
Vue Router 3.4.9:官方路由管理器
-
Vuex 3.6.0:状态管理模式
-
Axios 0.21.0:HTTP客户端库
-
ECharts 4.9.0:数据可视化图表库
项目结构设计
fronteducation/├── public/│ ├── index.html # 入口HTML文件│ └── favicon.ico # 网站图标├── src/│ ├── api/ # API接口定义│ │ ├── system/ # 系统管理相关API│ │ │ ├── exam.js # 考试管理API│ │ │ ├── student.js # 学生管理API│ │ │ └── user.js # 用户管理API│ │ └── login.js # 登录相关API│ ├── assets/ # 静态资源│ │ ├── images/ # 图片资源│ │ ├── styles/ # 样式文件│ │ └── icons/ # 图标资源│ ├── components/ # 通用组件│ │ ├── Pagination/ # 分页组件│ │ ├── RightToolbar/ # 右侧工具栏│ │ └── IconSelect/ # 图标选择器│ ├── layout/ # 布局组件│ │ ├── components/ # 布局相关组件│ │ └── index.vue # 主布局│ ├── router/ # 路由配置│ │ └── index.js # 路由定义│ ├── store/ # Vuex状态管理│ │ ├── modules/ # 模块化store│ │ ├── getters.js # 全局getters│ │ └── index.js # store入口│ ├── utils/ # 工具函数│ │ ├── request.js # HTTP请求封装│ │ ├── auth.js # 认证相关工具│ │ └── validate.js # 表单验证工具│ ├── views/ # 页面组件│ │ ├── exam/ # 考试管理页面│ │ ├── sys/ # 系统管理页面│ │ └── login/ # 登录页面│ ├── App.vue # 根组件│ └── main.js # 应用入口├── package.json # 项目配置└── vue.config.js # Vue CLI配置
核心页面设计与实现
1. 考试管理页面
页面布局设计
考试管理页面采用经典的列表+表单的布局模式,包含搜索区域、操作区域、数据表格和弹窗表单。
搜索 重置 新增 删除 {{ getStatusText(scope.row.status) }} 编辑 = 2\" >删除 = 2\" >参与对象 = 2\" > {{ scope.row.status === 0 ? \'开始\' : scope.row.status === 1 ? \'启动\' : \'已开始\' }} 0\" :total=\"total\" :page.sync=\"queryParams.current\" :limit.sync=\"queryParams.size\" @pagination=\"getList\" />
核心JavaScript逻辑
import { listExams, addExam, updateExam, deleteExam, getExamById, listPapers } from \'@/api/system/exam\'export default { name: \'ExamList\', data() { return { // 遮罩层 loading: true, // 选中数组 ids: [], // 非单个禁用 single: true, // 非多个禁用 multiple: true, // 显示搜索条件 showSearch: true, // 总条数 total: 0, // 考试表格数据 list: [], // 试卷选项 paperList: [], // 弹出层标题 title: \"\", // 是否显示弹出层 open: false, // 查询参数 queryParams: { current: 1, size: 10, name: undefined, status: undefined, creatorId: undefined, examType: undefined }, // 表单参数 form: {}, // 表单校验 rules: { subject: [ { required: true, message: \"考试科目不能为空\", trigger: \"blur\" } ], paperId: [ { required: true, message: \"关联试卷不能为空\", trigger: \"change\" } ], startTime: [ { required: true, message: \"开始时间不能为空\", trigger: \"change\" } ], endTime: [ { required: true, message: \"结束时间不能为空\", trigger: \"change\" } ], durationMin: [ { required: true, message: \"考试时长不能为空\", trigger: \"blur\" } ] } } }, created() { this.getList() this.getPaperList() }, methods: { /** 查询考试列表 */ getList() { this.loading = true listExams(this.queryParams).then(res => { if (res && res.code === 200) { this.list = res.data || [] this.total = res.total || 0 } else { this.$message.error(res.message || \'获取考试列表失败\') } this.loading = false }).catch(err => { console.error(\'考试列表错误:\', err) this.$message.error(\'网络错误,请检查后端服务\') this.loading = false }) }, /** 状态显示文字 */ getStatusText(status) { const statusMap = { 0: \'计划\', 1: \'就绪\', 2: \'进行中\', 3: \'已结束\', 4: \'已评分\', 5: \'已发布\' } return statusMap[status] || \'未知\' }, /** 状态标签类型 */ getStatusType(status) { const typeMap = { 0: \'info\', 1: \'warning\', 2: \'primary\', 3: \'success\', 4: \'success\', 5: \'success\' } return typeMap[status] || \'info\' }, /** 状态修改 */ handleStatusChange(row) { let text = row.status === 0 ? \"开始\" : \"启动\" let newStatus = row.status === 0 ? 1 : 2 this.$confirm(\'确认要\"\' + text + \'\"\"\' + row.subject + \'\"考试吗?\').then(() => { const updateData = { examId: row.examId, paperId: row.paperId, subject: row.subject, startTime: row.startTime, endTime: row.endTime, durationMin: row.durationMin, rulesJson: row.rulesJson, status: newStatus, creatorId: row.creatorId, createTime: row.createTime } return updateExam(updateData) }).then((response) => { if (response && response.code === 200) { this.$message.success(text + \"成功\") this.getList() } else { this.$message.error(response.message || text + \'失败\') } }).catch((error) => { console.error(\'状态更新失败:\', error) }) }, /** 参与对象管理 */ handleParticipants(row) { this.$router.push({ path: \'/exam/participants\', query: { examId: row.examId, examSubject: row.subject } }) } }}
2. API接口封装
HTTP请求统一封装
// utils/request.jsimport axios from \'axios\'import { MessageBox, Message } from \'element-ui\'import store from \'@/store\'import { getToken } from \'@/utils/auth\'// 创建axios实例const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API || \'http://localhost:8089\', // api的base_url timeout: 5000 // 请求超时时间})// request拦截器service.interceptors.request.use( config => { // 是否需要设置 token const isToken = (config.headers || {}).isToken === false if (getToken() && !isToken) { config.headers[\'Authorization\'] = \'Bearer \' + getToken() // 让每个请求携带自定义token } return config }, error => { console.log(error) Promise.reject(error) })// response 拦截器service.interceptors.response.use( response => { const res = response.data // 如果自定义代码不是200,则判断为错误 if (res.code !== 200) { Message({ message: res.message || \'Error\', type: \'error\', duration: 5 * 1000 }) // 401: 未授权 if (res.code === 401) { MessageBox.confirm(\'登录状态已过期,您可以继续留在该页面,或者重新登录\', \'系统提示\', { confirmButtonText: \'重新登录\', cancelButtonText: \'取消\', type: \'warning\' }).then(() => { store.dispatch(\'LogOut\').then(() => { location.href = \'/login\' }) }) } return Promise.reject(new Error(res.message || \'Error\')) } else { return res } }, error => { console.log(\'err\' + error) let { message } = error if (message == \"Network Error\") { message = \"后端接口连接异常\" } else if (message.includes(\"timeout\")) { message = \"系统接口请求超时\" } else if (message.includes(\"Request failed with status code\")) { message = \"系统接口\" + message.substr(message.length - 3) + \"异常\" } Message({ message: message, type: \'error\', duration: 5 * 1000 }) return Promise.reject(error) })export default service
考试管理API接口
// api/system/exam.jsimport request from \'@/utils/request\'// 查询考试列表export function listExams(query) { return request({ url: \'/exam/list\', method: \'get\', params: query })}// 查询考试详细export function getExamById(examId) { return request({ url: \'/exam/\' + examId, method: \'get\' })}// 新增考试export function addExam(data) { return request({ url: \'/exam/add\', method: \'post\', data: data })}// 修改考试export function updateExam(data) { return request({ url: \'/exam/update\', method: \'put\', data: data })}// 删除考试export function deleteExam(examId) { return request({ url: \'/exam/delete/\' + examId, method: \'delete\' })}// 查询试卷列表export function listPapers(query) { return request({ url: \'/paper/list\', method: \'get\', params: query })}
3. 用户体验优化
加载状态处理
.loading-container { padding: 20px;}.empty-container { padding: 60px 0; text-align: center;}
错误处理与提示
// 统一错误处理methods: { async handleApiCall() { try { this.loading = true const response = await someApiCall() if (response.code === 200) { this.$message.success(\'操作成功\') this.refreshData() } else { this.$message.error(response.message || \'操作失败\') } } catch (error) { console.error(\'API调用失败:\', error) if (error.response) { // 服务器响应错误 const status = error.response.status if (status === 401) { this.$message.error(\'登录已过期,请重新登录\') this.$router.push(\'/login\') } else if (status === 403) { this.$message.error(\'权限不足\') } else if (status === 500) { this.$message.error(\'服务器内部错误\') } else { this.$message.error(\'请求失败:\' + status) } } else if (error.request) { // 网络错误 this.$message.error(\'网络连接失败,请检查网络设置\') } else { // 其他错误 this.$message.error(\'操作失败:\' + error.message) } } finally { this.loading = false } }}
表单验证优化
// 自定义验证规则const validatePhone = (rule, value, callback) => { if (value && !/^1[3-9]\\d{9}$/.test(value)) { callback(new Error(\'请输入正确的手机号\')) } else { callback() }}const validateEmail = (rule, value, callback) => { if (value && !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value)) { callback(new Error(\'请输入正确的邮箱地址\')) } else { callback() }}// 表单验证规则rules: { username: [ { required: true, message: \'用户名不能为空\', trigger: \'blur\' }, { min: 3, max: 20, message: \'用户名长度在 3 到 20 个字符\', trigger: \'blur\' } ], phone: [ { validator: validatePhone, trigger: \'blur\' } ], email: [ { validator: validateEmail, trigger: \'blur\' } ]}
4. 响应式设计
移动端适配
// 响应式样式.app-container { padding: 20px; @media (max-width: 768px) { padding: 10px; .el-form--inline .el-form-item { display: block; margin-right: 0; margin-bottom: 15px; } .el-table { font-size: 12px; .el-table__header th, .el-table__body td { padding: 8px 0; } } .el-button--mini { padding: 5px 8px; font-size: 11px; } } @media (max-width: 480px) { .el-table { .el-table-column--selection { display: none; } .operation-column { .el-button { display: block; margin: 2px 0; width: 100%; } } } }}
弹性布局应用
.dashboard-container { padding: 20px;}.stat-card { display: flex; align-items: center; padding: 20px; background: #fff; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); transition: all 0.3s; &:hover { transform: translateY(-2px); box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.15); }}.stat-icon { width: 60px; height: 60px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; font-size: 24px; margin-right: 15px;}.stat-content { flex: 1;}.stat-number { font-size: 28px; font-weight: bold; color: #303133; line-height: 1;}.stat-label { font-size: 14px; color: #909399; margin-top: 5px;}{{ userCount }}用户总数
性能优化策略
1. 组件懒加载
// router/index.jsconst routes = [ { path: \'/exam\', component: Layout, children: [ { path: \'list\', name: \'ExamList\', component: () => import(\'@/views/exam/Exam/index\'), // 懒加载 meta: { title: \'考试管理\', icon: \'exam\' } } ] }]
2. 图片懒加载
3. 虚拟滚动
4. 防抖与节流
import { debounce, throttle } from \'lodash\'export default { data() { return { searchKeyword: \'\' } }, watch: { searchKeyword: { handler: debounce(function(newVal) { this.handleSearch(newVal) }, 300), immediate: false } }, methods: { // 搜索处理(防抖) handleSearch(keyword) { console.log(\'执行搜索:\', keyword) // 执行搜索逻辑 }, // 滚动处理(节流) handleScroll: throttle(function(event) { console.log(\'滚动事件:\', event) // 执行滚动逻辑 }, 100) }}
主题定制与样式管理
1. Element UI主题定制
// styles/element-variables.scss/* 改变主题色变量 */$--color-primary: #409EFF;$--color-success: #67C23A;$--color-warning: #E6A23C;$--color-danger: #F56C6C;$--color-info: #909399;/* 改变 icon 字体路径变量,必需 */$--font-path: \'~element-ui/lib/theme-chalk/fonts\';@import \"~element-ui/packages/theme-chalk/src/index\";
2. 全局样式管理
// styles/index.scss// 全局样式// 重置样式* { box-sizing: border-box;}body { margin: 0; padding: 0; font-family: \'Helvetica Neue\', Helvetica, \'PingFang SC\', \'Hiragino Sans GB\', \'Microsoft YaHei\', \'微软雅黑\', Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;}// 通用类.clearfix::after { content: \"\"; display: table; clear: both;}.text-center { text-align: center;}.text-right { text-align: right;}.pull-left { float: left;}.pull-right { float: right;}// 间距类.mt-10 { margin-top: 10px; }.mt-20 { margin-top: 20px; }.mb-10 { margin-bottom: 10px; }.mb-20 { margin-bottom: 20px; }// 动画类.fade-enter-active, .fade-leave-active { transition: opacity 0.3s;}.fade-enter, .fade-leave-to { opacity: 0;}// 卡片样式.custom-card { background: #fff; border-radius: 8px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); padding: 20px; margin-bottom: 20px; transition: all 0.3s; &:hover { box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.15); }}
国际化支持
1. 多语言配置
// i18n/index.jsimport Vue from \'vue\'import VueI18n from \'vue-i18n\'import elementEnLocale from \'element-ui/lib/locale/lang/en\'import elementZhLocale from \'element-ui/lib/locale/lang/zh-CN\'import enLocale from \'./en\'import zhLocale from \'./zh\'Vue.use(VueI18n)const messages = { en: { ...enLocale, ...elementEnLocale }, zh: { ...zhLocale, ...elementZhLocale }}const i18n = new VueI18n({ locale: \'zh\', // 默认语言 messages})export default i18n
2. 语言包定义
// i18n/zh.jsexport default { route: { dashboard: \'首页\', exam: \'考试管理\', student: \'学生管理\', teacher: \'教师管理\' }, navbar: { logOut: \'退出登录\', profile: \'个人中心\', theme: \'换肤\', size: \'布局大小\' }, exam: { title: \'考试管理\', subject: \'考试科目\', status: \'状态\', startTime: \'开始时间\', endTime: \'结束时间\', duration: \'考试时长\', participants: \'参与对象\' }}
小结
本文详细介绍了在线教育系统前端界面的设计与实现,包括Vue组件开发、Element UI的使用、用户体验优化、性能优化等方面。良好的前端设计不仅能提升用户体验,还能提高系统的易用性和维护性。
下一篇文章将介绍《在线教育系统开发实战(五):系统部署与运维实践》,详细讲解项目的部署、监控、维护等运维相关内容。