H5 中 van-popup 的使用以及题目的切换

H5 中 van-popup 的使用以及题目的切换

在移动端开发中,弹窗组件是一个常见的需求。vant 是一个轻量、可靠的移动端 Vue 组件库,其中的 van-popup 组件可以方便地实现弹窗效果。本文将介绍如何使用 van-popup 实现题目详情的弹窗展示,并实现题目的切换功能。

关键点总结

  1. 引入 van-popup 组件

    • 在 Vue 项目中引入 vant 组件库,并使用 van-popup 组件实现弹窗效果。
    • import { createApp } from 'vue'
      import Vant from 'vant'
      
      const app = createApp(App)
      app.use(Vant)
      app.mount('#app')
  2. 弹窗内容的条件渲染

    • 根据不同的题目类型(如互动题和练习题),在弹窗中显示不同的内容。
  3. 题目详情的展示

    • 使用 computed 属性计算当前题目的详情,并在弹窗中展示题目的相关信息。
  4. 题目的切换

    • 通过按钮实现题目的上一题和下一题的切换,并更新当前题目的索引。
代码示例

以下是实现上述功能的关键代码片段:

questions.vue---子组件

<template>
  <van-popup v-model:show="localVisible" position="bottom" round :style="{ height: '80%' }" @close="close">
    <div v-if="type === 'interactive'">
      <div class="picker-header">
        <div class="picker-title">
          题目详情
          <button @click="close" class="close-button">X</button>
        </div>
        <div class="picker-info">
          <div class="left-info">
            <span class="number">第{{ currentQuestion.serial_number }}题</span>
            <span class="status">{{ getStatusText(currentQuestion.status) }}</span>
          </div>
          <div class="right-info">
            <button v-if="!isFirstQuestion" @click="prevQuestion">
              <van-icon name="arrow-left" />
            </button>
            <span>{{ currentQuestion.serial_number }}/{{ questions.length }}</span>
            <button v-if="!isLastQuestion" @click="nextQuestion">
              <van-icon name="arrow" />
            </button>
          </div>
        </div>
      </div>
      <div class="picker-content">
        <div class="section-title">课件页面</div>
        <iframe :src="currentQuestion.previewUrl" frameborder="0"></iframe>
        <div class="use-duration">
          我的用时:
          <span class="time-number">{{ formattedDuration.minutes }}</span>分 <span class="time-number">{{
            formattedDuration.seconds }}</span>秒
        </div>
      </div>
    </div>
    <div v-else-if="type === 'practice'">
      //  其他内容
    </div>
  </van-popup>
</template>

<script setup>
import { defineProps, defineEmits, computed, ref, watch } from 'vue'
import { Popup } from 'vant'

const props = defineProps({
  visible: Boolean,
  questions: {
    type: Array,
    required: true,
  },
  currentQuestionIndex: {
    type: Number,
    required: true,
  },
  type: {
    type: String,
    required: true,
  },
})

const emits = defineEmits(['close', 'changeQuestion'])

const localVisible = ref(props.visible)

watch(
  () => props.visible,
  newVal => {
    localVisible.value = newVal
  },
)

const currentQuestion = computed(() => {
  const question = props.questions[props.currentQuestionIndex] || {}
  if (props.type === 'practice' && !question.serial_number) {
    question.serial_number = props.currentQuestionIndex + 1
  }
  return question
})

const getStatusText = status => {
  switch (status) {
    case 1:
      return '正确'
    case 2:
      return '错误'
    case 3:
      return '半对半错'
    default:
      return '未作答'
  }
}

const formatDuration = duration => {
  const minutes = String(Math.floor(duration / 60)).padStart(2, '0')
  const seconds = String(duration % 60).padStart(2, '0')
  return { minutes, seconds }
}

const formattedDuration = computed(() => formatDuration(currentQuestion.value.use_duration))

const isFirstQuestion = computed(() => props.currentQuestionIndex === 0)
const isLastQuestion = computed(() => props.currentQuestionIndex === props.questions.length - 1)

const prevQuestion = () => {
  if (!isFirstQuestion.value) {
    emits('changeQuestion', props.currentQuestionIndex - 1)
  }
}

const nextQuestion = () => {
  if (!isLastQuestion.value) {
    emits('changeQuestion', props.currentQuestionIndex + 1)
  }
}

