当前位置: 首页 > news >正文

【前端】Vue 3 课程选择组件开发实战:从设计到实现

文章目录

    • 前言
    • 一、组件设计思路
      • 1. 需求分析
      • 2. 技术选型
    • 二、核心功能实现
      • 1. 数据结构设计
      • 2. 动态样式计算
      • 3. 颜色处理工具函数
    • 三、交互实现
      • 1. 点击事件处理
      • 2. 键盘可访问性
      • 3. 状态管理
    • 四、样式设计
      • 1. 基础布局
      • 2. 卡片效果
      • 3. 交互状态
      • 4. 响应式设计
    • 五、完整页面代码
    • 六、实现效果
    • 七、优化与改进
      • 1. 性能优化
      • 2. 可访问性增强
      • 3. 代码组织
    • 总结


前言

今天我将分享一个基于 Vue 3 和 Element Plus 的课程选择组件的开发过程。这个组件不仅实现了基本的课程选择功能,还包含了精美的UI设计和良好的交互体验。下面我们将从组件设计、功能实现到样式优化进行全面解析。

一、组件设计思路

1. 需求分析

我们需要实现一个课程选择界面,要求:

  • 以卡片形式展示多个课程
  • 点击卡片时显示课程详情
  • 有视觉反馈的交互效果
  • 良好的可访问性支持
  • 响应式布局适配不同设备

2. 技术选型

  • Vue 3:使用 <script setup> 语法简化代码
  • Element Plus:利用其 ElMessage 组件提供用户反馈
  • CSS 变量:实现动态主题色
  • 绝对定位:创建自由布局的卡片位置

二、核心功能实现

1. 数据结构设计

const courses = [{id: 'math',name: '高等数学',description: '涵盖微积分、线性代数等核心数学知识',color: '#f7b100',activeColor: '#ffcc00',position: { top: '0%', left: '0.2%', width: '18%', height: '48%' }},// 其他课程...
]

每个课程对象包含:

  • 基础信息:id、name、description
  • 样式配置:color、activeColor
  • 布局参数:position 对象定义绝对定位属性

2. 动态样式计算

const getCourseStyle = (course) => {const isActive = activeId.value === course.idconst baseColor = isActive ? course.activeColor : course.colorconst darkenColor = adjustColor(baseColor, isActive ? -25 : -15)return {top: course.position.top,left: course.position.left,width: course.position.width,height: course.position.height,'--base-color': baseColor,'--darken-color': darkenColor,'--text-color': getContrastColor(baseColor)}
}

这里使用了 CSS 变量来实现动态主题色,通过计算属性返回样式对象。

3. 颜色处理工具函数

组件中实现了两个实用的颜色处理函数:

adjustColor 函数:调整颜色亮度

const adjustColor = (hexColor, percent) => {// 处理3位或6位hex颜色// 转换为RGB数值// 按百分比调整亮度// 返回新的hex颜色
}

getContrastColor 函数:自动计算对比色

const getContrastColor = (hexColor) => {// 计算颜色亮度// 根据亮度返回黑色或白色
}

三、交互实现

1. 点击事件处理

const handleCourseClick = (course) => {activeId.value = course.idElMessage.success({message: `已选中课程: ${course.name}`,duration: 1500})
}

使用 Element Plus 的 ElMessage 提供用户反馈,增强交互体验。

2. 键盘可访问性

<div @keydown.enter="handleCourseClick(course)"tabindex="0"role="button":aria-label="`选择课程${course.name}`"
>

添加了键盘事件支持和 ARIA 属性,使组件可以通过键盘操作。

3. 状态管理

使用 activeId ref 来跟踪当前选中的课程,通过计算属性动态应用样式类:

:class="{ active: activeId === course.id }"

四、样式设计

1. 基础布局

.course-selection-container {position: relative;width: 100%;height: 600px;margin: 20px auto;
}.course-box {position: absolute;/* 其他样式... */
}

使用绝对定位创建自由布局,容器设置固定高度。

2. 卡片效果

