zn-admin-vue3-wcs/src/views/mapPage/realTimeMap/components/indexPage.vue
2025-02-22 17:06:16 +08:00

1030 lines
30 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div
class="affix-container"
id="indexpage-container"
:style="{
height: heightVal * radio + 'px',
cursor: isDrag ? 'pointer' : 'default',
scale: isSizeRaio,
transformOrigin: 'center center'
}"
>
<div
class="indexpage-container"
v-if="imgUrl"
v-drag="isDrag"
:style="{ scale: 1, transformOrigin: '0 0' }"
ref="draggableElement"
>
<div class="indexpage-container-box">
<img :src="imgUrl" alt="" class="indexpage-container-box-img" />
<div class="indexpage-container-box-point">
<!-- 连线 -->
<div v-if="legendObj.driveLineShow">
<div class="line-box" v-for="(item, index) in lineList" :key="index">
<svg id="svg" :width="widthVal" :height="heightVal">
<template v-if="item.method == 0">
<line
:x1="(Number(item.startPointX) + Number(item.beginWidth) / 2) * radio"
:y1="(Number(item.startPointY) + Number(item.beginHigh) / 2) * radio"
:x2="(Number(item.endPointX) + Number(item.endWidth) / 2) * radio"
:y2="(Number(item.endPointY) + Number(item.endHigh) / 2) * radio"
stroke="#00329F"
stroke-width="5"
/>
</template>
<template v-else>
<path :d="getCurvePath(item)" :stroke="'#00329F'" stroke-width="5" fill="none" />
</template>
</svg>
</div>
</div>
<!-- 小车 -->
<div
class="indexpage-car-item"
v-for="(item, index) in testCarList"
@dblclick="carDbClick(item, index)"
:key="index"
:style="{
left: item.realX * radio + 'px',
top: item.realY * radio + 'px',
width: legendObj.carShow ? 40 * radio + 'px' : '0',
height: legendObj.carShow ? 22 * radio + 'px' : '0'
}"
>
<img
src="@/assets/imgs/indexPage/chache-4备份 7@2x.png"
alt=""
style="width: 100%; height: 100%"
/>
</div>
<div
class="indexpage-container-box-point-item"
v-for="(item, index) in pointList"
:key="index"
:style="{ left: item.locationX * radio + 'px', top: item.locationY * radio + 'px' }"
>
<!-- 库位点 -->
<div
class="indexpage-container-box-point-item-inner"
v-if="item.showData"
:style="{
width: item.showData.locationWide * radio + 'px',
height: item.showData.locationDeep * radio + 'px'
}"
>
<!-- 库位 2-->
<div v-if="item.type == 2" style="width: 100%; height: 100%">
<el-popover placement="top-start" trigger="hover" width="auto">
<template #reference>
<img
:src="item.imgUrl"
alt=""
style="width: 100%; height: 100%"
@dblclick="storeClick(item)"
/>
</template>
<div class="indexpage-container-box-point-item-inner-popover">
<div
class="indexpage-container-box-point-item-inner-popover-item"
style="margin-bottom: 8px"
>
<div class="indexpage-container-box-point-item-inner-popover-name">
库位名:
</div>
<div class="indexpage-container-box-point-item-inner-popover-value">
{{ item.showData.locationNo || '' }}
</div>
</div>
<div
class="indexpage-container-box-point-item-inner-popover-item"
style="margin-bottom: 8px"
>
<div class="indexpage-container-box-point-item-inner-popover-name">
所属线库:
</div>
<div class="indexpage-container-box-point-item-inner-popover-value">
{{ item.showData.laneName || '' }}
</div>
</div>
<div
class="indexpage-container-box-point-item-inner-popover-item"
style="margin-bottom: 8px"
>
<div class="indexpage-container-box-point-item-inner-popover-name">
所属区域:
</div>
<div class="indexpage-container-box-point-item-inner-popover-value">
{{ item.showData.areaName || '' }}
</div>
</div>
</div>
</el-popover>
</div>
<!-- 设备点 -->
<div v-if="item.type == 3">
<img
:src="item.formattedData.mapImageUrl"
alt=""
style="width: 100%; height: 100%"
/>
</div>
</div>
<!-- 设备点 -->
<div
class="indexpage-container-box-point-item-inner"
v-if="item.type == 3"
:style="{
width: item.formattedData.locationWide * radio + 'px',
height: item.formattedData.locationDeep * radio + 'px'
}"
>
<div>
<img
:src="
item.formattedData.mapImageUrl
? item.formattedData.mapImageUrl
: 'https://api.znkjfw.com/admin-api/infra/file/4/get/设备点_png_179_1739327151877.png'
"
alt=""
style="width: 100%; height: 100%"
/>
</div>
</div>
<!-- 停车点 -->
<div
class="indexpage-container-box-point-item-inner"
v-if="item.type == 4"
:style="{
width: item.formattedData.locationWide * radio + 'px',
height: item.formattedData.locationDeep * radio + 'px'
}"
>
<div>
<img
:src="
item.formattedData.mapImageUrl
? item.formattedData.mapImageUrl
: 'https://api.znkjfw.com/admin-api/infra/file/4/get/停车场-01_png_179_1739326933020.png'
"
alt=""
style="width: 100%; height: 100%"
/>
</div>
</div>
<!-- 路径点 -->
<div
class="indexpage-container-box-point-item-inner"
v-if="item.type == 5"
:style="{
width: 40 * radio + 'px',
height: 40 * radio + 'px'
}"
>
<div>
<img
:src="
item.formattedData.mapImageUrl
? item.formattedData.mapImageUrl
: 'https://api.znkjfw.com/admin-api/infra/file/4/get/区域_png_179_1739327151876.png'
"
alt=""
style="width: 100%; height: 100%"
/>
</div>
</div>
<!-- 等待点 -->
<div
class="indexpage-container-box-point-item-inner"
v-if="item.type == 6"
:style="{
width: 40 * radio + 'px',
height: 40 * radio + 'px'
}"
>
<div>
<img
:src="
item.formattedData.mapImageUrl
? item.formattedData.mapImageUrl
: 'https://api.znkjfw.com/admin-api/infra/file/4/get/等待点_png_179_1739326991439.png'
"
alt=""
style="width: 100%; height: 100%"
/>
</div>
</div>
<div
v-if="item.type == 1"
style="width: 10px; height: 10px; border-radius: 50%; background: #1890ff"
>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 左下角图例 -->
<div class="affix-container-left" :style="{ left: boxLeft + 'px' }">
<div class="affix-container-left-box">
<div
class="affix-container-left-box-item-box"
:style="{
height: legendObj.legendShow ? '84px' : '0',
overflow: 'hidden',
transition: 'all 0.3s ease-in-out'
}"
>
<div class="affix-container-left-box-item">
<div class="affix-container-left-box-item-left"> 行驶路线 </div>
<img
src="@/assets/imgs/indexPage/yanjing_xianshi_o.png"
alt=""
class="affix-container-left-box-item-img"
v-if="legendObj.driveLineShow"
@click="changDriveLineShow"
/>
<img
src="@/assets/imgs/indexPage/yanjing_yincang_o.png"
alt=""
class="affix-container-left-box-item-img"
v-if="!legendObj.driveLineShow"
@click="changDriveLineShow"
/>
</div>
<div class="affix-container-left-box-item">
<div class="affix-container-left-box-item-left"> 车辆 </div>
<img
src="@/assets/imgs/indexPage/yanjing_xianshi_o.png"
alt=""
class="affix-container-left-box-item-img"
v-if="legendObj.carShow"
@click="changCarShow"
/>
<img
src="@/assets/imgs/indexPage/yanjing_yincang_o.png"
alt=""
class="affix-container-left-box-item-img"
v-if="!legendObj.carShow"
@click="changCarShow"
/>
</div>
</div>
<div
class="affix-container-left-box-item-bottom"
@click="legendObj.legendShow = !legendObj.legendShow"
>
<div class="affix-container-left-box-item-bottom-left"> 图例 </div>
<img
src="@/assets/imgs/indexPage/zhankai@2x.png"
alt=""
class="affix-container-left-box-item-bottom-img"
:style="{
transform: legendObj.legendShow ? 'rotate(180deg)' : 'rotate(0deg)',
transition: 'all 0.3s ease-in-out'
}"
/>
</div>
</div>
</div>
<!-- 右下角的功能 -->
<div class="affix-container-right">
<!-- 拖拽 -->
<div class="affix-container-right-item" @click="changeIsDrag">
<img src="@/assets/imgs/indexPage/编组 12.png" alt="" style="width: 100%; height: 100%" />
</div>
<!-- 放大 -->
<div class="affix-container-right-item">
<img
src="@/assets/imgs/indexPage/编组 14.png"
alt=""
style="width: 100%; height: 100%"
@click="changeSizeRaio(0.2)"
/>
</div>
<!-- 缩小 -->
<div class="affix-container-right-item">
<img
src="@/assets/imgs/indexPage/编组 15.png"
alt=""
style="width: 100%; height: 100%"
@click="changeSizeRaio(-0.2)"
/>
</div>
<!-- 全屏 -->
<div class="affix-container-right-item">
<img
src="@/assets/imgs/indexPage/编组 22.png"
alt=""
style="width: 100%; height: 100%"
@click="toggleFullScreen"
/>
</div>
</div>
<storeDialog ref="storeDialogRef" @success="getList" />
<carDialog ref="carDialogRef" />
</template>
<script setup>
import {
ref,
defineComponent,
reactive,
nextTick,
onMounted,
onBeforeUnmount,
onUnmounted
} from 'vue'
import * as MapApi from '@/api/map/map'
import WebSocketClient from '../webSocket.js'
import storeDialog from './storeDialog.vue'
import { color } from 'echarts'
import { resetDragPosition } from '@/utils/drag'
import carDialog from './carDialog.vue'
import { is } from 'bpmn-js/lib/util/ModelUtil'
const imgUrl = ref('')
const carDialogRef = ref(null)
const socketClient = ref(null)
const emit = defineEmits(['transmitMapId'])
const storeDialogRef = ref(null)
const list = ref([])
const nowObject = ref(null)
const testCarList = ref([])
const carPointListFun = () => {
let testJson = {
type: 'map_push',
content:
'{"d0:65:78:c4:af:cc":"{\\"id\\":1,\\"macAddress\\":\\"d0:65:78:c4:af:cc\\",\\"robotModelNumber\\":\\"A-1\\",\\"pose2d\\":{\\"y\\":\\"1\\",\\"x\\":\\"2\\",\\"yaw\\":\\"30\\",\\"floor\\":\\"1\\",\\"area\\":\\"A区\\",\\"bat_soc\\":\\"40\\"}}"}'
}
let data = JSON.parse(testJson.content)
// console.log("============",data)
let dataList = []
for (let key in data) {
dataList.push({
macAddress: key,
data: JSON.parse(data[key])
})
}
console.log('=====', dataList)
testCarList.value = dataList
// let data2 = JSON.parse(data['d0:65:78:c4:af:cc'])
// console.log(data2)
}
//是否可以拖拽
const isDrag = ref(false)
const changeIsDrag = () => {
nextTick(() => {
isDrag.value = !isDrag.value
console.log(isDrag.value)
if (!isDrag.value) {
//还原位置
resetPosition()
}
})
}
// 获取曲线的路径
const getCurvePath = (curve) => {
let startPointX = (Number(curve.startPointX) + Number(curve.beginWidth) / 2) * radio.value
let startPointY = (Number(curve.startPointY) + Number(curve.beginHigh) / 2) * radio.value
let endPointX = (Number(curve.endPointX) + Number(curve.endWidth) / 2) * radio.value
let endPointY = (Number(curve.endPointY) + Number(curve.endHigh) / 2) * radio.value
return `M ${startPointX} ${startPointY} C ${curve.beginControlX * radio.value} ${curve.beginControlY * radio.value}, ${curve.endControlX * radio.value} ${curve.endControlY * radio.value}, ${endPointX} ${endPointY}`
}
//放大缩小
const isSizeRaio = ref(1)
const changeSizeRaio = (type) => {
if (type < 0 && isSizeRaio.value + type <= 0) {
return
}
isSizeRaio.value += type
}
//图例状态
const legendObj = reactive({
driveLineShow: true,
carShow: true,
legendShow: true
})
// 车辆是否显示
const changCarShow = () => {
legendObj.carShow = !legendObj.carShow
}
// 车辆行驶路线是否展示
const changDriveLineShow = () => {
legendObj.driveLineShow = !legendObj.driveLineShow
}
const toggleFullScreen = () => {
var elem = document.getElementById('indexpage-container') // 获取元素
if (!document.fullscreenElement) {
// 检查是否已经是全屏模式
if (elem.requestFullscreen) {
// 支持requestFullscreen API的标准方式
elem.requestFullscreen().catch((err) => {
alert(`无法进入全屏模式: ${err.message}`) // 处理错误情况
})
} else if (elem.mozRequestFullScreen) {
// 旧版Firefox的API名称已废弃
elem.mozRequestFullScreen()
} else if (elem.webkitRequestFullscreen) {
// WebKit/Safari/Chrome的API名称已废弃
elem.webkitRequestFullscreen()
} else if (elem.msRequestFullscreen) {
// IE/Edge的API名称已废弃
elem.msRequestFullscreen()
}
isDrag.value = true
} else {
// 退出全屏模式
if (document.exitFullscreen) {
// 标准API退出全屏模式
document.exitFullscreen()
} else if (document.mozCancelFullScreen) {
// 旧版Firefox的API名称已废弃
document.mozCancelFullScreen()
} else if (document.webkitExitFullscreen) {
// WebKit/Safari/Chrome的API名称已废弃
document.webkitExitFullscreen()
} else if (document.msExitFullscreen) {
// IE/Edge的API名称已废弃
document.msExitFullscreen()
}
console.log('退出全屏')
isDrag.value = false
resetPosition()
}
// 监听全屏状态变化事件
document.addEventListener('fullscreenchange', function () {
if (!document.fullscreenElement) {
console.log('已退出全屏模式')
// 在这里可以添加退出全屏后的逻辑
isDrag.value = false
resetPosition()
} else {
console.log('已进入全屏模式')
// 在这里可以添加进入全屏后的逻辑
isDrag.value = true
}
})
// 监听旧版浏览器的全屏状态变化事件
document.addEventListener('mozfullscreenchange', function () {
if (!document.mozFullScreenElement) {
console.log('已退出全屏模式 (Firefox旧版)')
isDrag.value = false
resetPosition()
} else {
console.log('已进入全屏模式 (Firefox旧版)')
isDrag.value = true
}
})
document.addEventListener('webkitfullscreenchange', function () {
if (!document.webkitFullscreenElement) {
console.log('已退出全屏模式 (WebKit旧版)')
isDrag.value = false
resetPosition()
} else {
console.log('已进入全屏模式 (WebKit旧版)')
isDrag.value = true
}
})
document.addEventListener('msfullscreenchange', function () {
if (!document.msFullscreenElement) {
console.log('已退出全屏模式 (IE/Edge旧版)')
isDrag.value = false
resetPosition()
} else {
console.log('已进入全屏模式 (IE/Edge旧版)')
isDrag.value = true
}
})
}
//获取地图区域
const getList = async () => {
let data = await MapApi.getPositionMapGetMap()
let mapList = []
for (let key in data) {
mapList.push({
floor: key,
label: key + '层',
value: key,
children: data[key]
})
}
if (mapList.length) {
mapList.forEach((item) => {
if (item.children.length) {
item.children.forEach((child) => {
child.label = child.area
child.value = child.id
})
}
})
}
list.value = mapList
console.log(list.value, data)
//默认取第一个
if (data[1][0]) {
getMapData(data[1][0])
}
}
//库位双击
const storeClick = async (item) => {
console.log(item)
let storeData = await MapApi.houseLocationGetByMapItemId({
mapId: item.positionMapId,
mapItemId: item.id
})
console.log(storeData)
storeDialogRef.value.open(JSON.parse(JSON.stringify(storeData)))
}
const lineList = ref([])
const pointList = ref([])
const getPositionMapListFun = async (positionMapId) => {
// console.log(positionMapId)
let data = await MapApi.getPositionMapItemList({ positionMapId: positionMapId })
// console.log(data)
if (data && data.length > 0) {
data.forEach((item) => {
// console.log(JSON.parse(item.dataJson))
item.formattedData = item.dataJson ? JSON.parse(item.dataJson) : ''
item.showData = item.dataJson ? JSON.parse(item.dataJson)[0] : null
item.imgUrl = formatteTypeImg(item.type)
})
}
console.log(data)
pointList.value = data
// console.log(pointList.value)
// let lineStyle = calculateDistanceAndAngle(
// {
// left: pointList.value[0].locationX,
// top: pointList.value[0].locationY
// },
// {
// left: pointList.value[1].locationX,
// top: pointList.value[1].locationY
// }
// )
// console.log(lineStyle)
// lineList.value = [
// {
// ...lineStyle,
// color: '#1677ff',
// height: 2
// }
// ]
}
const draggableElement = ref(null)
const resetPosition = () => {
if (draggableElement.value) {
resetDragPosition(draggableElement.value)
}
}
const calculateDistanceAndAngle = (point1, point2) => {
// 计算水平和垂直方向的差值
const dx = point2.left - point1.left
const dy = point2.top - point1.top
// 计算两点之间的长度(使用勾股定理)
const distance = Math.sqrt(dx * dx + dy * dy)
// 计算两点连线与水平轴正方向的夹角(弧度)
const angleInRadians = Math.atan2(dy, dx)
// 将弧度转换为角度
const angleInDegrees = angleInRadians * (180 / Math.PI)
return {
distance: distance,
angle: angleInDegrees,
left: point1.left,
top: point1.top
}
}
const formatteTypeImg = (type) => {
switch (type) {
case 1:
return ''
case 2:
return 'https://api.znkjfw.com/admin-api/infra/file/4/get/库位库存_png_179_1739326653035.png'
case 3:
return 'https://api.znkjfw.com/admin-api/infra/file/4/get/设备点_png_179_1739327151877.png'
case 4:
return 'https://api.znkjfw.com/admin-api/infra/file/4/get/停车场-01_png_179_1739326933020.png'
case 5:
return 'https://api.znkjfw.com/admin-api/infra/file/4/get/区域_png_179_1739327151876.png'
case 6:
return 'https://api.znkjfw.com/admin-api/infra/file/4/get/等待点_png_179_1739326991439.png'
default:
return ''
}
}
const replaceHttpWithWs = (str) => {
return str.replace(/^http/, 'ws')
}
// 连接websocket
const linkWebSocket = (url) => {
socketClient.value = new WebSocketClient(url)
if (socketClient.value) {
// 监听消息
socketClient.value.onMessage((message) => {
console.log('收到消息:', message)
if (message.type == 'map_push') {
let data = JSON.parse(testJson.content)
// console.log("============",data)
let dataList = []
for (let key in data) {
dataList.push({
macAddress: key,
data: JSON.parse(data[key])
})
}
console.log('=====', dataList)
testCarList.value = dataList
}
})
}
}
// 发送websocket消息
const sendMessage = () => {
socketClient.value.send('Hello, WebSocket!')
}
// 断开连接
const disconnect = () => {
socketClient.value.disconnect()
}
//获取扫描图
const getMapData = async (item) => {
// console.log("============",item)
nowObject.value = JSON.parse(JSON.stringify(item))
let websoketUrl = `${replaceHttpWithWs(import.meta.env.VITE_BASE_URL)}/infra/ws?type=map&floor=${nowObject.value.floor}&area=${nowObject.value.area}`
console.log(websoketUrl)
linkWebSocket(websoketUrl)
getPositionMapListFun(nowObject.value.id)
emit('transmitMapInfo', {
id: item.id,
floor: item.floor,
area: item.area
})
let data = await MapApi.getPositionMapdDwnloadPngBase64({
floor: item.floor,
area: item.area
})
imgUrl.value = data
computedRatio()
getMapLineList()
}
const heightVal = ref(0)
const widthVal = ref(0)
const radio = ref(1)
const computedRatio = () => {
nextTick(() => {
if (imgUrl.value) {
//这段代码之后会删掉 yaml会给原始宽高
getImageSize(imgUrl.value)
.then(({ width, height }) => {
// console.log("原始地图的宽高",JSON.parse(nowObject.value.yamlJson))
if (testCarList.value.length) {
testCarList.value.forEach((item) => {
item.originWidth = width
item.originHeight = height
item.origin = JSON.parse(nowObject.value.yamlJson).origin
item.realX = convertToFrontendCoordinates(item.origin, width, height, [
item.data.pose2d.x,
item.data.pose2d.y
]).left
item.realY = convertToFrontendCoordinates(item.origin, width, height, [
item.data.pose2d.x,
item.data.pose2d.y
]).top
})
console.log('当前数据', testCarList.value)
}
})
.catch((error) => {
console.error(error.message)
})
let width = getElementWidthByClass('indexpage-container')
getImageWidth(imgUrl.value, 'width').then((res) => {
// console.log(res)
let ratioVal = width / res
radio.value = ratioVal
widthVal.value = res
if (pointList.value.length) {
pointList.value.forEach((item) => {
item.radio = radio.value
})
}
if (testCarList.value.length) {
testCarList.value.forEach((item) => {
item.radio = radio.value
})
}
})
getImageWidth(imgUrl.value, 'height').then((res) => {
console.log('高', res)
heightVal.value = res
})
// console.log(width)
}
})
}
const getMapLineList = () => {
MapApi.mapLineListGet({ positionMapId: nowObject.value.id }).then((res) => {
console.log(res)
lineList.value = res
})
}
const getImageSize = (url) => {
return new Promise((resolve, reject) => {
const img = new Image()
img.src = url
// 图片加载成功时触发
img.onload = () => {
const width = img.width
const height = img.height
resolve({ width, height })
}
// 图片加载失败时触发
img.onerror = () => {
reject(new Error(`Failed to load image from URL 获取图片尺寸失败: ${url}`))
}
})
}
const convertToFrontendCoordinates = (origin, width, height, target) => {
// 提取原点的 x 和 y 坐标
const [originX, originY] = origin
// 提取目标点的 x 和 y 坐标
const [targetX, targetY] = target
// 计算目标点相对于原点在 x 方向上的偏移量
const offsetX = targetX - originX
// 计算目标点相对于原点在 y 方向上的偏移量
const offsetY = targetY - originY
// 计算前端页面中的 left 坐标
const left = offsetX
// 计算前端页面中的 top 坐标,需要考虑坐标系的转换
const top = height - offsetY
return {
top,
left,
origin
}
}
const getImageWidth = (imageUrl, name) => {
return new Promise((resolve, reject) => {
const img = new Image()
img.onload = function () {
resolve(img[name])
}
img.onerror = function () {
reject(new Error('图片加载失败'))
}
img.src = imageUrl
})
}
const getElementWidthByClass = (className) => {
const element = document.querySelector(`.${className}`)
if (element) {
// return window.getComputedStyle(element).width
const widthWithUnit = window.getComputedStyle(element).width
return widthWithUnit.slice(0, -2)
}
return null
}
//小车双击
const carDbClick = (item, index) => {
console.log(item)
carDialogRef.value.open(JSON.parse(JSON.stringify(item)))
}
const boxLeft = ref(0)
//获取元素距离左边的距离
const getLeftPx = () => {
let indexpageContainer = document.getElementById('indexpage-container')
console.log('距离左边的距离', indexpageContainer.getBoundingClientRect().left)
boxLeft.value = indexpageContainer.getBoundingClientRect().left + 6
}
// 封装监听元素宽度变化的方法
const observeWidthChange = (id, callback) => {
const targetElement = document.getElementById(id)
if (!targetElement) {
console.error(`未找到 ID 为 ${id} 的元素`)
return
}
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
const newWidth = entry.contentRect.width
callback(newWidth)
}
})
resizeObserver.observe(targetElement)
return () => {
resizeObserver.disconnect()
}
}
let stopObserving
defineExpose({ getMapData }) // 提供 open 方法,用于打开弹窗
onMounted(() => {
carPointListFun()
// getList()
window.addEventListener('resize', computedRatio)
stopObserving = observeWidthChange('indexpage-container', (newWidth) => {
// 在这里执行宽度变化时的自定义操作
// console.log(`元素宽度已变为: ${newWidth}px`);
// 你可以添加更多的操作,比如更新组件的状态等
getLeftPx()
computedRatio()
})
})
onBeforeUnmount(() => {
window.removeEventListener('resize', computedRatio)
})
// 组件卸载时停止监听
onUnmounted(() => {
if (stopObserving) {
stopObserving()
}
})
</script>
<style lang="scss" scoped>
.affix-container {
width: 100%;
position: relative;
overflow: hidden;
background: rgba(0, 0, 0, 0.8);
}
.indexpage-container {
width: 100%;
position: relative;
}
.indexpage-container .affix-container-top {
width: 100%;
height: 80px;
background-color: #fff;
border-bottom: 1px solid #f5f5f5;
}
.indexpage-container-box-point {
width: 100%;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
.indexpage-container-box-point-item {
position: absolute;
cursor: pointer;
}
.scrollbar-flex-content {
padding: 2px 6px;
display: flex;
align-items: center;
flex-wrap: wrap;
}
.scrollbar-demo-item {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
width: 90px;
height: 30px;
margin: 10px 6px;
text-align: center;
border-radius: 4px;
background: var(--el-color-primary-light-9);
color: var(--el-color-primary);
position: relative;
}
.scrollbar-demo-item {
cursor: pointer;
border: none;
}
:focus-visible {
outline: none;
}
.indexpage-container-box {
width: 100%;
position: relative;
}
.indexpage-container-box-img {
width: 100%;
height: auto;
}
.indexpage-container-box-point-item-inner-popover-item {
display: flex;
font-family:
PingFangSC,
PingFang SC;
font-weight: 400;
font-size: 14px;
color: #0d162a;
}
.line-box {
position: absolute;
}
.indexpage-car-item {
position: absolute;
cursor: pointer;
}
.affix-container-left {
position: fixed;
bottom: 26px;
z-index: 999;
// overflow: hidden;
}
.affix-container-left-box {
width: 144px;
// height: 120px;
// background: #ffffff;
}
.affix-container-left-box-item-box {
width: 100%;
border-bottom: 1px solid #eeeeee;
background: #ffffff;
}
.affix-container-left-box-item {
width: 100%;
padding: 0 18px;
height: 42px;
display: flex;
align-items: center;
justify-content: space-between;
}
.affix-container-left-box-item-left {
font-family:
PingFangSC,
PingFang SC;
font-weight: 400;
font-size: 14px;
color: rgba(0, 0, 0, 0.88);
}
.affix-container-left-box-item-img {
cursor: pointer;
flex-shrink: 0;
width: 20px;
height: 12px;
}
.affix-container-left-box-item-bottom {
width: 100%;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
background: #ffffff;
}
.affix-container-left-box-item-bottom-img {
cursor: pointer;
flex-shrink: 0;
width: 12px;
height: 7px;
}
.affix-container-left-box-item-bottom-left {
cursor: pointer;
flex-shrink: 0;
font-family:
PingFangSC,
PingFang SC;
font-weight: 400;
font-size: 16px;
color: #98a4bf;
margin-right: 2px;
}
.affix-container-right {
position: fixed;
z-index: 999;
right: 40px;
bottom: 20px;
display: flex;
align-items: center;
justify-content: flex-end;
}
.affix-container-right-item {
width: 28px;
height: 28px;
cursor: pointer;
flex-shrink: 0;
margin-left: 8px;
}
</style>