卡片式组件封装demo

news2024/9/22 11:26:19

效果视频:

卡片组件

样式还得细调~,时间有限,主要记录一下逻辑。

html结构:
在这里插入图片描述

目录

  • 父组件
    • 数据处理
      • 数据格式
    • 父组件的全部代码
  • 子组件
    • 数据处理
      • props参数
    • 样式部分
      • 三个圆点
        • 点击三圆点在对应位置显示查看弹框
        • 点击非内容部分隐藏查看弹框
      • 遮罩层
    • 子组件的全部代码

父组件

数据处理

在父页面进行v-for循环,灵活根据状态可赋值数组,再根据数组的长度调用卡片组件。

数据格式

  • ASTATE:状态;整型,0表示进行中,1表示超期
  • DESCRIPTION:描述;字符串类型;
  • STARTTIME:开始时间;字符串类型,格式为年月日T时分秒
  • PLANENDTIME:结束时间;字符串类型,格式为年月日T时分秒

以上字段在子组件中皆有用到

[
  {
    "ASTATE": 0,
    "DESCRIPTION": "大名称大名称\n名称:21名称21名称21\n名称:21名称21名称21\n时间:2024-7-17",
    "STARTTIME": "2024-01-03T01:02:03",
    "PLANENDTIME": "2024-01-04T01:02:03",
    "ID": 2
  },
  {
    "ASTATE": 2,
    "DESCRIPTION": "大名称大名称\n名称:21名称21名称21\n名称:21名称21名称21\n时间:2024-7-17",
    "STARTTIME": "2024-01-07T01:02:03",
    "PLANENDTIME": "2024-01-08T01:02:03",
    "ID": 4
  }
]

父组件的全部代码

<!-- 页面名称 -->
<template>
  <div class="homebox-class">
    <div class="mainbox">
      <h2>任务管理</h2>
      <!-- 超期 -->
      <h4 v-if="taskOverdueData.length>0" class="overduetitle">超期 {{taskOverdueData.length}}</h4>
      <div class="outtabsbox">
        <tab-task-com-vue :width="18" :height="16" v-for="(task,index) in taskOverdueData" :taskData="task" :key="'overdue' + index"></tab-task-com-vue>
      </div>

      <!-- 进行中 -->
      <h4 v-if="taskOngoingData.length>0" class="ongoingtitle">进行中 {{taskOngoingData.length}}</h4>
      <div class="outtabsbox">
        <tab-task-com-vue :width="18" :height="16" v-for="(task,index) in taskOngoingData" :taskData="task" :key="'ongoing' + index"></tab-task-com-vue>
      </div>
    </div>
  </div>
</template>

<script>
import testdata from './test.json';
import tabTaskComVue from '@/views/hzevt/abnormal/tabTaskCom.vue';
export default {
  components: { tabTaskComVue },
  props: {},
  data() {
    return {
      taskOverdueData: [],
      taskOngoingData: [],
    };
  },
  watch: {},
  computed: {},
  created() { },
  mounted() {
    console.log('testdata', testdata);
    for (var i of testdata) {
      if (i.ASTATE == 0) {
        this.taskOngoingData.push(i);
      } else if (i.ASTATE == 2) {
        this.taskOverdueData.push(i);
      }
    }
  },
  methods: {},
};
</script>
<style scoped>
.homebox-class {
  width: 100%;
  height: 100%;
}
.mainbox {
  width: 100%;
  height: 100%;
  overflow-y: auto;
}
.mainbox h2 {
  letter-spacing: 2px;
  font-size: 1.2vw;
}
.mainbox h4 {
  margin: 2% 0;
  font-size: 1vw;
}
.overduetitle {
  color: #f26950;
}
.ongoingtitle {
  color: #2cc09c;
}

.outtabsbox {
  display: flex;
  justify-content: flex-start;
  flex-wrap: wrap;
  font-weight: bold;
}
</style>

子组件

数据处理

props参数

宽高单位为百分比。

 props: {
    width: {//卡片的宽度
      type: Number,
      required: true
    },
    height: {//卡片的高度
      type: Number,
      required: true
    },
    taskData: {//父组件传过来对应的item对象
      type: Object,
      required: true
    },
  },