.course-box {border-radius: 12px;cursor: pointer;transition: all 0.3s ease;background: linear-gradient(135deg, var(--base-color), var(--darken-color));box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
  • 圆角边框
  • 平滑过渡动画
  • 渐变背景
  • 阴影效果

3. 交互状态

.course-box:hover {transform: translateY(-5px) scale(1.02);
}.course-box.active {transform: scale(1.05);z-index: 10;box-shadow: 0 12px 24px rgba(0, 0, 0, 0.25);
}
  • 悬停效果:轻微上浮和放大
  • 激活状态:更大缩放和更高阴影

4. 响应式设计

@media (max-width: 768px) {.course-selection-container {height: 1000px;}.course-box {position: relative;width: 90% !important;height: 120px !important;margin: 10px auto;}
}

在小屏幕设备上,将绝对定位改为常规布局,垂直排列卡片。

五、完整页面代码

<template><div class="course-selection-container"><!-- 课程卡片组件 --><divv-for="course in courses":key="course.id"class="course-box":style="getCourseStyle(course)":class="{ active: activeId === course.id }"@click="handleCourseClick(course)"@keydown.enter="handleCourseClick(course)"tabindex="0"role="button":aria-label="`选择课程${course.name}`"><div class="course-name">{{ course.name }}</div><div class="course-desc" v-if="activeId === course.id">{{ course.description }}</div></div></div>
</template><script setup>
import { ref, computed } from 'vue'
import { ElMessage } from 'element-plus'// 当前选中的课程ID
const activeId = ref('math')// 课程数据配置
const courses = [{id: 'math',name: '高等数学',description: '涵盖微积分、线性代数等核心数学知识',color: '#f7b100',activeColor: '#ffcc00',position: { top: '0%', left: '0.2%', width: '18%', height: '48%' }},{id: 'physics',name: '大学物理',description: '力学、电磁学、热力学等物理基础',color: '#0096ff',activeColor: '#00b4ff',position: { top: '0%', left: '20.2%', width: '18%', height: '48%' }},{id: 'chemistry',name: '有机化学',description: '有机化合物结构与反应机理研究',color: '#32c832',activeColor: '#4ce64c',position: { top: '0%', left: '40.2%', width: '18%', height: '48%' }},{id: 'programming',name: '程序设计',description: '编程基础与算法思维训练',color: '#ff6400',activeColor: '#ff8200',position: { top: '0%', left: '60.2%', width: '18%', height: '48%' }},{id: 'english',name: '学术英语',description: '学术写作与专业英语能力提升',color: '#6464ff',activeColor: '#8282ff',position: { top: '50%', left: '0.2%', width: '18%', height: '48%' }},{id: 'history',name: '世界历史',description: '全球文明发展与历史事件分析',color: '#b464b4',activeColor: '#d282d2',position: { top: '50%', left: '20.2%', width: '18%', height: '48%' }},{id: 'art',name: '艺术设计',description: '视觉艺术原理与创意设计实践',color: '#ff3296',activeColor: '#ff50b4',position: { top: '50%', left: '40.2%', width: '18%', height: '48%' }}
]// 计算课程卡片样式
const getCourseStyle = (course) => {const isActive = activeId.value === course.idconst baseColor = isActive ? course.activeColor : course.colorconst darkenColor = adjustColor(baseColor, isActive ? -25 : -15)return {top: course.position.top,left: course.position.left,width: course.position.width,height: course.position.height,'--base-color': baseColor,'--darken-color': darkenColor,'--text-color': getContrastColor(baseColor)}
}// 处理课程点击
const handleCourseClick = (course) => {activeId.value = course.idElMessage.success({message: `已选中课程: ${course.name}`,duration: 1500})
}// 颜色调整工具函数(优化版)
const adjustColor = (hexColor, percent) => {// 确保hexColor是6位十六进制格式let hex = hexColor.replace('#', '')if (hex.length === 3) {hex = hex.split('').map(x => x + x).join('')}// 转换为RGBlet r = parseInt(hex.substring(0, 2), 16)let g = parseInt(hex.substring(2, 4), 16)let b = parseInt(hex.substring(4, 6), 16)// 调整亮度r = Math.min(255, Math.max(0, r + Math.round(r * percent / 100)))g = Math.min(255, Math.max(0, g + Math.round(g * percent / 100)))b = Math.min(255, Math.max(0, b + Math.round(b * percent / 100)))// 返回hex格式return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`
}// 获取对比色(确保文字可读性)
const getContrastColor = (hexColor) => {const hex = hexColor.replace('#', '')const r = parseInt(hex.substring(0, 2), 16)const g = parseInt(hex.substring(2, 4), 16)const b = parseInt(hex.substring(4, 6), 16)const brightness = (r * 299 + g * 587 + b * 114) / 1000return brightness > 128 ? '#333' : '#fff'
}
</script><style scoped>
.course-selection-container {position: relative;width: 100%;height: 600px;margin: 20px auto;
}.course-box {position: absolute;border-radius: 12px;cursor: pointer;transition: all 0.3s ease;display: flex;flex-direction: column;align-items: center;justify-content: center;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);background: linear-gradient(135deg, var(--base-color), var(--darken-color));color: var(--text-color);overflow: hidden;outline: none;
}.course-box:hover {transform: translateY(-5px) scale(1.02);box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
}.course-box.active {transform: scale(1.05);z-index: 10;box-shadow: 0 12px 24px rgba(0, 0, 0, 0.25);
}.course-box:focus-visible {outline: 2px solid var(--darken-color);outline-offset: 2px;
}.course-name {font-size: 1.25rem;font-weight: bold;text-align: center;padding: 12px;transition: all 0.3s ease;
}.course-desc {font-size: 0.875rem;padding: 0 12px 12px;text-align: center;max-width: 90%;opacity: 0;max-height: 0;transition: all 0.3s ease;
}.course-box.active .course-desc {opacity: 1;max-height: 100px;
}@media (max-width: 768px) {.course-selection-container {height: 1000px;}.course-box {position: relative;top: auto !important;left: auto !important;width: 90% !important;height: 120px !important;margin: 10px auto;}
}
</style>