const close = () => {
  emits('close')
}
</script>

<style lang="less" scoped>
.picker-header {
  padding: 10px;
}

.picker-title {
  font-size: 18px;
  font-weight: bold;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
  color: #000;
  margin-top: 10px;
  display: flex;
  width: 100%;

  .close-button {
    background: none;
    border: none;
    font-size: 16px;
    margin-left: auto;
    color: #a9aeb8;
    cursor: pointer;
  }
}

.picker-info {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 10px 0 10px;
}

.left-info {
  display: flex;
  flex-direction: row;

  .number {
    margin-right: 20px;
    font-size: 16px;
    font-weight: 500;
  }

  .status {
    font-size: 16px;
    font-weight: 500;
    color: #1f70ff;
  }
}

.right-info {
  display: flex;
  position: absolute;
  right: 10px;
  color: #a9aeb8;

  .right-icon {
    width: 28px;
    height: 28px;
  }
}

.right-info button {
  background: none;
  border: none;
  font-size: 16px;
  cursor: pointer;
  margin: 0 5px;
}

.picker-content {
  padding: 10px 20px 0 20px;
}

.section-title {
  font-size: 16px;
  font-family: PingFangSC-Regular, PingFang SC;
  font-weight: 400;
  color: #2b2f38;
}

iframe {
  width: 100%;
  height: 300px;
  border: none;
  margin-bottom: 10px;
}

.use-duration {
  font-size: 16px;
  color: #2b2f38;
}

.time-number {
  font-weight: bold;
  color: #0074fc;
  font-size: 24px;
}

.van-popup {
  height: 50%;
  z-index: 99999;
}

.practice-content {
  padding: 0px 20px 0 20px;
}
</style>

courseDetail.vue---父组件

// template关键代码
<div v-for="(item, index) in period.interactive_performance.list" :key="index" :class="[
              'performance-item',
              getStatusClass(item.status),
              { selected: selectedQuestion === index },
            ]" @click="selectQuestion(index, period.interactive_performance.list, 'interactive')">
              <span :class="getQuestionTextClass(item.status, selectedQuestion === index)">{{
                item.serial_number
                }}</span>
            </div>

<div v-for="(item, index) in period.practice_detail.list" :key="index" :class="[
              'practice-item',
              getStatusClass(item.status),
              { selected: selectedPracticeQuestion === index },
            ]" @click="selectPracticeQuestion(index, period.practice_detail.list, 'practice')">
              <div class="question-number">
                <span>{{ index + 1 }}</span>
              </div>
</div>

<QuestionDetail :visible="showQuestionDetail" :questions="currentQuestions" :type="currentType"
      :currentQuestionIndex="currentQuestionIndex" @close="closeQuestionDetail" @changeQuestion="changeQuestion" />

// script关键代码
const selectQuestion = (index, questions, type) => {
  selectedQuestion.value = index
  currentQuestions.value = questions
  currentType.value = type
  currentQuestionIndex.value = index
  showQuestionDetail.value = true
}

const selectPracticeQuestion = (index, questions, type) => {
  selectedPracticeQuestion.value = index
  currentQuestions.value = questions
  currentQuestionIndex.value = index
  // 设置 serial_number 属性
  currentQuestions.value.forEach((question, idx) => {
    question.serial_number = idx + 1
  })
  currentType.value = type
  showQuestionDetail.value = true
}
const changeQuestion = index => {
  currentQuestionIndex.value = index
}

数据结构

关键点解析
  1. 引入 van-popup 组件

    • 在模板中使用 <van-popup> 标签,并通过 v-model:show 绑定弹窗的显示状态。
    • 设置 position="bottom" 和 round 属性,使弹窗从底部弹出并带有圆角。
  2. 弹窗内容的条件渲染

    • 使用 v-if 和 v-else-if 根据 type 属性的值渲染不同的内容。
    • 当 type 为 interactive 时,显示互动题的详情;当 type 为 practice 时,显示练习题的详情。
  3. 题目详情的展示

    • 使用 computed 属性计算当前题目的详情,并在弹窗中展示题目的相关信息。
    • 通过 currentQuestion 计算属性获取当前题目的详细信息。
  4. 题目的切换

    • 通过按钮实现题目的上一题和下一题的切换,并更新当前题目的索引。
    • 使用 isFirstQuestion 和 isLastQuestion 计算属性判断当前题目是否为第一题或最后一题,以控制按钮的显示和隐藏。