样式部分

三个圆点

圆点的实现是采用三个div并添加border-radius: 50%的样式。
dots: [1, 2, 3],表示有三个圆点。

<div class="dotbox" @click="showTooltip($event)">
          <div class="dot" v-for="(dot, index) in dots" :key="index"></div>
          <div v-if="showPopup" @click="toCheckBtn" class="popup" :style="{ top: popupTop + 'px', left: popupLeft + 'px' }">
            查看
          </div>
</div>
点击三圆点在对应位置显示查看弹框
  1. 获取目标元素的位置信息:
    getBoundingClientRect() 是一个 DOM API 方法,返回一个 DOMRect 对象,提供了目标元素的大小和其相对于视口的位置信息。

  2. 计算鼠标相对于目标元素的偏移量:
    event.clientXevent.clientY 分别是鼠标指针相对于整个文档的坐标位置。
    dotboxRect.leftdotboxRect.top 分别是目标元素的左上角相对于视口的坐标位置。

  3. 设置popup弹框元素的位置信息:

  4. 显示popup弹框元素:

showTooltip(event) {
      const dotboxRect = event.currentTarget.getBoundingClientRect();
      const offsetX = event.clientX - dotboxRect.left;
      const offsetY = event.clientY - dotboxRect.top;
	  //设置popup弹框元素的位置信息
      this.popupTop = offsetY + 'px';
      this.popupLeft = offsetX + 'px';
      this.showPopup = true;//显示popup弹框元素
},
点击非内容部分隐藏查看弹框

内容部分:

<div class="contentbox" ref="contentRef"></div>
  1. mounted添加监听事件
    ①. 获取点击目标信息:
    ②. 判断点击位置
handleClickOutside(event) {
      const clickedInsideContentbox = this.$refs.contentRef.contains(event.target);//获取点击目标信息
      if (!clickedInsideContentbox) {//判断点击位置
        this.showPopup = false;
      }
},

遮罩层

根据showPopup动态控制遮罩层的显示和隐藏

<div v-if="showPopup" class="popup-overlay" @click="togglePopup()"></div>

子组件的全部代码