六、实现效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

七、优化与改进

1. 性能优化

  • 使用 CSS 变量减少重复计算
  • 避免在模板中进行复杂计算
  • 合理使用 transition 实现平滑动画

2. 可访问性增强

  • 添加 tabindex 使元素可聚焦
  • 使用 ARIA 属性描述元素
  • 键盘事件支持
  • 高对比度文字颜色

3. 代码组织

  • 将样式计算逻辑提取到单独函数
  • 使用计算属性缓存结果
  • 清晰的代码注释

总结

这个课程选择组件展示了 Vue 3 的多种特性应用:

  1. 使用 <script setup> 简化组合式 API 代码
  2. 动态样式绑定和 CSS 变量实现主题化
  3. 完善的交互状态管理
  4. 响应式布局设计
  5. 可访问性最佳实践

组件可以轻松扩展:

  • 添加更多课程只需在数据中增加配置
  • 可以通过 props 接收外部课程数据
  • 可以添加 emit 事件实现父子组件通信

希望这个实现案例对你的 Vue 开发有所帮助,你可以根据实际需求调整样式和功能。

http://www.dtcms.com/a/299830.html

相关文章:

  • 如何从自定义或本地仓库安装 VsCode 扩展
  • 手写PPO_clip(FrozenLake环境)
  • 统计学08:概率分布
  • 面试实战,问题十二,Spring Boot接收和处理HTTP请求的详细原理,怎么回答
  • AI 编程工具 Trae 重要的升级。。。
  • 二维数组相关学习
  • 栈----3.字符串解码
  • 论文阅读-RaftStereo
  • 2025中国GEO优化白皮书:AI搜索优化趋势+行业数据报告
  • 应急控制HMI的“黄金10秒”设计:紧急场景下的操作路径极速简化技术
  • 嵌入式硬件篇---有线串口通信问题解决
  • PHP语法高级篇(六):面向对象编程
  • MyBatis-Plus 核心注解详解:从表映射到逻辑删除的全方位指南
  • C++/CLI vs 标准 C++ vs C# 语法对照手册
  • 9.3 快速傅里叶变换
  • 深度解析 noisereduce:开源音频降噪库实践
  • 深入理解Redission释放锁过程
  • Blender入门笔记(一)
  • 利用RAII与析构函数避免C++资源泄漏
  • 基于DataX的数据同步实战
  • 中电建路桥集团有限公司重大项目管理办公室成立
  • 【安全漏洞】网络守门员:深入理解与应用iptables,守护Linux服务器安全
  • Linux 如何统计系统上各个用户登录(或者登出)记录出现的次数?
  • Ubuntu安装node-red
  • 磁悬浮轴承转子不平衡质量控制策略设计:原理、分析与智能实现
  • C/C++中常量放置在比较操作符左侧
  • 基于匿名管道的多进程任务池实现与FD泄漏解决方案
  • 消息缓存系统
  • Docker学习日志-Docker容器配置、Nginx 配置与文件映射
  • Vim 进阶教程