大致效果

通过以上关键点的实现,我们可以在移动端应用中使用 van-popup 组件实现题目详情的弹窗展示,并实现题目的切换功能。希望本文对您有所帮助!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/939022.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

leetcode 36.有效的数独

1.题目要求: 2.题目步骤: 写好判断函数 3.题目代码: class Solution { public:bool isvalid(vector<vector<char>>& board,char num,int row,int col){//先找左下标int leftrow row - 1;while(leftrow > 0){if(board[leftrow][col] num){return fals…

ElasticSearch中的深度分页问题

在使用 ElasticSearch 进行搜索时&#xff0c;很多小伙伴会遇到“深度分页”问题。当需要获取大量的分页数据时&#xff0c;查询性能会急剧下降&#xff0c;甚至导致集群负载过高。这篇文章将深入剖析 ElasticSearch 深度分页的成因、危害&#xff0c;并提供一些常用的优化方案…

Eureka学习笔记-服务端

Eureka学习笔记 服务端 模块设计 Resources &#xff1a;这部分对外暴露了一系列的 Restful 接口。Eureka Client 的注册、心跳、获取服务列表等操作都需要调用这些接口。另外&#xff0c;其他的 Server 在同步 Registry 时也需要调用这些接口。Controller &#xff1a;这里提…

快速上手:利用 FFmpeg 合并音频文件的实用教程

FFmpeg 是一个强大的多媒体处理工具&#xff0c;能够轻松地对音频、视频进行编辑和转换。本文将介绍如何使用 FFmpeg 来合并&#xff08;拼接&#xff09;多个音频文件为一个单一文件。无论您是想要创建播客、音乐混音还是其他任何形式的音频项目&#xff0c;这都是一个非常实用…

在 CUDA C/C++ 中使用共享內存

文章结尾有最新热度的文章,感兴趣的可以去看看。 本文是经过严格查阅相关权威文献和资料,形成的专业的可靠的内容。全文数据都有据可依,可回溯。特别申明:数据和资料已获得授权。本文内容,不涉及任何偏颇观点,用中立态度客观事实描述事情本身 文章有点长(4700字),期望您…

Linux文件属性 --- 硬链接、所有者、所属组

三、硬链接数 1.目录 使用“ll”命令查看&#xff0c;在文件权限的后面有一列数字&#xff0c;这是文件的硬链接数。 对于目录&#xff0c;硬链接的数量是它具有的直接子目录的数量加上其父目录和自身。 下图的“qwe”目录就是“abc”目录的直接子目录。 2.文件 对于文件可…

RAG开发中,如何用Milvus 2.5 BM25算法实现混合搜索

01. 背景 混合搜索(Hybrid Search)作为RAG应用中Retrieve重要的一环&#xff0c;通常指的是将向量搜索与基于关键词的搜索&#xff08;全文检索&#xff09;相结合&#xff0c;并使用RRF算法合并、并重排两种不同检索的结果&#xff0c;最终来提高数据的召回率。全文检索与语义…

SpringAI人工智能开发框架002---SpringAI项目搭建_依赖导入_maven仓库引入_接口中转

然后看依赖.可以看到 看一下官网springAI的最新版本看到是0.8.1 这里我们用1.0.0这个最新的,快照版 1.0.0-snapshot 然后再来看,需要导入这个 spring-ai-openai-spring-boot-starter 这个依赖是openai的.

机器学习之偏差

机器学习中的偏差&#xff08;Bias&#xff09;是指模型的预测值与真实值之间的系统性误差&#xff0c;或者说模型无法准确捕捉数据中复杂模式的能力。偏差通常与模型的假设或学习能力有关&#xff0c;过高的偏差会导致模型的性能不佳&#xff0c;表现为欠拟合。 偏差的来源 模…

(css)鼠标移入或点击改变背景图片

(css)鼠标移入或点击改变背景图片 html <div class"mapTip"><divv-for"(item, index) of legendList":key"index"class"mapTipOne":class"{ active: change index }"click"legendHandle(item, index)"…

二、windows环境下vscode使用wsl教程

本篇文件介绍了在windows系统使用vscode如何连接使用wsl&#xff0c;方便wsl在vscode进行开发。 1、插件安装 双击桌面vscode&#xff0c;按快捷键CtrlShiftX打开插件市场&#xff0c;搜索【WSL】点击安装即可。 2、开启WSL的linux子系统 点击左下方图标【Open a Remote Win…

不同数据中心间海量数据的安全加密传输方案

安当TDE&#xff08;透明数据加密&#xff09;为实现不同数据中心间异地海量数据安全传输提供了一套高效且可靠的解决方案。以下是该解决方案的具体内容&#xff1a; 一、透明加密机制 安当TDE的核心在于其透明加密机制。在数据传输前&#xff0c;TDE能够自动对文件进行加密处…

前后端分离的项目使用nginx 解决 Invalid CORS request

我是这样打算的&#xff0c;前端用nginx代理&#xff0c;使用80 转443 端口走https 前端的地址就是http://yumbo.top 或https://yumbo.top 后端服务地址是&#xff1a;http://yumbo.top:8081 下面是我的完整配置&#xff0c;功能是正常的&#xff0c;加了注释 user nginx; …

边缘智能创新应用大赛获奖作品系列一:智能边缘计算✖软硬件一体化,开启全场景效能革命新征程

边缘智能技术快速迭代&#xff0c;并与行业深度融合。它正重塑产业格局&#xff0c;催生新产品、新体验&#xff0c;带动终端需求增长。为促进边缘智能技术的进步与发展&#xff0c;拓展开发者的思路与能力&#xff0c;挖掘边缘智能应用的创新与潜能&#xff0c;高通技术公司联…

【自动化】Python SeleniumUtil 工具 开启开发者模式 自动安装油猴用户脚本等

【自动化】Python SeleniumUtil 工具 【Python】使用Selenium 操作浏览器 自动化测试 记录-CSDN博客文章浏览阅读58次。文章浏览阅读42次。【附件】Selenium chromedriver 驱动及浏览器下载。【附件】Selenium chromedriver 驱动及浏览器下载-CSDN博客。3.安装Chrome浏览器驱动…

三、使用langchain搭建RAG:金融问答机器人--检索增强生成

经过前面2节数据准备后&#xff0c;现在来构建检索 加载向量数据库 from langchain.vectorstores import Chroma from langchain_huggingface import HuggingFaceEmbeddings import os# 定义 Embeddings embeddings HuggingFaceEmbeddings(model_name"m3e-base")#…

Springboot应用开发:工具类整理

目录 一、编写目的 二、映射工具类 2.1 依赖 2.2 代码 三、日期格式 3.1 依赖 3.2 代码 四、加密 4.1 代码 五、Http请求 5.1 依赖 5.2 代码 六、金额 6.1 代码 七、二维码 7.1 依赖 7.2 代码 八、坐标转换 8.1 代码 九、树结构 9.1 代码 9.1.1 节点 9.1…

【第一节】Git的简介和安装

目录 一、git的介绍 二、git 的安装 2.1 Linux 平台安装 2.2 源码安装 2.3 Windows 平台安装 2.4 Mac 平台安装 2.5 Git 配置 2.5.1 配置文件 2.5.2 用户信息配置 2.5.3 文本编辑器配置 2.5.4 差异分析工具配置 2.5.5 查看配置信息 一、git的介绍 Git 是一种开源的…

数据库和SQL的基本概念

目录 定义数据分类非结构化数据&#xff1a;半结构化数据 :​ 结构化数据 : SQL(Structured Query Language)概念分类 关系模型常用的概念数据库管理概念理解 定义 数据库(Database)是按照数据结构来组织、存储和管理数据的建立在计算机存储设备上的仓库。 数据库是长期储存在…

oneflow深度学习框架使用问题总结(Windows/Linux)

目录 1.简述 2.在Windows下使用Oneflow深度学习框架&#xff08;错误记录&#xff0c;谨慎&#xff0c;官方不支持&#xff0c;需要WSL&#xff09; 2.1安装Anaconda 2.1创建虚拟环境 2.2安装Pytorch 2.3安装Pycharm 2.4 安装Oneflow 3.在Linux下使用Oneflow深度学习框…