数据地图

This commit is contained in:
bb_pan 2025-04-07 19:23:57 +08:00
parent 745efa50f3
commit 4d019f3396
6 changed files with 1008 additions and 3 deletions

View File

@ -10,5 +10,6 @@
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=cClgLBaLgGUdQDilX9dGvieL"></script>
</body>
</html>

View File

@ -35,7 +35,7 @@ watch(
navMenuList.value = [
{ name: '首页', routerName: 'home', permissions: 'home' },
// { name: '', routerName: 'big-screen', permissions: 'home' },
{ name: '数据地图', routerName: '', permissions: 'home' },
{ name: '数据地图', routerName: 'map', permissions: 'home' },
{ name: '装备共享大厅', routerName: 'equipList', permissions: 'share' },
{ name: '租赁需求大厅', routerName: 'parity', permissions: 'demand' },
// { name: '', routerName: 'zoneEquipment' },
@ -45,7 +45,7 @@ watch(
navMenuList.value = [
{ name: '首页', routerName: 'home', permissions: 'home' },
// { name: '', routerName: 'big-screen', permissions: 'home' },
{ name: '数据地图', routerName: '', permissions: 'home' },
{ name: '数据地图', routerName: 'map', permissions: 'home' },
{ name: '装备共享大厅', routerName: 'equipList', permissions: 'home' },
{ name: '租赁需求大厅', routerName: 'parity', permissions: 'home' },
// { name: '', routerName: 'zoneEquipment' },
@ -57,7 +57,7 @@ watch(
navMenuList.value = [
{ name: '首页', routerName: 'home', permissions: 'home' },
// { name: '', routerName: 'big-screen', permissions: 'home' },
{ name: '数据地图', routerName: '', permissions: 'home' },
{ name: '数据地图', routerName: 'map', permissions: 'home' },
{ name: '装备共享大厅', routerName: 'equipList', permissions: 'home' },
// { name: '', routerName: 'zoneEquipment' },
{ name: '租赁需求大厅', routerName: 'parity', permissions: 'home' },

View File

@ -0,0 +1,6 @@
import { get, post } from '../../index'
// 获取坐标点数据
export const getMapData = () => {
return get('/material-mall/api/mqtt/latest-data', {})
}

View File

@ -93,6 +93,17 @@ const routes: Array<RouteRecordRaw> = [
activeName: 'home',
},
},
{
path: 'map',
name: 'map',
component: () => import('views/home/map.vue'),
meta: {
title: '数据地图',
keepAlive: true,
AuthFlag: false,
activeName: 'map',
},
},
// 自选直租
{
path: '/equipList',

91
src/utils/map.ts Normal file
View File

@ -0,0 +1,91 @@
// 坐标转换工具函数
const xPI = (Math.PI * 3000.0) / 180.0
const PI = Math.PI
const a = 6378245.0
const ee = 0.00669342162296594323
function transformlat(lng: number, lat: number): number {
let ret =
-100.0 +
2.0 * lng +
3.0 * lat +
0.2 * lat * lat +
0.1 * lng * lat +
0.2 * Math.sqrt(Math.abs(lng))
ret += ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0) / 3.0
ret += ((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) * 2.0) / 3.0
ret += ((160.0 * Math.sin((lat / 12.0) * PI) + 320 * Math.sin((lat * PI) / 30.0)) * 2.0) / 3.0
return ret
}
function transformlng(lng: number, lat: number): number {
let ret =
300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng))
ret += ((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0) / 3.0
ret += ((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) * 2.0) / 3.0
ret += ((150.0 * Math.sin((lng / 12.0) * PI) + 300.0 * Math.sin((lng / 30.0) * PI)) * 2.0) / 3.0
return ret
}
function convertDmToDd(dmValue: any) {
// 确保是字符串类型
const str = dmValue.toString()
// 经度处理(dddmm.mmmm)
if (str.length > 11) {
const degrees = parseFloat(str.substring(0, 3))
const minutes = parseFloat(str.substring(3))
return degrees + minutes / 60
}
// 纬度处理(ddmm.mmmm)
else {
const degrees = parseFloat(str.substring(0, 2))
const minutes = parseFloat(str.substring(2))
return degrees + minutes / 60
}
}
interface CoordInfo {
[key: string]: any
latitude: number
longitude: number
}
/**
* WGS84 -> GCJ02 -> BD09
* @param lng
* @param lat
* @param extra
* @returns
*/
export function wgs84ToGcj02ToBd09Public(
lng: number,
lat: number,
extra: { [key: string]: any } = {},
): CoordInfo {
lng = convertDmToDd(lng)
lat = convertDmToDd(lat)
// Step 1: WGS84 -> GCJ02
let dlat = transformlat(lng - 105.0, lat - 35.0)
let dlng = transformlng(lng - 105.0, lat - 35.0)
let radlat = (lat / 180.0) * PI
let magic = Math.sin(radlat)
magic = 1 - ee * magic * magic
let sqrtmagic = Math.sqrt(magic)
dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI)
dlng = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI)
let mglat = lat + dlat
let mglng = lng + dlng
// Step 2: GCJ02 -> BD09
let z = Math.sqrt(mglng * mglng + mglat * mglat) + 0.00002 * Math.sin(mglat * xPI)
let theta = Math.atan2(mglat, mglng) + 0.000003 * Math.cos(mglng * xPI)
let bdlng = z * Math.cos(theta) + 0.0065
let bdlat = z * Math.sin(theta) + 0.006
return {
...extra,
longitude: bdlng,
latitude: bdlat,
}
}

896
src/views/home/map.vue Normal file
View File

@ -0,0 +1,896 @@
<script setup lang="ts">
import EquipCard from 'components/equipCard.vue'
import EquipCardNew from 'components/equipCardNew/index.vue'
import NavMenu from 'components/Navmenu/index.vue'
import { getGoodsClassListApi, getCompanyListApi, getSwiperListApi } from 'http/api/home'
import { useStore } from 'store/user'
import { mainStore } from 'store/main'
import { getHotList } from 'http/api/equip'
import { loginNewApi } from 'http/api/home/index'
import { getUserInfoAPI } from 'http/api/login/index'
import { ElMessage } from 'element-plus'
import { wgs84ToGcj02ToBd09Public } from 'utils/map'
import { getMapData } from 'http/api/map'
const router: any = useRouter()
const userStore = useStore()
const store: any = mainStore()
const tokenNew = localStorage.getItem('tokenNew')
const companyList: any = ref([])
const classList: any = ref([])
const selectOptions = ref<boolean>(false) //
const selectOptionsValue = ref<string>('分类筛选')
const mapContainer = ref(null) //
const mapDialog = ref(false) //
//
const points: any = ref([])
const mapData = ref()
const tagData: any = ref()
const tagIndex: any = ref()
//
const getMapDataList = async () => {
try {
const res: any = await getMapData()
console.log('🚀 ~ getMapDataList ~ res:', res)
mapData.value = res.data
console.log('🚀 ~ getMapDataList ~ mapData.value:', mapData.value.longitude)
let { longitude, latitude } = wgs84ToGcj02ToBd09Public(
Number(mapData.value.longitude),
Number(mapData.value.latitude),
)
mapData.value.longitude = longitude
mapData.value.latitude = latitude
console.log('🚀 ~ getMapDataList ~ longitude:', longitude, latitude)
points.value.push({
id: 4,
lng: longitude,
lat: latitude,
})
initMap()
} catch (error) {
console.log('🚀 ~ getMapDataList ~ error:', error)
initMap()
}
}
//
const getCompanyListData = async () => {
const res: any = await getCompanyListApi()
const result: any = await getGoodsClassListApi()
// isShow false
companyList.value = res.data.filter((item: any) => item.isShow)
// console.log('🚀 ~ getCompanyListData ~ companyList.value:', companyList.value)
classList.value = result.data
// console.log('res', res)
}
getCompanyListData()
//
const onSharedHall = (level: number, typeId: any, name: any) => {
router.push({
name: 'equipList',
query: {
level,
typeId,
name,
},
})
}
const onSharedHallByCompany = (companyId: any, name: any) => {
router.push({
name: 'equipList',
query: {
companyId,
name,
},
})
}
const handlerGoodsDetails = (...arg: any) => {
const arr = arg.map((key: any, index: number) => {
return {
level: index + 1 + '',
typeId: key.id,
typeName: key.name,
}
})
router.push({
name: 'equipList',
state: { typeTag: arr },
})
}
const hotDeviceList: any = ref([])
/* 获取热搜装备 */
const getHotDeviceList = async () => {
const res: any = await getHotList({ pageSize: 3 })
hotDeviceList.value = res.data
}
/* 热搜卡片点击跳转至详情页 */
const onClick = (val: any) => {
router.push({
name: 'equipDetail',
query: {
id: val.id,
},
})
}
//
const onSelectOptions = () => {
selectOptions.value = !selectOptions.value
}
//
const onSelectItem = (type: number) => {
if (type === 1) {
selectOptionsValue.value = '分类筛选'
} else {
selectOptionsValue.value = '公司筛选'
}
selectOptions.value = false
}
const swiperList = ref([])
//
const getSwiperListData = async () => {
const res: any = await getSwiperListApi()
swiperList.value = res.rows
// console.log(res, '')
}
const onClickSwiper = (item: any) => {
if (item.slideLink) {
const link = item.slideLink.startsWith('http')
? item.slideLink
: `https://${item.slideLink}`
window.open(link, '_blank')
} else {
router.push({ name: 'equipList' })
}
}
getSwiperListData()
const formatUtcTimeV2 = (utcTime: any, date: any) => {
if (!utcTime || !date) return '未知时间'
// UTC
const hours = parseInt(utcTime.substring(0, 2))
const minutes = parseInt(utcTime.substring(2, 4))
const seconds = parseInt(utcTime.substring(4, 6))
const day = parseInt(date.substring(0, 2))
const month = parseInt(date.substring(2, 4)) - 1 // 0-11
const year = 2000 + parseInt(date.substring(4, 6)) // 20xx
// UTC
const utcDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds))
// (UTC+8)
// const beijingOffset = 8 * 60 * 60 * 1000; // 8
const beijingTime = new Date(utcDate.getTime())
//
return `${beijingTime
.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
})
.replace(/\//g, '-')} (北京时间)`
}
const initMap = () => {
if (window.BMap) {
const map = new BMap.Map(mapContainer.value)
const point = new BMap.Point(106.6302, 26.6477) //
//
points.value.forEach((item: any) => {
const point = new BMap.Point(item.lng, item.lat)
const marker = new BMap.Marker(point)
map.addOverlay(marker)
// const label = new BMap.Label(item.name, { offset: new BMap.Size(20, -10) })
// marker.setLabel(label) //
marker.addEventListener('click', () => {
//
console.log('点击了标记点')
// -
mapDialog.value = true
tagData.value = mapData.value.aphase
tagIndex.value = 1
})
})
map.centerAndZoom(point, 7) //
map.enableScrollWheelZoom(true) //
// 7
} else {
console.error('BMap 未加载')
}
}
const handleTag = (tag: number) => {
console.log('tag', tag)
tagIndex.value = tag
if (tag == 1) {
tagData.value = mapData.value.aphase
} else if (tag == 2) {
tagData.value = mapData.value.bphase
} else {
tagData.value = mapData.value.cphase
}
}
onMounted(async () => {
getMapDataList()
if (!tokenNew) {
const origin = window.location.href
console.log(origin.split('ticket=')[1], 'origin')
if (origin.indexOf('ticket') != -1) {
// console.log('origin.splitticket')
const res: any = await loginNewApi({
ticket: origin.split('ticket=')[1].split('#/')[0],
sysType: 0,
})
store.setToken(res.data.access_token)
localStorage.setItem('tokenNew', res.data.access_token)
const result: any = await getUserInfoAPI()
store.setUserInfo(result.user)
const roles = result.roles
const isAdmin = roles.some((e: any) => e == 'admin')
if (isAdmin) {
localStorage.setItem('rolesType', '3')
} else {
localStorage.setItem('rolesType', '1')
}
userStore.editcurrentMenuItem('goodsManagement')
//
}
}
})
</script>
<template>
<div class="home-index-banner">
<div class="home-index">
<!-- 商品菜单导航以及轮播图区域 -->
<div class="home-goods">
<ul class="left-nav">
<!-- 左侧机械名称菜单按钮 -->
<li class="left-filter" @click="onSelectOptions">
<div>
{{ selectOptionsValue }}
<el-icon style="margin-left: 10px"><ArrowDownBold /></el-icon>
</div>
<div class="select-options" v-if="selectOptions">
<div @click.stop="onSelectItem(1)">分类筛选</div>
<div @click.stop="onSelectItem(2)">公司筛选</div>
</div>
</li>
<template v-if="selectOptionsValue === '分类筛选'">
<div v-for="item in classList" :key="item.name" class="item-container">
<li class="item-nav" @click="onSharedHall(1, item.id, item.name)">
{{ item.name }}
<ul class="sub-goods">
<!-- 级联框内 类别名称 -->
<li
v-for="child in item.children"
:key="child.id"
@click.stop="onSharedHall(2, child.id, child.name)"
>
<!-- 第二级 -->
<span class="second-name">
{{ child.name }}
</span>
<span style="display: flex; flex: 1; flex-wrap: wrap">
<a
v-for="son in child.children"
:key="son.id"
style="font-size: 14px; font-weight: normal"
@click.stop="onSharedHall(3, son.id, son.name)"
>
{{ son.name }} ( {{ son.maCount }} )
</a>
</span>
</li>
</ul>
</li>
</div>
</template>
<template v-else>
<div
v-for="item in companyList"
:key="item.companyId"
class="item-container"
>
<li
class="item-nav"
@click="onSharedHallByCompany(item.companyId, item.companyName)"
>
{{ item.companyName }}
{{ item.maCount ? `(${item.maCount})` : '(0)' }}
</li>
</div>
</template>
</ul>
<div class="right-content">
<NavMenu />
<!-- 地图 -->
<div class="swiper-img">
<div ref="mapContainer" style="width: 100%; height: 100%"></div>
</div>
</div>
</div>
<!-- 热搜装备 -->
<div class="hot-equip-container" v-if="false">
<div class="hot-equip">
<span>最新装备</span>
<a
@click="
() => {
router.push({ name: 'equipList' })
}
"
style="
color: #00a288;
font-weight: bold;
text-decoration: underline;
font-size: 16px;
letter-spacing: 1px;
"
>更多产品</a
>
</div>
<div v-for="item in hotDeviceList" :key="item.typeName" style="margin-top: 15px">
<div class="hot-title">
{{ item.typeName }}
</div>
<ul class="equip-pic">
<li v-for="g in item.devInfoList" :key="g.maId" style="cursor: pointer">
<EquipCardNew
@onClick="onClick"
:id="g.maId"
:companyId="g.companyId"
:company="g.companyName"
:operateAddress="g.operateAddress"
:name="g.deviceName"
:typeName="g.typeName"
:price="g.dayLeasePrice"
:url="g.picUrl"
/>
</li>
</ul>
</div>
</div>
</div>
</div>
<el-dialog v-model="mapDialog" title="IOT设备信息" width="70%">
<div>
<el-row :gutter="20">
<el-col :span="12" style="margin-bottom: 20px">
<el-card>
<template #header>
<div class="card-header">
<span>设备信息</span>
</div>
</template>
<div class="card-item">
<span class="card-title">设备UUID: </span>{{ mapData.uuid }}
</div>
<div class="card-item">
<span class="card-title">最后更新时间: </span
>{{ formatUtcTimeV2(mapData.utcTime, mapData.date) }}
</div>
</el-card>
</el-col>
<el-col :span="12" style="margin-bottom: 20px">
<el-card>
<template #header>
<div class="card-header">
<span>环境数据</span>
</div>
</template>
<div class="card-item">
<span class="card-title">温度: </span>{{ mapData.temperature }}
</div>
<div class="card-item">
<span class="card-title">湿度: </span>{{ mapData.humidity }}%
</div>
</el-card>
</el-col>
<el-col :span="12" style="margin-bottom: 20px">
<el-card>
<template #header>
<div class="card-header">
<span>位置信息</span>
</div>
</template>
<div class="card-item">
<span class="card-title">经度: </span>{{ mapData.longitude }} N
</div>
<div class="card-item">
<span class="card-title">维度: </span>{{ mapData.latitude }} E
</div>
<div class="card-item">
<span class="card-title">速度: </span>{{ mapData.speedOverGround }}
</div>
<div class="card-item">
<span class="card-title">航向: </span
>{{ mapData.courseOverGround || 0 }}°
</div>
<div class="card-item">
<span class="card-title">海拔: </span>{{ mapData.altitude }}
</div>
</el-card>
</el-col>
<el-col :span="12" style="margin-bottom: 20px">
<el-card>
<template #header>
<div class="card-header">
<span>运动传感器数据</span>
</div>
</template>
<div class="card-item">
<span class="card-title">X轴加速度: </span>{{ mapData.accX }} m/
</div>
<div class="card-item">
<span class="card-title">Y轴加速度: </span>{{ mapData.accY }} m/
</div>
<div class="card-item">
<span class="card-title">Z轴加速度: </span>{{ mapData.accZ }} m/
</div>
<div class="card-item">
<span class="card-title">X轴角速度: </span>{{ mapData.gyroX }} m/
</div>
<div class="card-item">
<span class="card-title">Y轴角速度: </span>{{ mapData.gyroY }} m/
</div>
<div class="card-item">
<span class="card-title">Z轴角速度: </span>{{ mapData.gyroZ }} m/
</div>
</el-card>
</el-col>
<el-col :span="24" style="margin-bottom: 20px">
<el-card>
<template #header>
<div class="card-header">
<span>运动传感器数据</span>
</div>
</template>
<div
class="card-item"
style="display: flex; justify-content: space-between"
>
<div><span class="card-title">定位质量: </span>{{ 'GPS定位' }}</div>
<div>
<span class="card-title">使用卫星数: </span
>{{ mapData.numSatellitesUsed }}
</div>
<div><span class="card-title">水平精度: </span>{{ mapData.hdop }}</div>
</div>
</el-card>
</el-col>
<el-col :span="24" style="margin-bottom: 20px">
<el-card>
<template #header>
<div class="card-header">
<span>电力数据</span>
</div>
</template>
<div style="margin-bottom: 15px">
<el-button :type="tagIndex == 1 ? 'primary' : ''" @click="handleTag(1)"
>A组</el-button
>
<el-button :type="tagIndex == 2 ? 'primary' : ''" @click="handleTag(2)"
>B组</el-button
>
<el-button :type="tagIndex == 3 ? 'primary' : ''" @click="handleTag(3)"
>C组</el-button
>
</div>
<div
class="card-item"
style="display: flex; justify-content: space-between"
>
<div>
<div class="item">
<span class="card-title">电压: </span>{{ tagData.voltage }} W
</div>
<div class="item">
<span class="card-title">电流: </span>{{ tagData.current }} A
</div>
<div class="item">
<span class="card-title">有功功率: </span
>{{ tagData.activePower }} V
</div>
</div>
<div>
<div class="item">
<span class="card-title">无功功率: </span
>{{ tagData.reactivePower }} VAR
</div>
<div class="item">
<span class="card-title">功率因素: </span
>{{ tagData.powerFactor }}
</div>
<div class="item">
<span class="card-title">频率: </span>{{ tagData.frequency }} Hz
</div>
</div>
<div>
<div class="item">
<span class="card-title">正向有功: </span
>{{ tagData.forwardActiveEnergy }} kWh
</div>
<div class="item">
<span class="card-title">正向无功: </span
>{{ tagData.forwardReactiveEnergy }} kvarh
</div>
<div class="item">
<span class="card-title">反向有功: </span
>{{ tagData.reverseActiveEnergy }} kWh
</div>
</div>
<div>
<div class="item">
<span class="card-title">反向无功: </span
>{{ tagData.reverseReactiveEnergy }} kvarh
</div>
<div class="item">
<span class="card-title">视在功率: </span
>{{ tagData.apparentPower }} VA
</div>
<div class="item">
<span class="card-title">谐波有功: </span
>{{ tagData.harmonicActivePower }} W
</div>
<div class="item">
<span class="card-title">基波有功: </span
>{{ tagData.fundamentalActivePower }} W
</div>
<div class="item">
<span class="card-title">基波无功: </span
>{{ tagData.fundamentalReactivePower }} VAR
</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="mapDialog = false">关闭</el-button>
</div>
</template>
</el-dialog>
</template>
<style lang="scss" scoped>
.card-item {
margin-bottom: 15px;
text-align: left;
.item {
margin-bottom: 15px;
}
}
.card-title {
font-weight: bold;
color: #333;
}
:deep(.el-carousel__container) {
.el-carousel__item {
.el-image {
height: 100%;
img {
}
}
}
}
.home-index-banner {
background-color: #fff;
}
.home-index {
.home-goods {
height: 700px;
display: flex;
position: relative;
.left-nav {
width: 270px;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
background-color: #f7f9fa;
overflow: hidden;
overflow-y: auto;
border-radius: 16px;
.left-filter {
margin-top: 10px;
padding: 16px 0;
position: relative;
display: flex;
align-content: center;
justify-content: center;
color: #38b2a4;
font-size: 22px;
// height: 46px;
// line-height: 46px;
// text-align: center;
font-weight: bold;
cursor: pointer;
width: 100%;
.select-options {
position: absolute;
bottom: 0;
left: 0;
transform: translate(50%, 100%);
// left: 50%;
// bottom: -65px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
// transform: translateX(-50%);
background-color: #fff;
div {
padding: 10px 30px;
text-align: left;
// font-size: 14px;
font-size: 16px;
font-weight: normal;
cursor: pointer;
color: #333;
}
div:hover {
color: #fff;
background-color: #00a288;
}
}
}
.item-container {
width: 100%;
text-align: center;
.item-nav {
// text-align: left;
// padding-left: 50px;
display: inline-block;
margin-top: 10px;
// padding: 0 10px;
color: #000;
min-height: 32px;
line-height: 32px;
text-align: center;
padding: 2px 20px;
// font-size: 16px !important;
&:hover {
cursor: pointer;
// color: #2282ff;
// font-weight: bold;
background-color: #1abc9c;
border-radius: 18px;
color: #fff;
}
}
.sub-goods {
position: absolute;
display: none;
top: 0;
left: 270px;
width: 1242px;
min-height: 100%;
max-height: 100%;
background-color: #fff;
z-index: 999;
// opacity: 0.9;
border: 1px solid #eee;
overflow-y: auto;
box-sizing: border-box;
li {
// margin: 10px 0;
padding: 10px 80px;
color: #333;
font-weight: bold;
display: flex;
align-items: center;
// background: orange;
.second-name {
// width: 160px;
margin-left: 10px;
}
span a {
margin: 0 30px;
font-size: 16px;
font-weight: normal;
color: #333;
&:hover {
color: #1abc9c;
border-bottom: 1px solid #1abc9c !important;
}
}
.last-level {
margin-left: 3px;
}
.last-level:hover {
color: #1abc9c;
border-bottom: 1px solid #1abc9c !important;
}
}
}
}
.item-container:hover {
.sub-goods {
display: block !important;
z-index: 9999;
}
}
}
.right-content {
box-sizing: border-box;
flex: 1;
height: 100%;
margin-left: 20px;
display: flex;
flex-direction: column;
.swiper-img {
margin-top: 20px;
flex: 1;
border-radius: 8px;
}
}
}
.hot-equip {
margin-top: 30px;
height: 37px;
display: flex;
align-items: center;
justify-content: space-between;
span {
background-color: #1abc9c;
padding: 10px 18px;
color: #fff;
}
a {
color: rgba(24, 103, 221, 0.8);
font-size: 14px;
cursor: pointer;
}
}
.hot-title {
padding: 15px 0 10px 20px;
font-size: 16px;
font-weight: bold;
letter-spacing: 1px;
}
.equip-pic {
display: flex;
flex-wrap: wrap;
li {
width: calc((100% - 100px) / 4);
margin: 8px 0 8px 20px;
&:nth-child(4n) {
margin-right: 20px;
}
}
}
.consult-nav {
width: 100%;
height: 40px;
border-bottom: 1px solid #e0e0e0;
display: flex;
li {
height: 40px;
line-height: 40px;
margin: 0 15px;
&:hover {
border-bottom: 1px solid #3cb4a6;
cursor: pointer;
}
}
}
.consult-content {
margin-top: 20px;
height: 210px;
display: flex;
align-items: center;
.left-bg {
background: url(../../assets/img/home/2023_12_01_beijing2/left_bg.png) no-repeat;
background-size: cover;
width: 340px;
height: 210px;
}
.right-consult {
height: 210px;
flex: 1;
width: calc(100% - 340px);
// padding-left: 35px;
overflow-y: auto;
.consult-box {
margin-bottom: 10px;
//height: 70px;
padding-left: 15px;
cursor: pointer;
.consult-title {
height: 30px;
display: flex;
justify-content: space-between;
align-items: center;
h2 {
font-weight: bold;
}
span {
color: #827d7d;
font-size: 14px;
}
}
.consult-info {
width: calc(100% - 100px);
height: 10px;
line-height: 40px;
border-bottom: 1px dashed #979797;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
}
}
:deep(.el-carousel__container) {
height: 100%;
}
.el-carousel--horizontal {
border-radius: 14px;
}
</style>