<!-- 首页使用到的tab框组件 -->
<template>
  <div :style="{ width: `${width}%`, height: `${height}%` }" class="tabsbox">
    <!-- 遮罩层 -->
    <div v-if="showPopup" class="popup-overlay" @click="togglePopup()"></div>

    <div class="tabstitle overduetitle" v-if="taskData.ASTATE==2">超期</div>
    <div class="tabstitle ongoingtitle" v-if="taskData.ASTATE==0">进行中</div>
    <div class="contentbox" ref="contentRef">
      <div class="contenttitlebox">
        <div class="content-title" :class="taskData.ASTATE === 0?'state-0':'state-2'">生产异常流程</div>
        <div class="dotbox" @click="showTooltip($event)">
          <div class="dot" v-for="(dot, index) in dots" :key="index"></div>
          <div v-if="showPopup" @click="toCheckBtn" class="popup" :style="{ top: popupTop + 'px', left: popupLeft + 'px' }">
            查看
          </div>
        </div>
      </div>
      <pre class="content-descbox">{{taskData.DESCRIPTION}}</pre>
      <div class="tabsbottombox">
        <div class="timebox">
          <img src="@/views/system/index/components/d2-page-cover/image/time1.png" alt="">
          <div v-if="taskData.STARTTIME.includes('T')">{{taskData.STARTTIME.replace("T", " ").slice(0, 16)}}</div>
          <div v-else>{{taskData.STARTTIME}}</div>
        </div>
        <div class="timebox" style="justify-content: flex-end;">
          <img src="@/views/system/index/components/d2-page-cover/image/time2.png" alt="">
          <div v-if="taskData.PLANENDTIME.includes('T')">{{taskData.PLANENDTIME.replace("T", " ").slice(0, 16)}}</div>
          <div v-else>{{taskData.PLANENDTIME.replace("T", " ").slice(0, 16)}}</div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  components: {},
  props: {
    width: {
      type: Number,
      required: true
    },
    height: {
      type: Number,
      required: true
    },
    taskData: {
      type: Object,
      required: true
    },
  },
  data() {
    return {
      dots: [1, 2, 3],
      showPopup: false,
      popupTop: 0,
      popupLeft: 0,
    };
  },
  watch: {},
  computed: {},
  created() { },
  beforeDestroy() {
    // 在组件销毁前,移除全局点击事件监听器
    document.removeEventListener('click', this.handleClickOutside);
  },
  mounted() {
    document.addEventListener('click', this.handleClickOutside);
  },
  methods: {
    showTooltip(event) {
      const dotboxRect = event.currentTarget.getBoundingClientRect();
      const offsetX = event.clientX - dotboxRect.left;
      const offsetY = event.clientY - dotboxRect.top;

      this.popupTop = offsetY + 'px';
      this.popupLeft = offsetX + 'px';
      this.showPopup = true;

    },
    togglePopup() {
      // 点击tabsbox时切换popup状态
      this.showPopup = false;
    },
    // 全局点击事件处理函数,用于关闭弹窗
    handleClickOutside(event) {
      const clickedInsideContentbox = this.$refs.contentRef.contains(event.target);
      if (!clickedInsideContentbox) {
        this.showPopup = false;
      }
    },
    closePopup() {
      this.showPopup = false;
    },
    // 查看详情
    toCheckBtn() {
      //你的操作逻辑
    },
  },
  watch: {

  },
};
</script>
<style scoped>
.tabsbox {
  opacity: 0.6;
  border-radius: 15px;
  background: rgba(255, 255, 255, 1);
  border: 1px solid rgba(138, 127, 127, 0.3);
  box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25);
  margin: 0 3% 3% 1%;
  padding: 1%;
  position: relative;
}
.overduetitle {
  color: #f26950;
}
.ongoingtitle {
  color: #2cc09c;
}
.tabstitle {
  font-size: 0.8vw;
  height: 15%;
  border-bottom: 1px solid #e6e8ed;
}
.contentbox {
  height: 85%;
}
.content-title {
  font-size: 1vw;
  height: 10%;
  padding: 2%;
  font-weight: bold;
  position: relative; /* 让元素变为相对定位,以便使用 top 属性 */
}
.content-title::before {
  content: ""; /* 伪元素用于创建边框 */
  position: absolute; /* 绝对定位在元素内部 */
  top: 15px; /* 距离顶部偏移量,根据需要调整 */
  left: -2px; /* 边框从左侧开始 */
  height: 100%; /* 高度为元素高度减去偏移量 */
}
/* ASTATE 为 0 时的伪类元素样式 */
.content-title.state-0::before {
  border-left: 3px solid #2cc09c;
}

/* ASTATE 为 2 时的伪类元素样式 */
.content-title.state-2::before {
  border-left: 3px solid #f26950;
}
.content-descbox {
  height: 66%;
  margin: 0 3% 3%;
  color: #4f545a;
  font-family: none;
  font-size: 0.7vw;
  display: flex;
  align-items: center;
}
.tabsbottombox {
  height: 14%;
  display: flex;
}
.timebox {
  height: 100%;
  width: 100%;
  display: flex;
  align-items: center;
}
.timebox img {
  width: 18px;
  height: 18px;
}
.timebox div {
  font-size: 0.7vw;
  margin-left: 4px;
}

.contenttitlebox {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  justify-content: space-between;
}
.dotbox {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
}
.dotbox:hover {
  cursor: pointer;
}
.dot {
  width: 0.2vw;
  height: 0.3vh;
  background-color: #787b83;
  border-radius: 50%;
  margin: 1.5px;
  cursor: pointer;
}
.popup {
  position: absolute;
  background-color: #fff;
  padding: 5px 0;
  z-index: 1000;
  width: 55px;
  right: 10px;
  top: 10px;
  text-align: center;
  border-radius: 0.3rem;
  color: #8e8f95;
  font-size: 0.8vw;
  border: 1px solid #ededed;
  box-sizing: border-box;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.popup:hover {
  background: #409eff;
  color: #fff;
  box-shadow: 0 2px 12px 0 rgb(0 0 0 / 40%);
}
.popup-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.5); /* 遮罩层颜色 */
  z-index: 1; /* 确保遮罩层在popup之下 */
  border-radius: 15px;
}

