阅读量:0
效果视频:
卡片组件
样式还得细调~,时间有限,主要记录一下逻辑。
html结构:
目录
父组件
数据处理
在父页面进行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>
点击三圆点在对应位置显示查看弹框
获取目标元素的位置信息:
getBoundingClientRect()
是一个 DOM API 方法,返回一个 DOMRect 对象,提供了目标元素的大小和其相对于视口的位置信息。计算鼠标相对于目标元素的偏移量:
event.clientX
和event.clientY
分别是鼠标指针相对于整个文档的坐标位置。dotboxRect.left
和dotboxRect.top
分别是目标元素的左上角相对于视口的坐标位置。设置popup弹框元素的位置信息:
显示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>
- 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>