1、CSS 提示工具(Tooltip),2、box-sizing: border-box;的作用,3、实例2 - 图像的透明度 - 悬停效果,4、CSS 图像拼合技术,

本文介绍如何使用HTML与CSS创建提示工具(Tooltip),包括基本提示框的制作、定位技巧以及添加箭头的方法。此外,还介绍了图像拼合技术,通过单个图像实现导航列表的创建,并加入悬停效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CSS 提示工具(Tooltip)

本文我们为大家介绍如何使用 HTML 与 CSS 来创建提示工具。

提示工具在鼠标移动到指定元素后触发,先看以下四个实例:

基础提示框(Tooltip)

提示框在鼠标移动到指定元素上显示:

HTML 代码:

<style>

/* Tooltip 容器 */ .tooltip { position: relative; display: inline-block; border-bottom: 1px dotted black; /* 悬停元素上显示点线 */ } /* Tooltip 文本 */ .tooltip .tooltiptext { visibility: hidden; width: 120px; background-color: black; color: #fff; text-align: center; padding: 5px 0; border-radius: 6px; /* 定位 */ position: absolute; z-index: 1; } /* 鼠标移动上去后显示提示框 */ .tooltip:hover .tooltiptext { visibility: visible; }

</style> <div class="tooltip">鼠标移动到这 <span class="tooltiptext">提示文本</span> </div>


尝试一下 »

实例解析

HTML) 使用容器元素 (like <div>) 并添加 "tooltip" 类。在鼠标移动到 <div> 上时显示提示信息。

提示文本放在内联元素上(如 <span>) 并使用class="tooltiptext"

CSS)tooltip 类使用 position:relative, 提示文本需要设置定位值 position:absolute。 注意: 接下来的实例会显示更多的定位效果。

tooltiptext 类用于实际的提示文本。模式是隐藏的,在鼠标移动到元素显示 。设置了一些宽度、背景色、字体色等样式。

CSS3 border-radius 属性用于为提示框添加圆角。

:hover 选择器用于在鼠标移动到到指定元素 <div> 上时显示的提示。


定位提示工具

以下实例中,提示工具显示在指定元素的右侧(left:105%) 。

注意 top:-5px 同于定位在容器元素的中间。使用数字 5 因为提示文本的顶部和底部的内边距(padding)是 5px。

如果你修改 padding 的值,top 值也要对应修改,这样才可以确保它是居中对齐的。

在提示框显示在左边的情况也是这个原理。

显示在右侧:

.tooltip .tooltiptext { top: -5px; left: 105%; }


尝试一下 »

 

显示在左侧:

.tooltip .tooltiptext { top: -5px; right: 105%; }


尝试一下 »

如果你想要提示工具显示在头部和底部。我们需要使用 margin-left 属性,并设置为 -60px。 这个数字计算来源是使用宽度的一半来居中对齐,即: width/2 (120/2 = 60)。

显示在头部:

.tooltip .tooltiptext { width: 120px; bottom: 100%; left: 50%; margin-left: -60px; /* 使用一半宽度 (120/2 = 60) 来居中提示工具 */ }


尝试一下 »

 

显示在底部:

.tooltip .tooltiptext { width: 120px; top: 100%; left: 50%; margin-left: -60px; /* 使用一半宽度 (120/2 = 60) 来居中提示工具 */ }


尝试一下 »

添加箭头

我们可以用CSS 伪元素 ::after 及 content 属性为提示工具创建一个小箭头标志,箭头是由边框组成的,但组合起来后提示工具像个语音信息框。

以下实例演示了如何为显示在顶部的提示工具添加底部箭头:

顶部提示框/底部箭头:

.tooltip .tooltiptext::after { content: " "; position: absolute; top: 100%; /* 提示工具底部 */ left: 50%; margin-left: -5px; border-width: 5px; border-style: solid; border-color: black transparent transparent transparent; }

实例解析

在提示工具内定位箭头: top: 100% , 箭头将显示在提示工具的底部。left: 50% 用于居中对齐箭头。

注意:border-width 属性指定了箭头的大小。如果你修改它,也要修改 margin-left 值。这样箭头在能居中显示。

border-color 用于将内容转换为箭头。设置顶部边框为黑色,其他是透明的。如果设置了其他的也是黑色则会显示为一个黑色的四边形。

以下实例演示了如何在提示工具的头部添加箭头,注意设置边框颜色:

底部提示框/顶部箭头:

.tooltip .tooltiptext::after { content: " "; position: absolute; bottom: 100%; /* 提示工具头部 */ left: 50%; margin-left: -5px; border-width: 5px; border-style: solid; border-color: transparent transparent black transparent;

以下两个实例是左右两边的箭头实例:

右侧提示框/左侧箭头:

.tooltip .tooltiptext::after { content: " "; position: absolute; top: 50%; right: 100%; /* 提示工具左侧 */ margin-top: -5px; border-width: 5px; border-style: solid; border-color: transparent black transparent transparent; }


尝试一下 »

 

左侧提示框/右侧箭头:

.tooltip .tooltiptext::after { content: " "; position: absolute; top: 50%; left: 100%; /* 提示工具右侧 */ margin-top: -5px; border-width: 5px; border-style: solid; border-color: transparent transparent transparent black; }

 

 

box-sizing: border-box;的作用

2018年01月31日 13:40:05 阅读数:5123

box-sizing 属性可以被用来调整这些表现:

content-box  是默认值。如果你设置一个元素的宽为100px,那么这个元素的内容区会有100px宽,并且任何边框和内边距的宽度都会被增加到最后绘制出来的元素宽度中。

border-box 告诉浏览器去理解你设置的边框和内边距的值是包含在width内的。也就是说,如果你将一个元素的width设为100px,那么这100px会包含其它的border和padding,内容区的实际宽度会是width减去border + padding的计算值。大多数情况下这使得我们更容易的去设定一个元素的宽高。

 

 

border-box  width 和 height 属性包括内容,内边距和边框,但不包括外边距。这是当文档处于 Quirks模式 时Internet Explorer使用的盒模型。注意,填充和边框将在盒子内 , 例如, .box {width: 350px; border: 10px solid black;} 导致在浏览器中呈现的宽度为350px的盒子。内容框不能为负,并且被分配到0,使得不可能使用border-box使元素消失。 
这里的维度计算为: width = border + padding + 内容的  width, height = border + padding + 内容的 height。

 

实例2 - 图像的透明度 - 悬停效果

将鼠标移到图像上:

klematisklematis

CSS样式:

img { opacity:0.4; filter:alpha(opacity=40); /* IE8 及其更早版本 */ } img:hover { opacity:1.0; filter:alpha(opacity=100); /* IE8 及其更早版本 */ }

CSS 图像拼合技术


图像拼合

图像拼合就是单个图像的集合。

有许多图像的网页可能需要很长的时间来加载和生成多个服务器的请求。

使用图像拼合会降低服务器的请求数量,并节省带宽

图像拼合 - 简单实例

与其使用三个独立的图像,不如我们使用这种单个图像("img_navsprites.gif"):

navigation images

有了CSS,我们可以只显示我们需要的图像的一部分。

在下面的例子CSS指定显示 "img_navsprites.gif" 的图像的一部分:

实例

img.home
{
width:46px;
height:44px;
background:url(img_navsprites.gif) 0 0;
}

实例解析:

  • <img class="home" src="img_trans.gif" /> -因为不能为空,src属性只定义了一个小的透明图像。显示的图像将是我们在CSS中指定的背景图像
  • 宽度:46px;高度:44px; - 定义我们使用的那部分图像
  • background:url(img_navsprites.gif) 0 0; - 定义背景图像和它的位置(left:0px,top:    0px)

这是使用图像拼合最简单的方法,现在我们使用链接和悬停效果。

图像拼合 - 创建一个导航列表

我们想使用拼合图像 ("img_navsprites.gif"),以创建一个导航列表。

我们将使用一个HTML列表,因为它可以链接,同时还支持背景图像:

图像拼合 - 创建一个导航列表

我们想使用拼合图像 ("img_navsprites.gif"),以创建一个导航列表。

我们将使用一个HTML列表,因为它可以链接,同时还支持背景图像:

实例

#navlist{position:relative;}
#navlist li{margin:0;padding:0;list-style:none;position:absolute;top:0;}
#navlist li, #navlist a{height:44px;display:block;}

#home{left:0px;width:46px;}
#home{background:url('img_navsprites.gif') 0 0;}

#prev{left:63px;width:43px;}
#prev{background:url('img_navsprites.gif') -47px 0;}

#next{left:129px;width:43px;}
#next{background:url('img_navsprites.gif') -91px 0;}


尝试一下 »

实例解析:

  • #navlist{position:relative;} - 位置设置相对定位,让里面的绝对定位
  • #navlist li{margin:0;padding:0;list-style:none;position:absolute;top:0;} - margin和padding设置为0,列表样式被删除,所有列表项是绝对定位
  • #navlist li, #navlist a{height:44px;display:block;} - 所有图像的高度是44px

现在开始每个具体部分的定位和样式:

  • #home{left:0px;width:46px;} - 定位到最左边的方式,以及图像的宽度是46px
  • #home{background:url(img_navsprites.gif) 0 0;} - 定义背景图像和它的位置(左0px,顶部0px)
  • #prev{left:63px;width:43px;} - 右侧定位63px(#home宽46px+项目之间的一些多余的空间),宽度为43px。
  • #prev{background:url('img_navsprites.gif') -47px 0;} - 定义背景图像右侧47px(#home宽46px+分隔线的1px)
  • #next{left:129px;width:43px;}- 右边定位129px(#prev 63px + #prev宽是43px + 剩余的空间), 宽度是43px.
  • #next{background:url('img_navsprites.gif') no-repeat -91px 0;} - 定义背景图像右边91px(#home 46px+1px的分割线+#prev宽43px+1px的分隔线)

图像拼合s - 悬停效果

现在,我们希望我们的导航列表中添加一个悬停效果。

lamp:hover 选择器用于鼠标悬停在元素上的显示的效果

提示: :hover 选择器可以运用于所有元素。

我们的新图像 ("img_navsprites_hover.gif") 包含三个导航图像和三幅图像:

navigation images

因为这是一个单一的图像,而不是6个单独的图像文件,当用户停留在图像上不会有延迟加载。

我们添加悬停效果只添加三行代码:

实例

#home a:hover{background: url('img_navsprites_hover.gif') 0 -45px;}
#prev a:hover{background: url('img_navsprites_hover.gif') -47px -45px;}
#next a:hover{background: url('img_navsprites_hover.gif') -91px -45px;}


尝试一下 »

实例解析:

  • 由于该列表项包含一个链接,我们可以使用:hover伪类
  • #home a:hover{background: transparent url(img_navsprites_hover.gif) 0 -45px;} - 对于所有三个悬停图像,我们指定相同的背景位置,只是每个再向下45px

 

<template> <div class="navbar"> <!-- 实习管理按钮 --> <el-button class="practice-management" @click="handlePracticeClick" :plain="false" :round="false" :disabled="false" > 实习管理 </el-button> <!-- 汉堡菜单按钮 --> <hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> <!-- 面包屑导航 --> <breadcrumb v-if="!settingsStore.topNav" id="breadcrumb-container" class="breadcrumb-container" /> <!-- 顶部导航 --> <top-nav v-if="settingsStore.topNav" id="topmenu-container" class="topmenu-container" /> <div class="right-menu"> <template v-if="appStore.device !== &#39;mobile&#39;"> <!-- 头部搜索 --> <header-search id="header-search" class="right-menu-item" /> <!-- 源码地址 --> <el-tooltip content="源码地址" effect="dark" placement="bottom"> <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" /> </el-tooltip> <!-- 文档地址 --> <el-tooltip content="文档地址" effect="dark" placement="bottom"> <ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" /> </el-tooltip> <!-- 全屏按钮 --> <screenfull id="screenfull" class="right-menu-item hover-effect" /> <!-- 主题模式 --> <el-tooltip content="主题模式" effect="dark" placement="bottom"> <div class="right-menu-item hover-effect theme-switch-wrapper" @click="toggleTheme"> <svg-icon v-if="settingsStore.isDark" icon-class="sunny" /> <svg-icon v-if="!settingsStore.isDark" icon-class="moon" /> </div> </el-tooltip> <!-- 布局大小 --> <el-tooltip content="布局大小" effect="dark" placement="bottom"> <size-select id="size-select" class="right-menu-item hover-effect" /> </el-tooltip> <!-- 导航菜单(学生、消息通知) --> <div class="main-menu"> <!-- 学生下拉菜单 --> <el-dropdown class="main-menu-item" trigger="hover" placement="bottom" > <span class="menu-text">学生</span> <template #dropdown> <el-dropdown-menu> <el-dropdown-item>学生</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> <!-- 消息通知下拉菜单 --> <el-dropdown class="main-menu-item" trigger="hover" placement="bottom" > <span class="menu-text">消息通知</span> <template #dropdown> <el-dropdown-menu class="message-dropdown"> <div class="message-content"> <p>欢迎进入工学云:</p> <p>1. 如果您是学校的师生,请在"我的"-"身份认证"中完成身份认证。</p> <p>2. 如果您是企业(HR),请使用微信小程序"蘑菇丁招聘"或者在电脑浏览器访问网址: <a href="https://zhaopin.moguding.net" target="_blank" class="external-link"> https://zhaopin.moguding.net </a> 上传企业资料,平台审核通过后可发布职位。 </p> </div> </el-dropdown-menu> </template> </el-dropdown> </div> </template> <!-- 个人信息下拉菜单(头像 + 昵称) --> <el-dropdown @command="handleCommand" class="avatar-container right-menu-item hover-effect" trigger="hover"> <div class="avatar-wrapper"> <img :src="userStore.avatar" class="user-avatar" /> <span class="user-nickname"> {{ userStore.nickName }} </span> </div> <template #dropdown> <el-dropdown-menu> <router-link to="/user/profile"> <el-dropdown-item command="profile">个人中心</el-dropdown-item> </router-link> <el-dropdown-item command="changePwd">修改密码</el-dropdown-item> <el-dropdown-item command="cancelAccount" divided>注销账号</el-dropdown-item> <el-dropdown-item command="logout" divided>退出登录</el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> <!-- 设置按钮 --> <div class="right-menu-item hover-effect setting" @click="setLayout" v-if="settingsStore.showSettings"> <svg-icon icon-class="more-up" /> </div> </div> </div> </template> <script setup> import { ElMessageBox } from &#39;element-plus&#39; import { useRouter } from &#39;vue-router&#39; // 组件引入 import Breadcrumb from &#39;@/components/Breadcrumb&#39; import TopNav from &#39;@/components/TopNav&#39; import Hamburger from &#39;@/components/Hamburger&#39; import Screenfull from &#39;@/components/Screenfull&#39; import SizeSelect from &#39;@/components/SizeSelect&#39; import HeaderSearch from &#39;@/components/HeaderSearch&#39; import RuoYiGit from &#39;@/components/RuoYi/Git&#39; import RuoYiDoc from &#39;@/components/RuoYi/Doc&#39; import SvgIcon from &#39;@/components/SvgIcon&#39; // 状态管理引入 import useAppStore from &#39;@/store/modules/app&#39; import useUserStore from &#39;@/store/modules/user&#39; import useSettingsStore from &#39;@/store/modules/settings&#39; // 状态实例 const appStore = useAppStore() const userStore = useUserStore() const settingsStore = useSettingsStore() const router = useRouter() // 侧边栏切换 function toggleSideBar() { appStore.toggleSideBar() } // 实习管理按钮点击事件 function handlePracticeClick() { console.log(&#39;点击了实习管理&#39;); } // 处理下拉菜单命令 function handleCommand(command) { switch (command) { case "profile": router.push(&#39;/user/profile&#39;) break case "changePwd": router.push(&#39;/user/change-pwd&#39;) break case "cancelAccount": cancelAccount() break case "logout": logout() break default: break } } // 注销账号确认 function cancelAccount() { ElMessageBox.confirm(&#39;确定要注销账号吗?此操作不可恢复!&#39;, &#39;警告&#39;, { confirmButtonText: &#39;确定&#39;, cancelButtonText: &#39;取消&#39;, type: &#39;error&#39; }).then(() => { if (userStore.cancelAccount) { userStore.cancelAccount().then(() => { location.href = &#39;/index&#39; }) } else { location.href = &#39;/index&#39; } }).catch(() => { }) } // 退出登录 function logout() { ElMessageBox.confirm(&#39;确定注销并退出系统吗?&#39;, &#39;提示&#39;, { confirmButtonText: &#39;确定&#39;, cancelButtonText: &#39;取消&#39;, type: &#39;warning&#39; }).then(() => { userStore.logOut().then(() => { location.href = &#39;/index&#39; }) }).catch(() => { }) } // emits声明 const emits = defineEmits([&#39;setLayout&#39;]) function setLayout() { emits(&#39;setLayout&#39;) } // 主题切换 function toggleTheme() { settingsStore.toggleTheme() } </script> <style lang=&#39;scss&#39; scoped> .navbar { height: 50px; overflow: hidden; position: relative; background: #000; box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); display: flex; align-items: center; /* 解决flex布局下子元素溢出问题 */ width: 100%; box-sizing: border-box; // 实习管理按钮样式(强化穿透优先级) :deep(.practice-management.el-button) { /* 基础布局 - 确保占满导航栏高度 */ height: 50px !important; /* 直接指定高度,避免100%可能的继承问题 */ min-width: 100px; margin: 0 !important; padding: 0 20px !important; order: -1; /* 强制居左 */ flex-shrink: 0; /* 避免被压缩 */ /* 样式覆盖 - 完全清除Element默认样式 */ border: none !important; border-radius: 0 !important; background-color: #409EFF !important; /* 主蓝色 */ color: #fff !important; font-size: 14px !important; font-weight: 500 !important; text-align: center !important; box-shadow: none !important; /* 清除默认阴影 */ box-sizing: border-box !important; /* 内容居中 - 解决文字偏移问题 */ display: inline-flex !important; align-items: center !important; justify-content: center !important; line-height: 1 !important; /* 重置行高,避免文字垂直偏移 */ /* 交互状态强化 */ &:hover { background-color: #66b1ff !important; /* 亮蓝色hover */ color: #fff !important; border-color: transparent !important; box-shadow: none !important; } &:active { background-color: #3a8ee6 !important; /* 深蓝光晕active */ color: #fff !important; border-color: transparent !important; } &:focus { outline: none !important; box-shadow: none !important; } /* 禁用状态(如果需要) */ &.is-disabled { background-color: #a0cfff !important; color: #fff !important; } } .hamburger-container { line-height: 46px; height: 100%; padding: 0 15px; cursor: pointer; transition: background 0.3s; -webkit-tap-highlight-color: transparent; &:hover { background: rgba(255, 255, 255, 0.1); } } .breadcrumb-container { padding: 0 15px; /* 避免面包屑挤压按钮 */ flex-shrink: 1; overflow: hidden; } .topmenu-container { flex: 1; padding-left: 15px; } .right-menu { margin-left: auto; height: 100%; line-height: 50px; display: flex; align-items: center; flex-shrink: 0; &:focus { outline: none; } .right-menu-item { display: inline-block; padding: 0 8px; height: 100%; font-size: 18px; color: #fff; vertical-align: text-bottom; &.hover-effect { cursor: pointer; transition: background 0.3s; &:hover { background: rgba(255, 255, 255, 0.1); } } &.theme-switch-wrapper { display: flex; align-items: center; svg { transition: transform 0.3s; &:hover { transform: scale(1.15); } } } } .main-menu { display: flex; align-items: center; height: 100%; margin: 0 8px; .main-menu-item { margin: 0 5px; padding: 0 8px; height: 100%; display: flex; align-items: center; cursor: pointer; color: #fff; font-size: 14px; &:hover { background: rgba(255, 255, 255, 0.1); } } } .message-dropdown { .message-content { padding: 15px; min-width: 350px; line-height: 1.6; font-size: 14px; p { margin: 0 0 10px 0; color: #333; } .external-link { color: #409EFF; text-decoration: underline; } } } .avatar-container { margin-right: 0px; padding-right: 0px; .avatar-wrapper { margin-top: 10px; right: 5px; position: relative; display: flex; align-items: center; padding: 0 10px; .user-avatar { cursor: pointer; width: 30px; height: 30px; border-radius: 50%; } .user-nickname{ margin-left: 8px; font-size: 14px; color: #fff; white-space: nowrap; } } } } } </style> 要怎么修改才能把实习管理弄成button的样式,并且是蓝色的,不需要跳转
07-25
<template> <div class="taskDetail" v-show="visible"> <editAndAddPage :router="&#39;/basicConfiguration&#39;" eyeType :title="titles" @cancel="cancel" @submit="submit"> <template v-slot:topTitle> <span class="top_title base-flex-align"> <span class="icon_info"></span> <span class="title_info">{{ jobName }}</span> <span class="status_info base-flex-align" :class="{ green_color: taskStatus == 1, blue_color: taskStatus == 2, red_color: taskStatus == 3 }"> {{ taskStatusName[taskStatus].title }} </span> </span> </template> <template v-slot:pageBody> <div class="mian-content"> <div class="table-box"> <div class="title-info"><i></i>{{ $t(&#39;下发列表&#39;) }}</div> <a-table :columns="columns" :data-source="tableData" row-key="id" :scroll="{ x: &#39;100%&#39;, y: 220 }" :pagination="false"> <template slot="operation" slot-scope="text, record"> <span :class="taskStatusName[record.detectorSendStatus].icon || &#39;&#39;" /> <a-tooltip placement="top"> <template slot="title"> <span>{{ record.weekInfo }}</span> </template> {{ taskStatusName[record.detectorSendStatus].title || "--" }} </a-tooltip> <hd-icon type="files" /> </template> </a-table> </div> <div class="pagination_box" v-show="tableData.length"> <my-pagination style="padding: 0 20px" :pageIndex.sync="pagination.currentPage" @changePage="changePage" :pageSize="pagination.pageSize" :total="pagination.totalRows" @onShowSizeChange="onShowSizeChange"></my-pagination> </div> <div class="config-box"> <div class="title-info"><i></i>{{ $t(&#39;空开配置&#39;) }}</div> <div class="config-list"> <div class="config-item" v-for="(item, index) in configDetail" :key="index"> <div class="config-title">{{ item.configTypeName }}</div> <div class="config-content"> <div> <span>{{ $t(&#39;配置内容:&#39;) }}</span> <span class="icon-item" :class="{ icon_gray: item.cmd == 0 }"></span> <span>{{ item.cmd == 0 ? $t(&#39;分闸&#39;) : $t(&#39;合闸&#39;) }}</span> </div> <div> <span>{{ $t(&#39;时间范围:&#39;) }}</span> <span>{{ item.executeTime }}</span> </div> <div class="week-item"> <span>{{ $t(&#39;运行周期:&#39;) }}</span> <span v-for="(el, indx) in item.weekList" :key="indx">{{ el }} </span> </div> </div> </div> </div> </div> </div> </template> </editAndAddPage> </div> </template> <script> import factory from &#39;../factory&#39; import myPagination from &#39;@/components/scfComponents/paginationFormwork/myPagination.vue&#39; import editAndAddPage from &#39;@/components/scfComponents/editAndAddPage/editAndAddPage.vue&#39; export default { components: { editAndAddPage, myPagination }, data() { return { titles: &#39;空开定时任务详情&#39;, visible: false, columns: [ { title: &#39;设备名称&#39;, dataIndex: &#39;deviceName&#39;, key: &#39;deviceName&#39;, ellipsis: true, }, { title: &#39;操作结果&#39;, dataIndex: &#39;operation&#39;, key: &#39;operation&#39;, ellipsis: true, scopedSlots: { customRender: &#39;operation&#39; }, }, { title: &#39;探测器名称&#39;, dataIndex: &#39;detectorName&#39;, key: &#39;detectorName&#39;, ellipsis: true, }, { title: &#39;探测器型号&#39;, dataIndex: &#39;productModel&#39;, key: &#39;productModel&#39;, ellipsis: true, }, { title: &#39;探测器编号&#39;, dataIndex: &#39;detectorUniqueCode&#39;, key: &#39;detectorUniqueCode&#39;, ellipsis: true, }, { title: &#39;所属组织&#39;, dataIndex: &#39;detectorOwnerName&#39;, key: &#39;detectorOwnerName&#39;, ellipsis: true, }, ], tableData: [], jobName: &#39;&#39;, jobId: &#39;&#39;, taskStatus: &#39;&#39;, // 任务状态 taskStatusName: { 1: { title: this.$t(&#39;下发成功&#39;), icon: &#39;hd-status-success&#39; }, 2: { title: this.$t(&#39;下发中&#39;), icon: &#39;hd-status-running&#39; }, 3: { title: this.$t(&#39;下发失败&#39;), icon: &#39;hd-status-error&#39; }, 4: { title: this.$t(&#39;已清除&#39;), icon: &#39;hd-status-error&#39; }, }, configDetail: [], // 分页 pagination: { pageSize: 10, currentPage: 1, totalRows: 0, }, } }, methods: { // 打开弹窗 showModal(record) { this.visible = true this.jobName = record.jobName this.taskStatus = record.status this.jobId = record.jobId this.getDetailList() this.getDetectorJob() }, // 获取详情列表 getDetailList() { let params = { jobId: this.jobId, page: this.pagination.currentPage, pageSize: this.pagination.pageSize, } factory.getBatchjobDetailList(params).then(res => { if (res.success) { this.tableData = (res.data.pageData || []).map(item => ({ ...item, weekInfo: (item.configList || []) .filter(Boolean) .map(el => el.configTypeName || &#39;&#39;) .join(&#39;,&#39;), })) console.log(this.tableData, &#39;this.tableData&#39;) this.pagination.totalRows = res.data.totalCount || 0 } }) }, // 查询已存在空开定时任务 getDetectorJob() { factory.getDetectorJob(this.jobId).then(res => { if (res.success) { this.configDetail = (res.data || []).map(item => { item.weekList = this.getActiveDays(item.rept) return item }) || [] } }) }, getActiveDays(rept) { const daysStr = rept.slice(0, 7).padEnd(7, &#39;0&#39;) const activeDays = [] const weekdays = [&#39;周日&#39;, &#39;周一&#39;, &#39;周二&#39;, &#39;周三&#39;, &#39;周四&#39;, &#39;周五&#39;, &#39;周六&#39;] for (let i = 0; i < daysStr.length; i++) { if (daysStr[i] == &#39;1&#39;) { activeDays.push(weekdays[i]) } } return activeDays }, // 失败重发 resendBatchjob() { factory.resendBatchjob(this.jobId).then(res => { if (res.success) { } }) }, // 取消 cancel() { this.visible = false }, }, } </script> <style lang="less" scoped> .taskDetail { .top_title { .icon_info { width: 1px; height: 16px; margin: 0 12px; display: inline-block; background: rgb(105, 118, 136); } .title_info { color: rgb(66, 75, 86); font-family: &#39;微软雅黑&#39;; font-size: 18px; font-weight: 400; } .status_info { margin-left: 12px; color: rgb(255, 255, 255); font-family: &#39;阿里巴巴普惠体&#39;; font-size: 14px; height: 24px; font-weight: 400; padding: 1px 12px; border-radius: 3px; &.green_color { background: rgb(39, 208, 90); } &.blue_color { background: rgb(41, 141, 255); } &.red_color { background: rgb(233, 70, 58); } } } .mian-content { width: 100%; height: 100%; position: relative; box-sizing: border-box; text-align: left; background-color: #eceef1; .title-info { display: flex; align-items: center; color: rgb(21, 23, 26); font-family: &#39;阿里巴巴普惠体&#39;; font-size: 16px; font-weight: 700; margin-bottom: 16px; i { width: 4px; height: 16px; display: inline-block; background: rgb(41, 141, 255); margin-right: 12px; } } .table-box, .config-box { width: 100%; padding: 20px; border-radius: 8px; box-sizing: border-box; background: rgb(255, 255, 255); } .config-box { margin-top: 16px; } .pagination_box { width: 100%; height: 64px; position: relative; background: #ffffff; } .config-list { .config-item { width: 33%; border: 1px solid #f2f3f4; .config-title { height: 46px; color: rgb(66, 75, 86); font-family: &#39;阿里巴巴普惠体&#39;; font-size: 14px; font-weight: 400; padding: 12px 16px 12px 16px; background: rgb(242, 243, 244); border-bottom: 1px solid rgb(229, 231, 234); } .config-content { height: 114px; padding: 16px; display: flex; flex-direction: column; justify-content: space-around; align-items: flex-start; > div > span:first-child { margin-right: 8px; } .icon-item { width: 8px; height: 8px; border-radius: 50%; display: inline-block; background: rgb(39, 208, 90); } .icon_gray { background: rgb(157, 166, 177); } } } } } } </style> 代码评审
最新发布
08-06
<template> <div id="cesiumContainer"></div> <div ref="miniMapContainer" class="mini-map" @click="handleMiniMapClick"> <div class="location-indicator" :style="indicatorStyle"></div> <!-- 四个角折角 --> <div class="corner corner-top-left"></div> <div class="corner corner-top-right"></div> <div class="corner corner-bottom-left"></div> <div class="corner corner-bottom-right"></div> <!-- <div class="camera-icon">📷</div> --> <div class="focus-effect"></div> <div class="pulse"></div> </div> <Search @location-selected="handleLocationSelected" /> <LocationBar v-if="loaded" :update-interval="100" :use-dms-format="useDmsFormat" /> </template> <style> /* @import "/temp/css/divGraphic.css"; */ </style> <script setup lang="ts"> import { computed, onUnmounted, onMounted, reactive } from "vue"; import LocationBar from "./location-bar.vue"; import Search from "./search.vue"; import initMap from "./init"; import { ref } from "vue"; import { throttle } from &#39;lodash&#39;; import { loadRipplePoints, createMultipleRippleCircles } from &#39;./circle.js&#39;; import { $prototype } from "../../main.ts"; const miniMapContainer = ref<HTMLElement>(); let viewIndicator: Rectangle; // 视图指示器样式 const currentPosition = reactive({ longitude: 113.361538, latitude: 27.339318, }); // 株洲市范围常量 const ZHUZHOU_EXTENT = { west: 112.5, // 株洲市西边界 east: 114.5, // 株洲市东边界 south: 26.0, // 株洲市南边界 north: 28.0 // 株洲市北边界 }; const rippleEntities = ref<any[]>([]); // 存储波纹圆实体 const heightThreshold = 80000; // 高度阈值(米),高于此值隐藏波纹圆 // 修复1:重新定义 indicatorStyle 为 ref const indicatorStyle = ref({ left: &#39;50%&#39;, top: &#39;50%&#39;, display: "block" }); const updateRippleVisibility = throttle(() => { if (!$prototype.$map || rippleEntities.value.length === 0) return; // 修复1:将 shouldShow 提取到块级作用域外部 let shouldShow = false; // 获取相机高度 const cartographic = $prototype.$map.camera.positionCartographic; // 修复2:添加空值检查 if (cartographic) { const cameraHeight = cartographic.height; shouldShow = cameraHeight > heightThreshold; } // 修复3:确保 shouldShow 已定义 rippleEntities.value.forEach(entity => { entity.show = shouldShow; }); }, 200); // 更新指示器位置 const updateIndicatorPosition = () => { if (!$prototype.$map) return; const camera = $prototype.$map.camera; const rect = camera.computeViewRectangle(); if (!rect) return; // 计算在株洲市范围内的相对位置 const center = Cesium.Rectangle.center(rect); const lon = Cesium.Math.toDegrees(center.longitude); const lat = Cesium.Math.toDegrees(center.latitude); // 确保位置在株洲市范围内 const constrainedLon = Math.max(ZHUZHOU_EXTENT.west, Math.min(ZHUZHOU_EXTENT.east, lon)); const constrainedLat = Math.max(ZHUZHOU_EXTENT.south, Math.min(ZHUZHOU_EXTENT.north, lat)); const lonPercent = ((constrainedLon - ZHUZHOU_EXTENT.west) / (ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west)) * 100; const latPercent = 100 - ((constrainedLat - ZHUZHOU_EXTENT.south) / (ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south)) * 100; // 更新CSS指示器 indicatorStyle.value = { left: `${lonPercent}%`, top: `${latPercent}%`, display: "block" }; }; // 更新鹰眼地图 - 只更新指示器位置 const updateOverview = () => { if (!$prototype.$map || !overviewViewer.value) return; // 获取主地图的当前视图中心 const rectangle = $prototype.$map.camera.computeViewRectangle(); if (!rectangle) return; // 更新指示器位置 updateIndicatorPosition(); }; const overviewViewer = ref(null); // 初始化鹰眼地图 - 使用全球底图+株洲市叠加层 const initMiniMap = () => { Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxMDhlNDdmYy03NzFhLTQ1ZTQtOWQ3NS1lZDAzNDc3YjE4NDYiLCJpZCI6MzAxNzQyLCJpYXQiOjE3NDcwNTMyMDN9.eaez8rQxVbPv2LKEU0sMDclPWyHKhh1tR27Vg-_rQSM"; if (!miniMapContainer.value) return; // 创建全球底图提供器 const worldProvider = new Cesium.UrlTemplateImageryProvider({ url: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", minimumLevel: 0, maximumLevel: 19, tilingScheme: new Cesium.WebMercatorTilingScheme(), }); // 创建株洲市专用影像提供器 // const zhuzhouProvider = new Cesium.UrlTemplateImageryProvider({ // url: "http://124.232.190.30:9000/proxy/pk1725866655224/map/zzzsyx_18/{z}/{x}/{y}.png", // minimumLevel: 1, // maximumLevel: 17, // tilingScheme: new Cesium.WebMercatorTilingScheme(), // }); // 鹰眼地图初始化 - 使用全球底图 overviewViewer.value = new Cesium.Viewer(miniMapContainer.value, { sceneMode: Cesium.SceneMode.SCENE2D, baseLayerPicker: false, homeButton: false, timeline: false, navigationHelpButton: false, animation: false, scene3DOnly: true, selectionIndicator: false, infoBox: false, imageryProvider: worldProvider, // 使用全球底图 terrainProvider: undefined, mapProjection: new Cesium.WebMercatorProjection(), skyBox: false, skyAtmosphere: false }); // 添加株洲市影像作为叠加层 // const zhuzhouLayer = overviewViewer.value.imageryLayers.addImageryProvider(zhuzhouProvider); // 在鹰眼地图添加株洲市白色边界 addMiniMapBoundary(); // 设置固定视野为株洲市范围 overviewViewer.value.camera.setView({ destination: Cesium.Rectangle.fromDegrees( ZHUZHOU_EXTENT.west, ZHUZHOU_EXTENT.south, ZHUZHOU_EXTENT.east, ZHUZHOU_EXTENT.north ) }); // 隐藏控件 const toolbar = overviewViewer.value.container.getElementsByClassName("cesium-viewer-toolbar")[0]; if (toolbar) toolbar.style.display = "none"; // 隐藏版权信息 overviewViewer.value.cesiumWidget.creditContainer.style.display = "none"; // 初始化视图指示器 initRectangle(); }; // 在鹰眼地图添加白色边界 function addMiniMapBoundary() { if (!overviewViewer.value) return; // 加载株洲市边界GeoJSON const boundary = overviewViewer.value.dataSources.add( Cesium.GeoJsonDataSource.load("/shp_zz.geojson", { stroke: Cesium.Color.WHITE, fill: false, strokeWidth: 2, clampToGround: true }) ); boundary.then((dataSource) => { const entities = dataSource.entities.values; for (let entity of entities) { if (entity.polyline) { // 设置白色边界样式 entity.polyline.material = Cesium.Color.WHITE; entity.polyline.width = 2; entity.polyline.clampToGround = true; } } }); } // 初始化视图指示器 function initRectangle() { // 创建视图指示器(株洲市范围框) viewIndicator = overviewViewer.value.entities.add({ rectangle: { coordinates: Cesium.Rectangle.fromDegrees( ZHUZHOU_EXTENT.west, ZHUZHOU_EXTENT.south, ZHUZHOU_EXTENT.east, ZHUZHOU_EXTENT.north ), material: Cesium.Color.RED.withAlpha(0.3), outline: true, outlineColor: Cesium.Color.RED, outlineWidth: 2, }, }); } const loaded = ref(false); const useDmsFormat = ref(false); function addDemoGraphics() { const chinaBoundary = $prototype.$map.dataSources.add( Cesium.GeoJsonDataSource.load("/shp_zz.geojson", { stroke: Cesium.Color.WHITE, fill: false, clampToGround: true, describe: null, }) ); chinaBoundary.then((dataSource) => { const entities = dataSource.entities.values; for (let entity of entities) { if (entity.polyline) { entity.polyline.fill = false; } } }); } function flyToDes() { const center = Cesium.Cartesian3.fromDegrees(-98.0, 40.0); $prototype.$map.camera.flyTo({ destination: new Cesium.Cartesian3( -2432812.6687511606, 5559483.804371395, 2832009.419525571 ), orientation: { heading: 6.283185307179421, pitch: -1.0472145569408116, roll: 6.2831853071795205, }, complete: function () {}, }); } // 监听主地图相机变化 const setupCameraListener = () => { $prototype.$map.camera.changed.addEventListener(updateOverview); }; // 鹰眼地图点击处理 const handleMiniMapClick = (event: MouseEvent) => { if (!miniMapContainer.value || !overviewViewer.value) return; const rect = miniMapContainer.value.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; // 计算在固定范围内的相对位置 const xPercent = (x / rect.width) * 100; const yPercent = (y / rect.height) * 100; // 转换为株洲市范围内的经纬度 const lon = ZHUZHOU_EXTENT.west + (xPercent / 100) * (ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west); const lat = ZHUZHOU_EXTENT.north - (yPercent / 100) * (ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south); // 主地图飞向点击位置 $prototype.$map.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(lon, lat, 1000000), }); }; function addImage() { var rightImageProvider = new Cesium.UrlTemplateImageryProvider({ name: "影像图", type: "xyz", layer: "vec_d", url: "http://124.232.190.30:9000/proxy/pk1725866655224/map/zzzsyx_18/{z}/{x}/{y}.png", minimumLevel: 1, maximumLevel: 17, crs: "EPSG:3857", }); $prototype.$map.imageryLayers.addImageryProvider(rightImageProvider); rightImageProvider.splitDirection = Cesium.SplitDirection.right; } onMounted(() => { initMap(); addImage(); loaded.value = true; addDemoGraphics(); flyToDes(); // 修复6:确保正确的初始化顺序 setTimeout(() => { initMiniMap(); setupCameraListener(); // 初始更新 updateOverview(); }, 1000); (async () => { try { const ripplePoints = await loadRipplePoints(); rippleEntities.value = createMultipleRippleCircles( $prototype.$map, ripplePoints ); $prototype.$map.camera.changed.addEventListener(updateRippleVisibility); updateRippleVisibility(); } catch (error) { console.error(&#39;加载波纹圆失败:&#39;, error); } })(); }); let currentMarker: any = null; const createMarker = (location: { lng: number; lat: number; name: string }) => { if (currentMarker) { $prototype.$map.entities.remove(currentMarker); } currentMarker = $prototype.$map.entities.add({ position: Cesium.Cartesian3.fromDegrees( location.lng, location.lat, 50 ), point: { pixelSize: 200, color: Cesium.Color.RED, outlineColor: Cesium.Color.WHITE, outlineWidth: 2, heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND, }, label: { text: location.name, font: "40px sans-serif", fillColor: Cesium.Color.WHITE, outlineColor: Cesium.Color.BLACK, outlineWidth: 1, verticalOrigin: Cesium.VerticalOrigin.BOTTOM, pixelOffset: new Cesium.Cartesian2(0, -20), heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND, distanceDisplayCondition: new Cesium.DistanceDisplayCondition(0, 10000), }, description: `<div style="padding: 10px;"> <h3 style="margin: 0 0 5px 0;">${location.name}</h3> <p>经度:${location.lng.toFixed(6)}</p> <p>纬度:${location.lat.toFixed(6)}</p> </div>`, }); return currentMarker; }; const handleLocationSelected = (location: { lng: number; lat: number; name: string; }) => { if (!$prototype.$map) return; const destination = Cesium.Cartesian3.fromDegrees( location.lng, location.lat, 200 ); $prototype.$map.camera.flyTo({ destination, orientation: { heading: Cesium.Math.toRadians(0), pitch: Cesium.Math.toRadians(-45), roll: 0, }, duration: 2, complete: () => { createMarker(location); }, }); }; onUnmounted(() => { if ($prototype.$map) { $prototype.$map.destroy(); $prototype.$map = null; } if ($prototype.$map) { $prototype.$map.camera.changed.removeEventListener(updateRippleVisibility); } console.log("组件销毁"); }); const emit = defineEmits(["onload", "onclick"]); const initMars3d = async (option: any) => { emit("onclick", true); emit("onload", $prototype.$map); }; </script> <style lang="less"> /**cesium 工具按钮栏*/ .cesium-viewer-toolbar { top: auto !important; bottom: 35px !important; left: 12px !important; right: auto !important; } .cesium-toolbar-button img { height: 100%; } .cesium-viewer-toolbar > .cesium-toolbar-button, .cesium-navigationHelpButton-wrapper, .cesium-viewer-geocoderContainer { margin-bottom: 5px; float: left; clear: both; text-align: center; } .cesium-button { background-color: rgba(23, 49, 71, 0.8); color: #e6e6e6; fill: #e6e6e6; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); line-height: 32px; } .cesium-button:hover { background: #3ea6ff; } /**cesium 底图切换面板*/ .cesium-baseLayerPicker-dropDown { bottom: 0; left: 40px; max-height: 700px; margin-bottom: 5px; background-color: rgba(23, 49, 71, 0.8); } /**cesium 帮助面板*/ .cesium-navigation-help { top: auto; bottom: 0; left: 40px; transform-origin: left bottom; background: none; background-color: rgba(23, 49, 71, 0.8); .cesium-navigation-help-instructions { background: none; } .cesium-navigation-button { background: none; } .cesium-navigation-button-selected, .cesium-navigation-button-unselected:hover { background: rgba(0, 138, 255, 0.2); } } /**cesium 二维三维切换*/ .cesium-sceneModePicker-wrapper { width: auto; } .cesium-sceneModePicker-wrapper .cesium-sceneModePicker-dropDown-icon { float: right; margin: 0 3px; } /**cesium POI查询输入框*/ .cesium-viewer-geocoderContainer .search-results { left: 0; right: 40px; width: auto; z-index: 9999; } .cesium-geocoder-searchButton { background-color: rgba(23, 49, 71, 0.8); } .cesium-viewer-geocoderContainer .cesium-geocoder-input { background-color: rgba(63, 72, 84, 0.7); } .cesium-viewer-geocoderContainer .cesium-geocoder-input:focus { background-color: rgba(63, 72, 84, 0.9); } .cesium-viewer-geocoderContainer .search-results { background-color: rgba(23, 49, 71, 0.8); } /**cesium info信息框*/ .cesium-infoBox { top: 50px; background-color: rgba(23, 49, 71, 0.8); } .cesium-infoBox-title { background-color: rgba(23, 49, 71, 0.8); } /**cesium 任务栏的FPS信息*/ .cesium-performanceDisplay-defaultContainer { top: auto; bottom: 35px; right: 50px; } .cesium-performanceDisplay-ms, .cesium-performanceDisplay-fps { color: #fff; } /**cesium tileset调试信息面板*/ .cesium-viewer-cesiumInspectorContainer { top: 10px; left: 10px; right: auto; } .cesium-cesiumInspector { background-color: rgba(23, 49, 71, 0.8); } /**覆盖mars3d内部控件的颜色等样式*/ .mars3d-compass .mars3d-compass-outer { fill: rgba(23, 49, 71, 0.8); } .mars3d-contextmenu-ul, .mars3d-sub-menu { background-color: rgba(23, 49, 71, 0.8); > li > a:hover, > li > a:focus, > li > .active { background-color: #3ea6ff; } > .active > a, > .active > a:hover, > .active > a:focus { background-color: #3ea6ff; } } /* Popup样式*/ .mars3d-popup-color { color: #ffffff; } .mars3d-popup-background { background: rgba(23, 49, 71, 0.8); } .mars3d-popup-content { margin: 15px; } .mars3d-template-content label { padding-right: 6px; } .mars3d-template-titile { border-bottom: 1px solid #3ea6ff; } .mars3d-template-titile a { font-size: 16px; } .mars3d-tooltip { background: rgba(23, 49, 71, 0.8); border: 1px solid rgba(23, 49, 71, 0.8); } .mars3d-popup-btn-custom { padding: 3px 10px; border: 1px solid #209ffd; background: #209ffd1c; } .mars-dialog .mars-dialog__content { height: 100%; width: 100%; overflow: auto; padding: 5px; } .image { border: solid 2px #fff; } .content { height: 90%; padding-top: 10px; overflow-x: auto; overflow-y: auto; } .content-text { padding: 0 10px; text-indent: 30px; font-size: 17px; } .details-video { width: 100%; height: 760px; background-color: #000; } :where(.css-lt97qq9).ant-space { display: inline-flex; } :where(.css-lt97qq9).ant-space-align-center { align-items: center; } :where(.css-lt97qq9).ant-image .ant-image-mask { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: #fff; background: rgba(0, 0, 0, 0.5); cursor: pointer; opacity: 0; transition: opacity 0.3s; } :where(.css-lt97qq9).ant-image .ant-image-mask .ant-image-mask-info { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; padding: 0 4px; } :where(.css-1t97qq9)[class^="ant-image"] [class^="ant-image"], :where(.css-1t97qq9)[class*=" ant-image"] [class^="ant-image"], :where(.css-1t97qq9)[class^="ant-image"] [class*=" ant-image"], :where(.css-1t97qq9)[class*=" ant-image"] [class*=" ant-image"] { box-sizing: border-box; } :where(.css-lt97qq9).ant-image .ant-image-img { width: 100%; height: auto; vertical-align: middle; } </style> <style scoped> .mini-map-container { position: relative; width: 100%; height: 100%; } .main-viewer { width: 100%; height: 100%; } .mini-map { position: absolute; right: 3vw; bottom: 6vh; width: 12vw; height: 17vh; border: 2px solid #fff; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); z-index: 999; cursor: pointer; overflow: hidden; } .location-indicator { position: absolute; width: 14px; height: 14px; background: #ff3e3e; border: 2px solid white; border-radius: 50%; transform: translate(-50%, -50%); box-shadow: 0 0 15px rgba(255, 62, 62, 1); z-index: 100; } .view-rectangle { position: absolute; border: 2px solid #00f2fe; z-index: 99; pointer-events: none; box-shadow: 0 0 20px rgba(0, 242, 254, 0.7); } /* 相机聚焦样式 - 四个角折角 */ .corner { position: absolute; width: 25px; height: 25px; z-index: 100; pointer-events: none; } .corner::before, .corner::after { content: ""; position: absolute; background: #00f2fe; box-shadow: 0 0 10px rgba(0, 242, 254, 0.8); } .corner-top-left { top: -2px; left: -2px; } .corner-top-left::before { top: 0; left: 0; width: 15px; height: 3px; } .corner-top-left::after { top: 0; left: 0; width: 3px; height: 15px; } .corner-top-right { top: -2px; right: -2px; } .corner-top-right::before { top: 0; right: 0; width: 15px; height: 3px; } .corner-top-right::after { top: 0; right: 0; width: 3px; height: 15px; } .corner-bottom-left { bottom: -2px; left: -2px; } .corner-bottom-left::before { bottom: 0; left: 0; width: 15px; height: 3px; } .corner-bottom-left::after { bottom: 0; left: 0; width: 3px; height: 15px; } .corner-bottom-right { bottom: -2px; right: -2px; } .corner-bottom-right::before { bottom: 0; right: 0; width: 15px; height: 3px; } .corner-bottom-right::after { bottom: 0; right: 0; width: 3px; height: 15px; } .camera-icon { position: absolute; top: 10px; right: 10px; color: #00f2fe; font-size: 24px; z-index: 100; text-shadow: 0 0 10px rgba(0, 242, 254, 0.8); } .focus-effect { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 98; border: 2px solid rgba(0, 242, 254, 0.2); border-radius: 5px; box-shadow: inset 0 0 30px rgba(0, 242, 254, 0.1); } .pulse { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 10px; height: 10px; border-radius: 50%; background: rgba(0, 242, 254, 0.7); box-shadow: 0 0 0 0 rgba(0, 242, 254, 0.7); animation: pulse 2s infinite; } ::v-deep.cesium-viewer-toolbar { display: none; } </style> 以上代码,鹰眼地图中为什么还是加载不了株洲的白色边界,你检查修改一下代码,尽可能不改动其他任何与这个无关代码,减少对vue的修改
07-07
<template> <div id="cesiumContainer" class="cesiumContainer"></div> <!-- 新增:坐标系选择器(不影响原有功能) --> <div class="coordinate-selector"> <span>坐标系:</span> <el-radio-group v-model="coordinateSystem" size="small"> <el-radio-button label="gcj02">高德/火星坐标</el-radio-button> <el-radio-button label="bd09">百度坐标</el-radio-button> </el-radio-group> </div> <!-- 原有点击事件已注释,保留代码以便后续恢复 --> <!-- <div ref="miniMapContainer" class="mini-map" @click="handleMiniMapClick"> --> <div ref="miniMapContainer" class="mini-map"> <div class="location-indicator" :style="indicatorStyle"> <!-- 新增四条准星延伸线 --> <div class="ext-line ext-line-top"></div> <div class="ext-line ext-line-right"></div> <div class="ext-line ext-line-bottom"></div> <div class="ext-line ext-line-left"></div> </div> <div class="corner corner-top-left"></div> <div class="corner corner-top-right"></div> <div class="corner corner-bottom-left"></div> <div class="corner corner-bottom-right"></div> <div class="focus-effect"></div> <div class="pulse"></div> </div> <Search @location-selected="handleLocationSelected" /> <LocationBar v-if="loaded" :update-interval="100" :use-dms-format="useDmsFormat" /> </template> <script setup lang="ts"> // 原有导入 + 新增坐标转换工具类导入 import { computed, onUnmounted, onMounted, reactive, ref } from "vue"; import LocationBar from "./location-bar.vue"; import Search from "./search.vue"; import initMap from "./init"; import { throttle } from "lodash"; import { loadRipplePoints, createMultipleRippleCircles } from "./circle.js"; import { $prototype } from "../../main.ts"; import markerImage from "@/assets/images/building.png"; import { imageDark } from "naive-ui/es/image/styles"; // 新增:导入坐标转换工具类 import { CoordinateTransformer } from "./coordinate-transformer"; // 修复类型定义 type Rectangle = any; // 原有状态 + 新增坐标系选择状态 const miniMapContainer = ref<HTMLElement>(); let viewIndicator: Rectangle; const currentPosition = reactive({ longitude: 113.361538, latitude: 27.339318, }); const ZHUZHOU_EXTENT = { west: 112.5, east: 114.5, south: 26.0, north: 28.0, }; const rippleEntities = ref<any[]>([]); const heightThreshold = 80000; const indicatorStyle = ref({ left: "50%", top: "50%", display: "block", }); const loaded = ref(false); const useDmsFormat = ref(false); const overviewViewer = ref(null); let currentMarker: any = null; // 新增:坐标系选择状态(默认高德GCJ-02) const coordinateSystem = ref("gcj02"); // &#39;gcj02&#39; | &#39;bd09&#39; // 原有方法完全不变(波纹可见性更新) const updateRippleVisibility = throttle(() => { if (!$prototype.$map || rippleEntities.value.length === 0) return; let shouldShow = false; const cartographic = $prototype.$map.camera.positionCartographic; if (cartographic) { const cameraHeight = cartographic.height; shouldShow = cameraHeight > heightThreshold; } rippleEntities.value.forEach((entity) => { //entity.show = shouldShow; entity.show = false; }); }, 200); // 原有方法完全不变(指示器位置更新) const updateIndicatorPosition = () => { if (!$prototype.$map) return; const camera = $prototype.$map.camera; const rect = camera.computeViewRectangle(); if (!rect) return; const center = Cesium.Rectangle.center(rect); const lon = Cesium.Math.toDegrees(center.longitude); const lat = Cesium.Math.toDegrees(center.latitude); const constrainedLon = Math.max( ZHUZHOU_EXTENT.west, Math.min(ZHUZHOU_EXTENT.east, lon) ); const constrainedLat = Math.max( ZHUZHOU_EXTENT.south, Math.min(ZHUZHOU_EXTENT.north, lat) ); const lonPercent = ((constrainedLon - ZHUZHOU_EXTENT.west) / (ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west)) * 100; const latPercent = 100 - ((constrainedLat - ZHUZHOU_EXTENT.south) / (ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south)) * 100; indicatorStyle.value = { left: `${lonPercent}%`, top: `${latPercent}%`, display: "block", }; }; // 原有方法完全不变(鹰眼地图更新) const updateOverview = () => { if (!$prototype.$map || !overviewViewer.value) return; const rectangle = $prototype.$map.camera.computeViewRectangle(); if (!rectangle) return; updateIndicatorPosition(); // 添加视口矩形更新逻辑 if (viewIndicator) { viewIndicator.rectangle.coordinates = rectangle; } }; // 原有方法完全不变(初始化鹰眼地图) const initMiniMap = () => { Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIxMDhlNDdmYy03NzFhLTQ1ZTQtOWQ3NS1lZDAzNDc3YjE4NDYiLCJpZCI6MzAxNzQyLCJpYXQiOjE3NDcwNTMyMDN9.eaez8rQxVbPv2LKEU0sMDclPWyHKhh1tR27Vg-_rQSM"; if (!miniMapContainer.value) return; const worldProvider = new Cesium.UrlTemplateImageryProvider({ url: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", minimumLevel: 0, maximumLevel: 19, tilingScheme: new Cesium.WebMercatorTilingScheme(), }); const zhuzhouProvider = new Cesium.UrlTemplateImageryProvider({ url: "http://124.232.190.30:9000/proxy/pk1725866655224/map/zzzsyx_18/{z}/{x}/{y}.png", minimumLevel: 1, maximumLevel: 17, tilingScheme: new Cesium.WebMercatorTilingScheme(), }); overviewViewer.value = new Cesium.Viewer(miniMapContainer.value, { sceneMode: Cesium.SceneMode.SCENE2D, baseLayerPicker: false, homeButton: false, timeline: false, navigationHelpButton: false, animation: false, scene3DOnly: true, selectionIndicator: false, infoBox: false, imageryProvider: worldProvider, terrainProvider: undefined, mapProjection: new Cesium.WebMercatorProjection(), skyBox: false, skyAtmosphere: false, }); // 禁用所有相机交互控制(新增功能代码) const cameraController = overviewViewer.value.scene.screenSpaceCameraController; cameraController.enableTranslate = false; cameraController.enableZoom = false; cameraController.enableRotate = false; cameraController.enableTilt = false; cameraController.enableLook = false; const zhuzhouLayer = overviewViewer.value.imageryLayers.addImageryProvider(zhuzhouProvider); overviewViewer.value.camera.setView({ destination: Cesium.Rectangle.fromDegrees( ZHUZHOU_EXTENT.west, ZHUZHOU_EXTENT.south, ZHUZHOU_EXTENT.east, ZHUZHOU_EXTENT.north ), }); const toolbar = overviewViewer.value.container.getElementsByClassName( "cesium-viewer-toolbar" )[0]; if (toolbar) toolbar.style.display = "none"; overviewViewer.value.cesiumWidget.creditContainer.style.display = "none"; initRectangle(); }; // 原有方法完全不变(初始化视图指示器) function initRectangle() { viewIndicator = overviewViewer.value.entities.add({ rectangle: { coordinates: Cesium.Rectangle.fromDegrees( ZHUZHOU_EXTENT.west, ZHUZHOU_EXTENT.south, ZHUZHOU_EXTENT.east, ZHUZHOU_EXTENT.north ), material: Cesium.Color.RED.withAlpha(0.3), outline: true, outlineColor: Cesium.Color.RED, outlineWidth: 2, }, }); } // 原有方法完全不变(添加演示图形) function addDemoGraphics() { const chinaBoundary = $prototype.$map.dataSources.add( Cesium.GeoJsonDataSource.load("/shp_zz.geojson", { stroke: Cesium.Color.YELLOW, strokeWidth: 0.8, fill: false, clampToGround: true, describe: null, }) ); chinaBoundary.then((dataSource) => { const entities = dataSource.entities.values; for (let entity of entities) { if (entity.polyline) { entity.polyline.fill = false; } } }); } // 原有方法完全不变(飞行到默认位置) function flyToDes() { return new Promise<void>((resolve) => { const center = Cesium.Cartesian3.fromDegrees(-98.0, 40.0); $prototype.$map.camera.flyTo({ destination: new Cesium.Cartesian3( -2432812.6687511606, 5559483.804371395, 2832009.419525571 ), orientation: { heading: 6.283185307179421, pitch: -1.0472145569408116, roll: 6.2831853071795205, }, complete: function () { resolve(); // 飞行完成后 resolve }, }); }); } // 原有方法完全不变(监听相机变化) const setupCameraListener = () => { $prototype.$map.camera.changed.addEventListener(updateOverview); }; // 原有方法完全不变(鹰眼地图点击处理) const handleMiniMapClick = (event: MouseEvent) => { if (!miniMapContainer.value || !overviewViewer.value) return; const rect = miniMapContainer.value.getBoundingClientRect(); const x = event.clientX - rect.left; const y = event.clientY - rect.top; const xPercent = (x / rect.width) * 100; const yPercent = (y / rect.height) * 100; const lon = ZHUZHOU_EXTENT.west + (xPercent / 100) * (ZHUZHOU_EXTENT.east - ZHUZHOU_EXTENT.west); const lat = ZHUZHOU_EXTENT.north - (yPercent / 100) * (ZHUZHOU_EXTENT.north - ZHUZHOU_EXTENT.south); $prototype.$map.camera.flyTo({ destination: Cesium.Cartesian3.fromDegrees(lon, lat, 1000000), }); }; // 原有生命周期方法完全不变 onMounted(() => { initMap(); loaded.value = true; addDemoGraphics(); // 先执行飞行,在飞行完成后再初始化鹰眼 flyToDes().then(() => { initMiniMap(); setupCameraListener(); updateOverview(); // 此时相机位置已稳定 // 波纹加载逻辑保持不变 (async () => { try { const ripplePoints = await loadRipplePoints(); rippleEntities.value = createMultipleRippleCircles( $prototype.$map, ripplePoints ); $prototype.$map.camera.changed.addEventListener(updateRippleVisibility); updateRippleVisibility(); } catch (error) { console.error("加载波纹圆失败:", error); } })(); }); }); // 原有方法完全不变(创建标记) const createMarker = (wgsLocation) => { if (currentMarker) { $prototype.$map.entities.remove(currentMarker); } console.log("标记图片加载状态:", markerImage); console.log("创建标记位置:", wgsLocation); currentMarker = $prototype.$map.entities.add({ position: Cesium.Cartesian3.fromDegrees( wgsLocation.lng, wgsLocation.lat, 10 ), label: { text: wgsLocation.name + "(标记位置)", font: "18px sans-serif", fillColor: Cesium.Color.YELLOW, outlineColor: Cesium.Color.BLACK, outlineWidth: 2, verticalOrigin: Cesium.VerticalOrigin.TOP, pixelOffset: new Cesium.Cartesian2(0, 20), heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND, show: true, }, // 关键:用 billboard 替换 point billboard: { image: new URL("@/assets/images/building.png", import.meta.url).href, // 本地图标 width: 32, // 图标宽度(像素) height: 32, // 图标高度(像素) verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 让图标尖点对地 heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND, scale: 1.0, }, }); console.log("标记实体已添加:", currentMarker); $prototype.$map.scene.requestRender(); return currentMarker; }; // 核心修改:使用工具类实现动态坐标系转换 const handleLocationSelected = (location: { lng: number; lat: number; name: string; }) => { if (!$prototype.$map) return; // 根据选择的坐标系进行转换 let wgsLocation; if (coordinateSystem.value === "gcj02") { // 高德/火星坐标转WGS84 wgsLocation = CoordinateTransformer.gcj02ToWgs84( location.lng, location.lat ); } else { // 百度坐标转WGS84 wgsLocation = CoordinateTransformer.bd09ToWgs84(location.lng, location.lat); } console.log( `转换前(${coordinateSystem.value})坐标:`, location.lng, location.lat ); console.log("转换后(WGS84)坐标:", wgsLocation.lng, wgsLocation.lat); // 使用转换后的坐标执行原有逻辑 const destination = Cesium.Cartesian3.fromDegrees( wgsLocation.lng, wgsLocation.lat, 3000 ); $prototype.$map.camera.flyTo({ destination, orientation: { heading: Cesium.Math.toRadians(0), pitch: Cesium.Math.toRadians(-90), roll: 0, }, duration: 2, complete: () => { createMarker({ ...location, ...wgsLocation }); }, }); }; // 原有方法完全不变(组件销毁清理) onUnmounted(() => { if ($prototype.$map) { $prototype.$map.destroy(); $prototype.$map = null; } if ($prototype.$map) { $prototype.$map.camera.changed.removeEventListener(updateRippleVisibility); } console.log("组件销毁"); }); const emit = defineEmits(["onload", "onclick"]); const initMars3d = async (option: any) => { emit("onclick", true); emit("onload", $prototype.$map); }; </script> <style lang="less"> /**cesium 工具按钮栏*/ .cesium-viewer-toolbar { top: auto !important; bottom: 35px !important; left: 12px !important; right: auto !important; } .cesium-toolbar-button img { height: 100%; } .cesium-viewer-toolbar > .cesium-toolbar-button, .cesium-navigationHelpButton-wrapper, .cesium-viewer-geocoderContainer { margin-bottom: 5px; float: left; clear: both; text-align: center; } .cesium-button { background-color: rgba(23, 49, 71, 0.8); color: #e6e6e6; fill: #e6e6e6; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); line-height: 32px; } .cesium-button:hover { background: #3ea6ff; } /**cesium 底图切换面板*/ .cesium-baseLayerPicker-dropDown { bottom: 0; left: 40px; max-height: 700px; margin-bottom: 5px; background-color: rgba(23, 49, 71, 0.8); } /**cesium 帮助面板*/ .cesium-navigation-help { top: auto; bottom: 0; left: 40px; transform-origin: left bottom; background: none; background-color: rgba(23, 49, 71, 0.8); .cesium-navigation-help-instructions { background: none; } .cesium-navigation-button { background: none; } .cesium-navigation-button-selected, .cesium-navigation-button-unselected:hover { background: rgba(0, 138, 255, 0.2); } } /**cesium 二维三维切换*/ .cesium-sceneModePicker-wrapper { width: auto; } .cesium-sceneModePicker-wrapper .cesium-sceneModePicker-dropDown-icon { float: right; margin: 0 3px; } /**cesium POI查询输入框*/ .cesium-viewer-geocoderContainer .search-results { left: 0; right: 40px; width: auto; z-index: 9999; } .cesium-geocoder-searchButton { background-color: rgba(23, 49, 71, 0.8); } .cesium-viewer-geocoderContainer .cesium-geocoder-input { background-color: rgba(63, 72, 84, 0.7); } .cesium-viewer-geocoderContainer .cesium-geocoder-input:focus { background-color: rgba(63, 72, 84, 0.9); } .cesium-viewer-geocoderContainer .search-results { background-color: rgba(23, 49, 71, 0.8); } /**cesium info信息框*/ .cesium-infoBox { top: 50px; background-color: rgba(23, 49, 71, 0.8); } .cesium-infoBox-title { background-color: rgba(23, 49, 71, 0.8); } /**cesium 任务栏的FPS信息*/ .cesium-performanceDisplay-defaultContainer { top: auto; bottom: 35px; right: 50px; } .cesium-performanceDisplay-ms, .cesium-performanceDisplay-fps { color: #fff; } /**cesium tileset调试信息面板*/ .cesium-viewer-cesiumInspectorContainer { top: 10px; left: 10px; right: auto; } .cesium-cesiumInspector { background-color: rgba(23, 49, 71, 0.8); } /**覆盖mars3d内部控件的颜色等样式*/ .mars3d-compass .mars3d-compass-outer { fill: rgba(23, 49, 71, 0.8); } .mars3d-contextmenu-ul, .mars3d-sub-menu { background-color: rgba(23, 49, 71, 0.8); > li > a:hover, > li > a:focus, > li > .active { background-color: #3ea6ff; } > .active > a, > .active > a:hover, > .active > a:focus { background-color: #3ea6ff; } } /* Popup样式*/ .mars3d-popup-color { color: #ffffff; } .mars3d-popup-background { background: rgba(23, 49, 71, 0.8); } .mars3d-popup-content { margin: 15px; } .mars3d-template-content label { padding-right: 6px; } .mars3d-template-titile { border-bottom: 1px solid #3ea6ff; } .mars3d-template-titile a { font-size: 16px; } .mars3d-tooltip { background: rgba(23, 49, 71, 0.8); border: 1px solid rgba(23, 49, 71, 0.8); } .mars3d-popup-btn-custom { padding: 3px 10px; border: 1px solid #209ffd; background: #209ffd1c; } .mars-dialog .mars-dialog__content { height: 100%; width: 100%; overflow: auto; padding: 5px; } .image { border: solid 2px #fff; } .content { height: 90%; padding-top: 10px; overflow-x: auto; overflow-y: auto; } .content-text { padding: 0 10px; text-indent: 30px; font-size: 17px; } .details-video { width: 100%; height: 760px; background-color: #000; } :where(.css-lt97qq9).ant-space { display: inline-flex; } :where(.css-lt97qq9).ant-space-align-center { align-items: center; } :where(.css-lt97qq9).ant-image .ant-image-mask { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: #fff; background: rgba(0, 0, 0, 0.5); cursor: pointer; opacity: 0; transition: opacity 0.3s; } :where(.css-lt97qq9).ant-image .ant-image-mask .ant-image-mask-info { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; padding: 0 4px; } :where(.css-1t97qq9)[class^="ant-image"] [class^="ant-image"], :where(.css-1t97qq9)[class*=" ant-image"] [class^="ant-image"], :where(.css-1t97qq9)[class^="ant-image"] [class*=" ant-image"], :where(.css-1t97qq9)[class*=" ant-image"] [class*=" ant-image"] { box-sizing: border-box; } :where(.css-lt97qq9).ant-image .ant-image-img { width: 100%; height: auto; vertical-align: middle; } </style> <style scoped> .mini-map-container { position: relative; width: 100%; height: 100%; } .main-viewer { width: 100%; height: 100%; } .mini-map { position: absolute; right: 3vw; bottom: 6vh; width: 12vw; height: 17vh; border: 2px solid #fff; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); z-index: 999; cursor: pointer; overflow: hidden; } .location-indicator { position: absolute; width: 19px; height: 19px; transform: translate(-50%, -50%); z-index: 100; /* border: 2px solid red; border-radius: 2px; */ } /* 原十字准星伪元素保持不变 */ .location-indicator::before, .location-indicator::after { content: ""; position: absolute; background-color: red; top: 50%; left: 50%; transform: translate(-50%, -50%); } .location-indicator::before { width: 14px; height: 2px; } .location-indicator::after { width: 2px; height: 14px; } .view-rectangle { position: absolute; border: 2px solid #00f2fe; z-index: 99; pointer-events: none; box-shadow: 0 0 20px rgba(0, 242, 254, 0.7); } /* 相机聚焦样式 - 四个角折角 */ .corner { position: absolute; width: 25px; height: 25px; z-index: 100; pointer-events: none; } .corner::before, .corner::after { content: ""; position: absolute; background: #00f2fe; box-shadow: 0 0 10px rgba(0, 242, 254, 0.8); } .corner-top-left { top: -2px; left: -2px; } .corner-top-left::before { top: 0; left: 0; width: 15px; height: 3px; } .corner-top-left::after { top: 0; left: 0; width: 3px; height: 15px; } .corner-top-right { top: -2px; right: -2px; } .corner-top-right::before { top: 0; right: 0; width: 15px; height: 3px; } .corner-top-right::after { top: 0; right: 0; width: 3px; height: 15px; } .corner-bottom-left { bottom: -2px; left: -2px; } .corner-bottom-left::before { bottom: 0; left: 0; width: 15px; height: 3px; } .corner-bottom-left::after { bottom: 0; left: 0; width: 3px; height: 15px; } .corner-bottom-right { bottom: -2px; right: -2px; } .corner-bottom-right::before { bottom: 0; right: 0; width: 15px; height: 3px; } .corner-bottom-right::after { bottom: 0; right: 0; width: 3px; height: 15px; } .camera-icon { position: absolute; top: 10px; right: 10px; color: #00f2fe; font-size: 24px; z-index: 100; text-shadow: 0 0 10px rgba(0, 242, 254, 0.8); } .focus-effect { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 98; border: 2px solid rgba(0, 242, 254, 0.2); border-radius: 5px; box-shadow: inset 0 0 30px rgba(0, 242, 254, 0.1); } .pulse { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 10px; height: 10px; border-radius: 50%; background: rgba(0, 242, 254, 0.7); box-shadow: 0 0 0 0 rgba(0, 242, 254, 0.7); animation: pulse 2s infinite; } ::v-deep.cesium-viewer-toolbar { display: none; } </style> 检查代码,为什么这个鹰眼地图不加载了
07-13
<template> <div class="label-container"> <div style="border: 1px solid #d9d9d9"></div> <div class="header"> <div class="biaoqian">客户特征标签</div> <el-popover placement="right" popper-class="group-popover" width="287" trigger="click"> <div class="full-label-section"> <span>业务需求</span> <div class="full-tag-group"> <el-popover v-for="(item, index) in requireList" :key="index" placement="top-start" popper-class="grid-popover" trigger="hover" :content="item.labelName" v-if="shouldShowPopover(item)" > <el-tag slot="reference" size="mini" class="truncate-tag" >{{ item.labelName }}</el-tag> </el-popover> <el-tag v-for="(item, index) in requireList" v-if="!shouldShowPopover(item)" :key="index" size="mini" >{{ item.labelName }}</el-tag> </div> </div> <div class="full-label-section"> <span>业务特征</span> <div class="full-tag-group"> <el-popover v-for="(item, index) in featureList" :key="index" placement="top-start" popper-class="grid-popover" trigger="hover" :content="item.labelName" v-if="shouldShowPopover(item)" > <el-tag slot="reference" type="" size="mini" class="truncate-tag" >{{ item.labelName }}</el-tag> </el-popover> <el-tag v-for="(item, index) in featureList" v-if="!shouldShowPopover(item)" :key="index" type="success" size="mini" >{{ item.labelName }}</el-tag> </div> </div> <div class="full-label-section full-label-sections"> <span>拓展任务</span> <div class="full-tag-group"> <el-popover v-for="(item, index) in taskList" :key="index" placement="top-start" popper-class="grid-popover" trigger="hover" :content="item.labelName" v-if="shouldShowPopover(item)" > <el-tag slot="reference" type="danger" size="mini" class="truncate-tag" >{{ item.labelName }}</el-tag> </el-popover> <el-tag v-for="(item, index) in taskList" v-if="!shouldShowPopover(item)" :key="index" type="danger" size="mini" >{{ item.labelName }}</el-tag> </div> </div> <el-button slot="reference" class="btn">更多</el-button> </el-popover> </div> <!-- 客户特征部分标签 --> <div style="padding: 10px; background: #fafafa"> <div class="label-section"> <span>业务需求</span> <div class="tag-group"> <el-popover v-for="(item, index) in showRequireList" :key="index" placement="top-start" popper-class="grid-popover" trigger="hover" :content="item.labelName" v-if="shouldShowPopover(item)" > <el-tag slot="reference" size="mini" class="truncate-tag" >{{ item.labelName }}</el-tag> </el-popover> <el-tag v-for="(item, index) in showRequireList" v-if="!shouldShowPopover(item)" :key="index" size="mini" >{{ item.labelName }}</el-tag> </div> </div> <div class="label-section"> <span>业务特征</span> <div class="tag-group"> <el-popover v-for="(item, index) in showFeatureList" :key="index" placement="top-start" popper-class="grid-popover" trigger="hover" :content="item.labelName" v-if="shouldShowPopover(item)" > <el-tag slot="reference" type="success" size="mini" class="truncate-tag" >{{ item.labelName }}</el-tag> </el-popover> <el-tag v-for="(item, index) in showFeatureList" v-if="!shouldShowPopover(item)" :key="index" type="success" size="mini" >{{ item.labelName }}</el-tag> </div> </div> <div class="label-section"> <span>拓展任务</span> <div class="tag-group"> <el-popover v-for="(item, index) in showTaskList" :key="index" placement="top-start" popper-class="grid-popover" trigger="hover" :content="item.labelName" v-if="shouldShowPopover(item)" > <el-tag slot="reference" type="danger" size="mini" class="truncate-tag" >{{ item.labelName }}</el-tag> </el-popover> <el-tag v-for="(item, index) in showTaskList" v-if="!shouldShowPopover(item)" :key="index" type="danger" size="mini" >{{ item.labelName }}</el-tag> </div> </div> </div> </div> </template> <script> import { Tag, Button, Popover,Tooltip } from &#39;element-ui&#39;; export default { name: &#39;groupLabelBox&#39;, components: { [Tag.name]: Tag, [Button.name]: Button, [Popover.name]: Popover, [Tooltip.name]: Tooltip, }, props: { requireList: { type: Array, required: true, }, featureList: { type: Array, required: true, }, taskList: { type: Array, required: true, }, }, data() { return { dialogVisible: false, }; }, computed: { showRequireList() { return this.requireList.slice(0, 2); }, showFeatureList() { return this.featureList.slice(0, 2); }, showTaskList() { return this.taskList.slice(0, 2); }, }, methods: { shouldShowPopover(item) { return item?.labelName?.length >= 7 } }, }; </script> <style lang="less" scoped> .group-popover { padding: 30px !important; /* 标题样式 */ .full-label-section{ padding-bottom: 12px; > span { display: block; margin-bottom: 10px; width: 56px; height: 14px; font-family: PingFangSC, PingFang SC, sans-serif; font-weight: 400; font-size: 14px; color: #888888; line-height: 14px; text-align: left; font-style: normal; } } /* 标签组样式 */ .full-tag-group { display: flex; flex-wrap: wrap; justify-content: flex-start; gap: 6px; .el-tag { margin: 0 !important; border-radius: 4px; font-size: 12px; padding: 0 8px; height: 24px; line-height: 24px; } } .full-label-sections { padding-bottom: 0 !important; } } .truncate-tag { display: inline-block; max-width: 112px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; vertical-align: middle; cursor: default; } .truncate-tag:hover:after { position: absolute; top: 30px; left: 10px; content: attr(data-title); color: #333333; } .grid-popover { min-width: 54px; text-align: center; padding: 10px 10px; } .label-container { position: relative; padding: 0px 14px 7px 16px; } .header { display: flex; justify-content: space-between; align-items: center; margin: 10px 0px; .biaoqian { width: 84px; height: 14px; font-family: PingFangSC, PingFang SC, sans-serif; font-weight: 400; font-size: 14px; color: #888888; line-height: 14px; text-align: left; font-style: normal; } .btn { width: 33px; height: 17px; padding: 0 4px; background: #ffffff; border-radius: 2px; border: 1px solid #cbcbcb; font-weight: 400; font-size: 12px; color: #333333; line-height: 17px; font-style: normal; > span { display: block; width: fit-content; } } } .label-section { > span { display: block; margin-bottom: 5px; width: 48px; height: 14px; font-family: PingFangSC, PingFang SC, sans-serif; font-weight: 400; font-size: 12px; color: #333333; line-height: 14px; text-align: left; font-style: normal; } } .tag-group { display: flex; gap: 10px; flex-wrap: wrap; } .label-section:not(:last-child) { margin-bottom: 10px; } </style> 使用vue2+js优化简化以上代码
07-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值