.tabsbox {
  /* 默认背景样式 */
  background-color: #fff;
}
</style>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《系统架构设计师教程(第2版)》第12章-信息系统架构设计理论与实践-03-信息系统架构设计方法-ADM架构开发方法

文章目录 1. TOGAF概述1.1 概念1.2 目标1.3 包括的组件1.4 特色 2. ADM 架构开发方法2.1 ADM 的架构开发阶段2.2 各阶段的活动2.3 ADM方法的详细说明2.3.1 准备阶段2.3.2 阶段A——架构愿景2.3.3 阶段 B——业务架构2.3.4 阶段C——信息系统架构3.2.5 阶段 D——技术架构3.2.6 …

STM32智能家居系统教程

目录 引言环境准备智能家居系统基础代码实现&#xff1a;实现智能家居系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;家居智能化管理问题解决方案与优化收尾与总结 1. 引言 智能家居系统通过STM32嵌入…

R语言优雅的把数据基线表(表一)导出到word

基线表&#xff08;Baseline Table&#xff09;是医学研究中常用的一种数据表格&#xff0c;用于在研究开始时呈现参与者的初始特征和状态。这些特征通常包括人口统计学数据、健康状况和疾病史、临床指标、实验室检测、生活方式、社会经济等。 本人在既往文章《scitb包1.6版本发…

Go语言中的并发

简单介绍go中的并发编程. 涉及内容主要为goroutine, goroutine间的通信(主要是channel), 并发控制(等待、退出). 想查看更多与Go相关的内容, 可以查看我的Go编程栏目 Goroutine 语法 在一个函数调用前加上go即可, go func(). 语法很简单, 可以说是并发写起来最简单的程序语言…

数据结构(Java):力扣 二叉树面试OJ题(二)【进阶】

目录 &#x1f48e; 1、题一&#xff1a;二叉树的层序遍历 &#x1f31f; 1.1 思路1&#xff08;递归求解&#xff09; &#x1f31f; 1.1.1 思路1代码 &#x1f506; 1.2 思路2&#xff08;队列求解&#xff09; &#x1f506; 1.2.1 思路2代码 &#x1f48e; 2、题二&…

2024.7.16日 最新版 docker cuda container tookit下载!

nvidia官方指导 https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html 其实就是这几个命令&#xff0c;但是有墙&#xff1a; curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/shar…

【JavaEE】-- 网络编程基础概念(详解)

&#x1f387;&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳&#xff0c;欢迎大佬指点&#xff01; 人生格言: 当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友…

AV1技术学习: Compound Prediction

一、双向 Compound Prediction AV1支持两个参考帧的预测通过多种复合模式线性组合。复合预测公式为 其中&#xff0c;权重m(x, y) is scaled by 64 以进行整数计算&#xff0c;R1(x, y)和R2(x, y)表示两个参考块中位于(x, y)的像素。P(x, y)将按比例缩小 1/64 以形成最终的预测…

十五、【机器学习】【监督学习】- 神经网络回归

系列文章目录 第一章 【机器学习】初识机器学习 第二章 【机器学习】【监督学习】- 逻辑回归算法 (Logistic Regression) 第三章 【机器学习】【监督学习】- 支持向量机 (SVM) 第四章【机器学习】【监督学习】- K-近邻算法 (K-NN) 第五章【机器学习】【监督学习】- 决策树…

MyPostMan 迭代文档管理、自动化接口闭环测试工具(自动化测试篇)

MyPostMan 是一款类似 PostMan 的接口请求软件&#xff0c;按照 项目&#xff08;微服务&#xff09;、目录来管理我们的接口&#xff0c;基于迭代来管理我们的接口文档&#xff0c;文档可以导出和通过 url 实时分享&#xff0c;按照迭代编写自动化测试用例&#xff0c;在不同环…

定制QCustomPlot 带有ListView的QCustomPlot 全网唯一份

定制QCustomPlot 带有ListView的QCustomPlot 文章目录 定制QCustomPlot 带有ListView的QCustomPlot摘要需求描述实现关键字: Qt、 QCustomPlot、 魔改、 定制、 控件 摘要 先上效果,是你想要的,再看下面的分解,顺便点赞搜藏一下;不是直接右上角。 QCustomPlot是一款…

Spring中IoC容器和Bean

目录 IoC(Inversion of Control)控制反转思想 Spring技术对IoC思想的实现 DI(Dependency Injection)依赖注入 目标 最终效果 IoC入门案例 传统方法&#xff0c;不使用IoC思想调用dao层 使用IoC思想调用dao层 第一步&#xff1a;导入Spring坐标 第二步&#xff1a;创建…

stm32:CAN通讯

目录 介绍 协议层 CAN的 帧/报文 种类 数据帧 远程帧&#xff08;遥控帧&#xff09; 错误帧 过载帧 帧间隔 总线仲裁 stm32的CAN外设 工作模式 测试模式 功能框图 时序 标准时序 例子 环回静默模式测试 寄存器代码 HAL版本 介绍 一种功能丰富的车用总线标…

【ffmpeg命令入门】重新编码媒体流、设置码率、设置帧速率

文章目录 前言ffmpeg的描述重新编码媒体流重新编码媒体流的命令ffmpeg支持的媒体流 设置视频码率视频码率是什么设置视频的码率 设置文件帧数率帧数率是什么ffmpeg设置帧数率 总结 前言 在数字媒体处理领域&#xff0c;ffmpeg是一款非常强大的工具&#xff0c;它可以用来进行媒…

自动化产线 搭配数据采集监控平台 创新与突破

自动化产线在现在的各行各业中应用广泛&#xff0c;已经是现在的生产趋势&#xff0c;不同的自动化生产设备充斥在各行各业中&#xff0c;自动化的设备会产生很多的数据&#xff0c;这些数据如何更科学化的管理&#xff0c;更优质的利用&#xff0c;就需要数据采集监控平台来完…

解决Ubuntu 20.04下外接显示屏无信号问题【多次尝试无坑完整版!!!】

解决Ubuntu 20.04下外接显示屏无信号问题【多次尝试无坑完整版&#xff01;&#xff01;&#xff01;】 一、引言 作为一名开发者&#xff0c;我经常在Windows和Ubuntu之间切换&#xff0c;以满足不同的开发需求。最近&#xff0c;我在使用惠普暗影精灵9&#xff08;搭载RTX 4…

HLS加密技术:保障流媒体内容安全的利器

随着网络视频内容的爆炸性增长&#xff0c;如何有效保护视频内容的版权和安全成为了一个亟待解决的问题。HLS&#xff08;HTTP Live Streaming&#xff09;加密技术作为一种先进的流媒体加密手段&#xff0c;凭借其高效性和安全性&#xff0c;在直播、点播等场景中得到了广泛应…

隐性行为克隆——机器人的复杂行为模仿学习的新表述

介绍 论文地址&#xff1a;https://arxiv.org/pdf/2109.00137.pdf 源码地址&#xff1a;https://github.com/opendilab/DI-engine.git 近年来&#xff0c;人们对机器人学习进行了大量研究&#xff0c;并取得了许多成果。其中&#xff0c;模仿学习法尤其受到关注。这是一种从人…

JavaEE初阶 - IO、存储、硬盘、文件系统相关常识 (二)

&#x1f387;&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳&#xff0c;欢迎大佬指点&#xff01; 人生格言: 当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友…

appium2.0 执行脚本遇到的问题

遇到的问题&#xff1a; appium 上的日志信息&#xff1a; 配置信息 方法一 之前用1.0的时候 地址默认加的 /wd/hub 在appium2.0上&#xff0c; 服务器默认路径是 / 如果要用/wd/hub 需要通过启动服务时设置基本路径 appium --base-path/wd/hub 这样就能正常执行了 方法二…