1、实时地图svg展示行驶路线、普通dom展示节点库位等节点信息,优化为行驶路线和路径节点由Canvas批量绘制,库位、设备点等需要交互的节点按照初始展示。
2、车辆列表优化加载速度
This commit is contained in:
parent
5f0f52d4ef
commit
7ff85039ba
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<div>
|
||||
<div v-loading="loading">
|
||||
<div class="new-top-box">
|
||||
<div class="new-top-box-left">
|
||||
<div class="new-top-box-left-title"> 车辆看板 </div>
|
||||
@ -125,59 +125,66 @@
|
||||
</div>
|
||||
<div class="item-inner-right-top">
|
||||
<div class="swiper-item-box-top-msg">
|
||||
<el-dropdown>
|
||||
<div style="flex-shrink: 0">
|
||||
<el-icon size="20px"><MoreFilled /></el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
@click="openForm('update', item.id)"
|
||||
v-hasPermi="['carBoard:index:edit']"
|
||||
>编辑</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
@click="clockCar(item)"
|
||||
v-hasPermi="['carBoard:index:lock']"
|
||||
>{{ item.robotTaskModel == 0 ? '解锁' : '锁定' }}</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
@click="deleteCar(item.id)"
|
||||
v-hasPermi="['carBoard:index:delete']"
|
||||
>删除</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
@click="clearCar(item.robotNo)"
|
||||
v-hasPermi="['carBoard:index:clear']"
|
||||
>清除交管</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
@click="recoveryTask(item.robotNo)"
|
||||
v-hasPermi="['carBoard:index:recovery']"
|
||||
>恢复任务</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
v-if="item.isStop != 1"
|
||||
@click="stopCar(item.robotNo)"
|
||||
v-hasPermi="['carBoard:index:stop']"
|
||||
>暂停车辆</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
v-if="item.isStop === 1"
|
||||
@click="recoveryCar(item.robotNo)"
|
||||
v-hasPermi="['carBoard:index:recoveryCar']"
|
||||
>恢复车辆</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
<el-popover placement="bottom" trigger="click" width="160">
|
||||
<template #reference>
|
||||
<div style="flex-shrink: 0; cursor: pointer">
|
||||
<el-icon size="20px"><MoreFilled /></el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<div class="popover-menu">
|
||||
<div
|
||||
class="popover-menu-item"
|
||||
@click="openForm('update', item.id)"
|
||||
v-hasPermi="['carBoard:index:edit']"
|
||||
>编辑</div
|
||||
>
|
||||
<div
|
||||
class="popover-menu-item"
|
||||
@click="clockCar(item)"
|
||||
v-hasPermi="['carBoard:index:lock']"
|
||||
>{{ item.robotTaskModel == 0 ? '解锁' : '锁定' }}</div
|
||||
>
|
||||
<div
|
||||
class="popover-menu-item"
|
||||
@click="deleteCar(item.id)"
|
||||
v-hasPermi="['carBoard:index:delete']"
|
||||
>删除</div
|
||||
>
|
||||
<div
|
||||
class="popover-menu-item"
|
||||
@click="clearCar(item.robotNo)"
|
||||
v-hasPermi="['carBoard:index:clear']"
|
||||
>清除交管</div
|
||||
>
|
||||
<div
|
||||
class="popover-menu-item"
|
||||
@click="recoveryTask(item.robotNo)"
|
||||
v-hasPermi="['carBoard:index:recovery']"
|
||||
>恢复任务</div
|
||||
>
|
||||
<div
|
||||
class="popover-menu-item"
|
||||
v-if="item.isStop != 1"
|
||||
@click="stopCar(item.robotNo)"
|
||||
v-hasPermi="['carBoard:index:stop']"
|
||||
>暂停车辆</div
|
||||
>
|
||||
<div
|
||||
class="popover-menu-item"
|
||||
v-if="item.isStop === 1"
|
||||
@click="recoveryCar(item.robotNo)"
|
||||
v-hasPermi="['carBoard:index:recoveryCar']"
|
||||
>恢复车辆</div
|
||||
>
|
||||
</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-inner">
|
||||
<div class="item-inner-left">
|
||||
<div class="item-inner-left-img-box">
|
||||
<el-image style="width: 100%; height: 100%" :src="item.url" :fit="'fill'" />
|
||||
<!-- <el-image style="width: 100%; height: 100%" :src="item.url" :fit="'fill'" /> -->
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
@ -477,7 +484,7 @@ const stopCar = (robotNo) => {
|
||||
})
|
||||
.then(() => {
|
||||
CarApi.stopCar({ robotNo }).then((res) => {
|
||||
if(res){
|
||||
if (res) {
|
||||
getCarList()
|
||||
fetchData()
|
||||
message.success('暂停成功')
|
||||
@ -495,7 +502,7 @@ const recoveryCar = (robotNo) => {
|
||||
})
|
||||
.then(() => {
|
||||
CarApi.recoveryCar({ robotNo }).then((res) => {
|
||||
if(res){
|
||||
if (res) {
|
||||
getCarList()
|
||||
fetchData()
|
||||
message.success('恢复成功')
|
||||
@ -584,8 +591,6 @@ onBeforeRouteLeave((to, from, next) => {
|
||||
|
||||
.swiperBox {
|
||||
width: 100%;
|
||||
|
||||
/* background: rgba(0, 0, 0, 0.3); */
|
||||
}
|
||||
|
||||
.swiper-item-box {
|
||||
@ -925,4 +930,21 @@ input::input-placeholder {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.popover-menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 120px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
.popover-menu-item {
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.popover-menu-item:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
</style>
|
||||
|
@ -21,33 +21,50 @@
|
||||
<div class="indexpage-container-box">
|
||||
<img :src="imgBgObj.imgUrl" class="map-bg" />
|
||||
<div class="point-information">
|
||||
<div class="line-list" v-if="legendObj.driveLineShow">
|
||||
<svg id="svg" :width="imgBgObj.width" :height="imgBgObj.height">
|
||||
<template v-for="(item, index) in state.mapRouteList" :key="index">
|
||||
<template v-if="item.method == 0">
|
||||
<!-- 直线 -->
|
||||
<line
|
||||
:x1="Number(item.startPointX) * radio"
|
||||
:y1="Number(item.startPointY) * radio"
|
||||
:x2="Number(item.endPointX) * radio"
|
||||
:y2="Number(item.endPointY) * radio"
|
||||
:stroke="item.isSelect ? '#f48924' : '#2d72d9'"
|
||||
:stroke-width="4 * radio"
|
||||
/>
|
||||
</template>
|
||||
<!-- 曲线 -->
|
||||
<template v-else>
|
||||
<path
|
||||
:d="getCurvePath(item)"
|
||||
:stroke="item.isSelect ? '#f48924' : '#2d72d9'"
|
||||
:stroke-width="4 * radio"
|
||||
fill="none"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</svg>
|
||||
<!-- 路线渲染由SVG改为Canvas -->
|
||||
<canvas
|
||||
ref="routeCanvasRef"
|
||||
:width="imgBgObj.width"
|
||||
:height="imgBgObj.height"
|
||||
class="route-canvas"
|
||||
style="position: absolute; left: 0; top: 0; z-index: 1; pointer-events: none"
|
||||
v-show="legendObj.driveLineShow"
|
||||
></canvas>
|
||||
<!-- type=1节点Canvas渲染+自定义tooltip浮层 -->
|
||||
<canvas
|
||||
ref="pointCanvasRef"
|
||||
:width="imgBgObj.width"
|
||||
:height="imgBgObj.height"
|
||||
class="point-canvas"
|
||||
style="position: absolute; left: 0; top: 0; z-index: 2"
|
||||
@mousemove="handlePointCanvasMouseMove"
|
||||
@mouseleave="handlePointCanvasMouseLeave"
|
||||
></canvas>
|
||||
<div
|
||||
v-if="tooltip.visible"
|
||||
:style="{
|
||||
position: 'fixed',
|
||||
left: tooltip.x + 'px',
|
||||
top: tooltip.y + 'px',
|
||||
background: '#fff',
|
||||
border: '1px solid #ebeef5',
|
||||
boxShadow: '0 2px 12px 0 rgba(0,0,0,0.18)',
|
||||
padding: '10px 16px',
|
||||
borderRadius: '6px',
|
||||
zIndex: 9999,
|
||||
pointerEvents: 'none',
|
||||
fontSize: '15px',
|
||||
color: '#303133',
|
||||
minWidth: '100px',
|
||||
maxWidth: '320px',
|
||||
whiteSpace: 'pre-line',
|
||||
transition: 'opacity 0.2s',
|
||||
lineHeight: '1.6',
|
||||
fontFamily: 'PingFang SC, Helvetica Neue, Arial, sans-serif'
|
||||
}"
|
||||
>
|
||||
<div v-html="tooltip.content"></div>
|
||||
</div>
|
||||
|
||||
<!-- 小车 -->
|
||||
<div v-for="(item, index) in testCarList" :key="item.macAddress">
|
||||
<el-popover placement="bottom">
|
||||
@ -70,9 +87,10 @@
|
||||
<div class="popover-robot-no">{{ item.robotNo }}</div>
|
||||
</el-popover>
|
||||
</div>
|
||||
<!-- 其它类型节点继续DOM渲染 -->
|
||||
<div
|
||||
class="map-point-list"
|
||||
v-for="(item, index) in state.allMapPointInfo"
|
||||
v-for="(item, index) in filteredMapPoints"
|
||||
:key="index"
|
||||
:style="{
|
||||
left:
|
||||
@ -299,15 +317,15 @@ import {
|
||||
nextTick,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
onUnmounted
|
||||
onUnmounted,
|
||||
watch,
|
||||
computed
|
||||
} 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'
|
||||
import JSONBigInt from 'json-bigint'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
@ -413,17 +431,40 @@ const filterTypeFun = (deviceType) => {
|
||||
return deviceItem.label
|
||||
}
|
||||
|
||||
const convertActualToBrowser = (pointX, pointY) => {
|
||||
let resolution = Number(imgBgObj.resolution)
|
||||
let origin = imgBgObj.origin
|
||||
// 优化车辆实际坐标到浏览器像素坐标的转换
|
||||
const convertActualCarToBrowser = (pointX, pointY) => {
|
||||
const resolution = Number(imgBgObj.resolution)
|
||||
const [originX, originY] = imgBgObj.origin.map(Number)
|
||||
const imgHeight = Number(imgBgObj.height)
|
||||
const carW = Number(carWidth.value)
|
||||
const carH = Number(carHeight.value)
|
||||
|
||||
const y1 = Number(origin[1]) + Number(imgBgObj.height) * resolution
|
||||
let x = Math.max(Number(pointX) - Number(origin[0]), 0)
|
||||
let y = Math.max(y1 - Number(pointY), 0)
|
||||
// 地图原点在左下角,y轴向上,需转换为浏览器坐标系(左上角,y轴向下)
|
||||
const yBottom = originY + imgHeight * resolution
|
||||
const xPx = Math.max(Number(pointX) - originX, 0) / resolution
|
||||
const yPx = Math.max(yBottom - Number(pointY), 0) / resolution
|
||||
|
||||
// 车辆中心点居中偏移
|
||||
return {
|
||||
x: xPx - carW / resolution / 100 / 2,
|
||||
y: yPx - carH / resolution / 100 / 2
|
||||
}
|
||||
}
|
||||
|
||||
// 传入实际现场的数据,转换成浏览器坐标的数据
|
||||
const convertActualToBrowser = (pointX, pointY) => {
|
||||
const y1 = Number(imgBgObj.origin[1]) + Number(imgBgObj.height) * Number(imgBgObj.resolution)
|
||||
let x =
|
||||
Math.round(
|
||||
(Math.max(Number(pointX) - Number(imgBgObj.origin[0]), 0) / Number(imgBgObj.resolution)) *
|
||||
10000
|
||||
) / 10000
|
||||
let y =
|
||||
Math.round((Math.max(y1 - Number(pointY), 0) / Number(imgBgObj.resolution)) * 10000) / 10000
|
||||
|
||||
return {
|
||||
x: x / resolution - carWidth.value / resolution / 100 / 2,
|
||||
y: y / resolution - carHeight.value / resolution / 100 / 2
|
||||
x,
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
@ -556,13 +597,16 @@ const replaceHttpWithWs = (str) => {
|
||||
// 优化轮询机制
|
||||
const robotListTimerRef = ref(null)
|
||||
const robotByFloorAndAreaList = ref([])
|
||||
const isUnmounted = ref(false)
|
||||
|
||||
const getRobotByFloorAndAreaList = async () => {
|
||||
if (isUnmounted.value) return
|
||||
try {
|
||||
const res = await MapApi.getRobotByFloorAndArea({
|
||||
floor: imgBgObj.floor,
|
||||
area: imgBgObj.area
|
||||
})
|
||||
if (isUnmounted.value) return
|
||||
robotByFloorAndAreaList.value = res
|
||||
if (testCarList.value.length) {
|
||||
// 使用 Set 优化查找性能
|
||||
@ -572,7 +616,9 @@ const getRobotByFloorAndAreaList = async () => {
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取机器人列表失败:', error)
|
||||
if (!isUnmounted.value) {
|
||||
console.error('获取机器人列表失败:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -598,7 +644,7 @@ const initWebsocket = () => {
|
||||
requestAnimationFrame(() => {
|
||||
let data = JSON.parse(jsonMsg.content)
|
||||
// console.log(data)
|
||||
// console.log(dayjs().format('HH:mm:ss SSS'))
|
||||
// console.log(data, dayjs().format('HH:mm:ss SSS'))
|
||||
|
||||
let dataList = Object.entries(data).map(([key, value]) => ({
|
||||
macAddress: key,
|
||||
@ -673,7 +719,7 @@ const mergeArraysWithoutDelete = (arr1, arr2) => {
|
||||
|
||||
// 优化车辆位置计算
|
||||
const processCarData = (car, mapInfo) => {
|
||||
const browserPos = convertActualToBrowser(car.data.pose2d.x, car.data.pose2d.y)
|
||||
const browserPos = convertActualCarToBrowser(car.data.pose2d.x, car.data.pose2d.y)
|
||||
return {
|
||||
...car,
|
||||
originWidth: mapInfo.width,
|
||||
@ -728,8 +774,8 @@ const computedRatio = () => {
|
||||
item.originWidth = imgBgObj.width
|
||||
item.originHeight = imgBgObj.height
|
||||
item.origin = imgBgObj.origin
|
||||
item.realX = convertActualToBrowser(item.data.pose2d.x, item.data.pose2d.y).x
|
||||
item.realY = convertActualToBrowser(item.data.pose2d.x, item.data.pose2d.y).y
|
||||
item.realX = convertActualCarToBrowser(item.data.pose2d.x, item.data.pose2d.y).x
|
||||
item.realY = convertActualCarToBrowser(item.data.pose2d.x, item.data.pose2d.y).y
|
||||
})
|
||||
|
||||
radio.value = width / imgBgObj.width
|
||||
@ -799,6 +845,9 @@ const getAllNodeList = async (positionMapId) => {
|
||||
positionMapId: imgBgObj.positionMapId
|
||||
})
|
||||
state.allMapPointInfo?.forEach((item) => {
|
||||
item.locationX = convertActualToBrowser(item.actualLocationX, item.actualLocationY).x
|
||||
item.locationY = convertActualToBrowser(item.actualLocationX, item.actualLocationY).y
|
||||
|
||||
if (item.type === 1) {
|
||||
item.locationDeep = 40
|
||||
item.locationWide = 40
|
||||
@ -871,6 +920,176 @@ const carDbClick = (item, index) => {
|
||||
carDialogRef.value.open(JSON.parse(JSON.stringify(item)))
|
||||
}
|
||||
|
||||
const routeCanvasRef = ref(null)
|
||||
const pointCanvasRef = ref(null)
|
||||
|
||||
function getPointTooltipContent(item) {
|
||||
// 复用el-tooltip内容逻辑
|
||||
if (item.dataList && item.dataList[0]?.laneName) {
|
||||
return `<div class='indexpage-popover-item'><div>所属线库:</div><div>${item.dataList[0]?.laneName || ''}</div></div>`
|
||||
}
|
||||
if (item.dataList && item.dataList[0]?.areaName) {
|
||||
return `<div class='indexpage-popover-item'><div>所属区域:</div><div>${item.dataList[0]?.areaName || ''}</div></div>`
|
||||
}
|
||||
return `<div class='indexpage-popover-item'><div>节点类型:</div><div>路径点</div><div>${item.sortNum || ''}</div></div>`
|
||||
}
|
||||
|
||||
function handlePointCanvasMouseMove(e) {
|
||||
const canvas = pointCanvasRef.value
|
||||
if (!canvas) return
|
||||
// 直接用canvas自身的rect,避免被菜单栏等父级影响
|
||||
const rect = canvas.getBoundingClientRect()
|
||||
const scrollLeft = canvas.scrollLeft || 0
|
||||
const scrollTop = canvas.scrollTop || 0
|
||||
const scale = typeof isSizeRadio?.value === 'number' ? isSizeRadio.value : 1
|
||||
const mouseX = (e.clientX - rect.left + scrollLeft) / scale
|
||||
const mouseY = (e.clientY - rect.top + scrollTop) / scale
|
||||
const points = state.allMapPointInfo.filter((item) => item.type === 1)
|
||||
let hit = null
|
||||
for (const item of points) {
|
||||
const x = Number(item.locationX) - Number(item.locationWidePx) / 2
|
||||
const y = Number(item.locationY) - Number(item.locationDeepPx) / 2
|
||||
const w = Number(item.locationWidePx)
|
||||
const h = Number(item.locationDeepPx)
|
||||
const cx = x + w / 2
|
||||
const cy = y + h / 2
|
||||
const r = Math.min(w, h) / 2
|
||||
if ((mouseX - cx) ** 2 + (mouseY - cy) ** 2 <= r ** 2) {
|
||||
hit = item
|
||||
break
|
||||
}
|
||||
}
|
||||
if (hit) {
|
||||
tooltip.visible = true
|
||||
const nodeCenterX = Number(hit.locationX)
|
||||
const nodeCenterY = Number(hit.locationY)
|
||||
tooltip.x = nodeCenterX + 10
|
||||
tooltip.y = nodeCenterY - 10
|
||||
tooltip.content = `<div class='indexpage-popover-item'><div>节点编号:<b>${hit.sortNum ?? ''}</b></div></div>`
|
||||
} else {
|
||||
tooltip.visible = false
|
||||
}
|
||||
}
|
||||
function handlePointCanvasMouseLeave() {
|
||||
tooltip.visible = false
|
||||
}
|
||||
|
||||
// 路线Canvas绘制方法
|
||||
const drawRoutesOnCanvas = () => {
|
||||
const canvas = routeCanvasRef.value
|
||||
if (!canvas || !imgBgObj.width || !imgBgObj.height) return
|
||||
const ctx = canvas.getContext('2d')
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
if (!legendObj.driveLineShow) return
|
||||
if (!Array.isArray(state.mapRouteList)) return
|
||||
state.mapRouteList.forEach((item) => {
|
||||
ctx.save()
|
||||
ctx.strokeStyle = item.isSelect ? '#f48924' : '#2d72d9'
|
||||
ctx.lineWidth = 4 * radio.value
|
||||
ctx.beginPath()
|
||||
if (item.method == 0) {
|
||||
// 直线
|
||||
ctx.moveTo(Number(item.startPointX) * radio.value, Number(item.startPointY) * radio.value)
|
||||
ctx.lineTo(Number(item.endPointX) * radio.value, Number(item.endPointY) * radio.value)
|
||||
} else {
|
||||
// 曲线
|
||||
ctx.moveTo(Number(item.startPointX) * radio.value, Number(item.startPointY) * radio.value)
|
||||
ctx.bezierCurveTo(
|
||||
item.beginControlX * radio.value,
|
||||
item.beginControlY * radio.value,
|
||||
item.endControlX * radio.value,
|
||||
item.endControlY * radio.value,
|
||||
Number(item.endPointX) * radio.value,
|
||||
Number(item.endPointY) * radio.value
|
||||
)
|
||||
}
|
||||
ctx.stroke()
|
||||
ctx.restore()
|
||||
})
|
||||
}
|
||||
|
||||
// 只渲染type!==1的节点
|
||||
const filteredMapPoints = computed(() => state.allMapPointInfo.filter((item) => item.type !== 1))
|
||||
|
||||
// type=1节点Canvas自定义tooltip浮层状态
|
||||
const tooltip = reactive({ visible: false, x: 0, y: 0, content: '' })
|
||||
|
||||
// type=1节点Canvas绘制方法
|
||||
const drawPointsOnCanvas = () => {
|
||||
const canvas = pointCanvasRef.value
|
||||
if (!canvas || !imgBgObj.width || !imgBgObj.height) return
|
||||
const ctx = canvas.getContext('2d')
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
if (!Array.isArray(state.allMapPointInfo)) return
|
||||
const points = state.allMapPointInfo.filter((item) => item.type === 1)
|
||||
// 1. 始终绘制所有圆点
|
||||
points.forEach((item) => {
|
||||
const x = (Number(item.locationX) - Number(item.locationWidePx) / 2) * Number(radio.value)
|
||||
const y = (Number(item.locationY) - Number(item.locationDeepPx) / 2) * Number(radio.value)
|
||||
const w = Number(item.locationWidePx) * Number(radio.value)
|
||||
const h = Number(item.locationDeepPx) * Number(radio.value)
|
||||
ctx.save()
|
||||
ctx.beginPath()
|
||||
ctx.arc(x + w / 2, y + h / 2, Math.min(w, h) / 2, 0, 2 * Math.PI)
|
||||
ctx.fillStyle = '#000' // 黑色圆点
|
||||
ctx.globalAlpha = 1
|
||||
ctx.fill()
|
||||
ctx.restore()
|
||||
})
|
||||
// 2. 仅在legendObj.sortNumberShow为true时绘制sortNum文本
|
||||
if (legendObj.sortNumberShow && props.sortNumberShow) {
|
||||
points.forEach((item) => {
|
||||
if (item.sortNum) {
|
||||
const x = (Number(item.locationX) - Number(item.locationWidePx) / 2) * Number(radio.value)
|
||||
const y = (Number(item.locationY) - Number(item.locationDeepPx) / 2) * Number(radio.value)
|
||||
const w = Number(item.locationWidePx) * Number(radio.value)
|
||||
const h = Number(item.locationDeepPx) * Number(radio.value)
|
||||
ctx.save()
|
||||
ctx.font = '13px sans-serif'
|
||||
ctx.fillStyle = '#000' // 黑色文本
|
||||
ctx.textAlign = 'center'
|
||||
ctx.textBaseline = 'top'
|
||||
ctx.globalAlpha = 1
|
||||
ctx.fillText(
|
||||
String(item.sortNum),
|
||||
x + w / 2,
|
||||
y + h / 2 + Math.min(w, h) / 2 + 4 // 圆点下方4px
|
||||
)
|
||||
ctx.restore()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 监听依赖变化自动重绘路线
|
||||
watch(
|
||||
[() => state.mapRouteList, () => radio.value, () => imgBgObj.width, () => imgBgObj.height],
|
||||
() => {
|
||||
nextTick(() => drawRoutesOnCanvas())
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 监听依赖变化自动重绘type=1节点
|
||||
watch(
|
||||
[
|
||||
() => state.allMapPointInfo,
|
||||
() => radio.value,
|
||||
() => imgBgObj.width,
|
||||
() => imgBgObj.height,
|
||||
() => legendObj.sortNumberShow
|
||||
],
|
||||
() => {
|
||||
nextTick(() => drawPointsOnCanvas())
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 地图数据加载后、缩放/拖拽后也需重绘
|
||||
onMounted(() => {
|
||||
nextTick(() => drawRoutesOnCanvas())
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('fullscreenchange', handleFullscreenChange)
|
||||
document.addEventListener('webkitfullscreenchange', handleFullscreenChange)
|
||||
@ -879,16 +1098,22 @@ onMounted(() => {
|
||||
|
||||
// 设置轮询间隔
|
||||
robotListTimerRef.value = setInterval(() => {
|
||||
if (document.hidden) return // 页面不可见时暂停轮询
|
||||
if (document.hidden || isUnmounted.value) return // 页面不可见或已卸载时暂停轮询
|
||||
getRobotByFloorAndAreaList()
|
||||
}, 10000)
|
||||
|
||||
// 监听页面可见性变化
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden && robotListTimerRef.value === null) {
|
||||
robotListTimerRef.value = setInterval(getRobotByFloorAndAreaList, 10000)
|
||||
const visibilityHandler = () => {
|
||||
if (!document.hidden && robotListTimerRef.value === null && !isUnmounted.value) {
|
||||
robotListTimerRef.value = setInterval(() => {
|
||||
if (document.hidden || isUnmounted.value) return
|
||||
getRobotByFloorAndAreaList()
|
||||
}, 10000)
|
||||
}
|
||||
})
|
||||
}
|
||||
document.addEventListener('visibilitychange', visibilityHandler)
|
||||
// 保存handler用于卸载时移除
|
||||
window.__indexPageVisibilityHandler = visibilityHandler
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
@ -896,10 +1121,15 @@ onUnmounted(() => {
|
||||
document.removeEventListener('webkitfullscreenchange', handleFullscreenChange)
|
||||
document.removeEventListener('mozfullscreenchange', handleFullscreenChange)
|
||||
document.removeEventListener('MSFullscreenChange', handleFullscreenChange)
|
||||
|
||||
if (window.__indexPageVisibilityHandler) {
|
||||
document.removeEventListener('visibilitychange', window.__indexPageVisibilityHandler)
|
||||
delete window.__indexPageVisibilityHandler
|
||||
}
|
||||
isUnmounted.value = true
|
||||
clearTheTimer()
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
isUnmounted.value = true
|
||||
clearTheTimer()
|
||||
})
|
||||
|
||||
@ -934,6 +1164,7 @@ defineExpose({ getMapData, computedRatio, clearTheTimer }) // 提供 open 方法
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
.index-page-container {
|
||||
position: relative;
|
||||
@ -972,6 +1203,7 @@ defineExpose({ getMapData, computedRatio, clearTheTimer }) // 提供 open 方法
|
||||
.map-bg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1007,6 +1239,7 @@ defineExpose({ getMapData, computedRatio, clearTheTimer }) // 提供 open 方法
|
||||
cursor: pointer;
|
||||
transform: translateZ(0);
|
||||
will-change: transform;
|
||||
z-index: 9;
|
||||
|
||||
.sort-num {
|
||||
position: absolute;
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" title="库位信息" width="660" class="task-dialog">
|
||||
<Dialog v-model="dialogVisible" title="库位信息" width="800" class="task-dialog">
|
||||
<div class="store-dialog">
|
||||
<div class="store-dialog-left">
|
||||
<div
|
||||
@ -41,7 +41,7 @@
|
||||
{{ item.locationNo || '--' }}
|
||||
</div>
|
||||
</div>
|
||||
<el-form label-width="auto" ref="formRef">
|
||||
<el-form label-width="auto" ref="formRef" style="width: 360px">
|
||||
<el-form-item label="库位编号">
|
||||
<el-input v-model="floorList[selectIndex].locationNo" :disabled="true" />
|
||||
</el-form-item>
|
||||
@ -137,7 +137,7 @@ const { push } = useRouter()
|
||||
flex-shrink: 0;
|
||||
|
||||
.store-dialog-left-item {
|
||||
width: 230px;
|
||||
width: 320px;
|
||||
height: 56px;
|
||||
line-height: 56px;
|
||||
text-align: center;
|
||||
@ -151,6 +151,10 @@ const { push } = useRouter()
|
||||
cursor: pointer;
|
||||
border: 1px solid rgba(0, 0, 0, 0);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
-o-text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
|
@ -1110,6 +1110,9 @@ const inputBoxRef = ref() //文字输入框
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
// loading实例全局变量,确保在<script setup>顶层声明
|
||||
let loading = null
|
||||
|
||||
//其他节点的样式
|
||||
const nodeStyle = (item, index) => {
|
||||
return {
|
||||
@ -4037,8 +4040,8 @@ const getAllNodeList = async () => {
|
||||
state.currentIndex = 0
|
||||
list.forEach((item) => {
|
||||
item.layerSelectionShow = true //用于图层
|
||||
item.locationX = Number(item.locationX)
|
||||
item.locationY = Number(item.locationY)
|
||||
item.locationX = convertActualToBrowser(item.actualLocationX, item.actualLocationY).x
|
||||
item.locationY = convertActualToBrowser(item.actualLocationX, item.actualLocationY).y
|
||||
|
||||
if (item.type === 1) {
|
||||
item.dataObj = {}
|
||||
@ -4408,8 +4411,13 @@ const disposeEventPoint = (x, y) => {
|
||||
// 传入实际现场的数据,转换成浏览器坐标的数据
|
||||
const convertActualToBrowser = (pointX, pointY) => {
|
||||
const y1 = Number(imgBgObj.origin[1]) + Number(imgBgObj.height) * Number(imgBgObj.resolution)
|
||||
let x = Math.max(Number(pointX) - Number(imgBgObj.origin[0]), 0) / Number(imgBgObj.resolution)
|
||||
let y = Math.max(y1 - Number(pointY), 0) / Number(imgBgObj.resolution)
|
||||
let x =
|
||||
Math.round(
|
||||
(Math.max(Number(pointX) - Number(imgBgObj.origin[0]), 0) / Number(imgBgObj.resolution)) *
|
||||
10000
|
||||
) / 10000
|
||||
let y =
|
||||
Math.round((Math.max(y1 - Number(pointY), 0) / Number(imgBgObj.resolution)) * 10000) / 10000
|
||||
|
||||
return {
|
||||
x,
|
||||
@ -4578,9 +4586,23 @@ onBeforeRouteLeave((to, from) => {
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getMapList()
|
||||
getCheckDistance()
|
||||
onMounted(async () => {
|
||||
// 页面全局loading,所有初始化接口完成后关闭
|
||||
loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: '加载中,请稍候...',
|
||||
background: 'rgba(255, 255, 255, 0.7)'
|
||||
})
|
||||
try {
|
||||
await Promise.all([getMapList(), getCheckDistance()])
|
||||
} catch (e) {
|
||||
// 可选:弹出错误提示
|
||||
message.error(e?.message || '页面加载失败')
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
loading.close()
|
||||
}, 1000)
|
||||
}
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
})
|
||||
|
||||
|
@ -1,101 +1,105 @@
|
||||
<template>
|
||||
<div class="view-box">
|
||||
<div id="toolbar">对齐时出现辅助线</div>
|
||||
<div class="container">
|
||||
<VueDragResizeRotate
|
||||
:w="200"
|
||||
:h="200"
|
||||
:parent="true"
|
||||
:debug="false"
|
||||
:min-width="200"
|
||||
:min-height="200"
|
||||
:isConflictCheck="true"
|
||||
:snap="true"
|
||||
:snapTolerance="10"
|
||||
@ref-line-params="getRefLineParams"
|
||||
style="background-color: rgb(174, 213, 129)"
|
||||
/>
|
||||
<VueDragResizeRotate
|
||||
:w="200"
|
||||
:h="200"
|
||||
:parent="true"
|
||||
:x="210"
|
||||
:debug="false"
|
||||
:min-width="200"
|
||||
:min-height="200"
|
||||
:isConflictCheck="true"
|
||||
:snap="true"
|
||||
:snapTolerance="10"
|
||||
@ref-line-params="getRefLineParams"
|
||||
style="background-color: rgb(129, 212, 250)"
|
||||
/>
|
||||
<VueDragResizeRotate
|
||||
:w="200"
|
||||
:h="200"
|
||||
:parent="true"
|
||||
:x="420"
|
||||
:debug="false"
|
||||
:min-width="200"
|
||||
:min-height="200"
|
||||
:isConflictCheck="true"
|
||||
:snap="true"
|
||||
:snapTolerance="10"
|
||||
@ref-line-params="getRefLineParams"
|
||||
style="background-color: rgb(239, 154, 154)"
|
||||
/>
|
||||
<span
|
||||
class="ref-line v-line"
|
||||
v-for="(item, index) in vLine"
|
||||
:key="'v_' + index"
|
||||
v-show="item.display"
|
||||
:style="{
|
||||
left: item.position,
|
||||
top: item.origin,
|
||||
height: item.lineLength
|
||||
}"
|
||||
></span>
|
||||
<span
|
||||
class="ref-line h-line"
|
||||
v-for="(item, index) in hLine"
|
||||
:key="'h_' + index"
|
||||
:style="{
|
||||
top: item.position,
|
||||
left: item.origin,
|
||||
width: item.lineLength
|
||||
}"
|
||||
></span>
|
||||
<div class="map-container">
|
||||
<div
|
||||
ref="fullscreenElement"
|
||||
class="fullscreen-content"
|
||||
:class="{ 'is-fullscreen': isFullscreen }"
|
||||
>
|
||||
<div
|
||||
v-for="car in cars"
|
||||
:key="car.id"
|
||||
class="car"
|
||||
:style="{ left: car.x + 'px', top: car.y + 'px' }"
|
||||
>
|
||||
<img src="@/assets/imgs/indexPage/car1.png" alt="car" class="car-image" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
const vLine = ref([])
|
||||
const hLine = ref([])
|
||||
const fullscreenElement = ref(null)
|
||||
const isFullscreen = ref(false)
|
||||
|
||||
const getRefLineParams = (params) => {
|
||||
const { vLine: newVLine, hLine: newHLine } = params
|
||||
vLine.value = newVLine
|
||||
hLine.value = newHLine
|
||||
// 创建20辆车的数据
|
||||
const cars = ref(
|
||||
Array.from({ length: 100 }, (_, index) => ({
|
||||
id: index + 1,
|
||||
x: Math.random() * 1600, // 初始x坐标
|
||||
y: Math.random() * 900, // 初始y坐标
|
||||
dx: (Math.random() - 0.5) * 6, // x方向速度
|
||||
dy: (Math.random() - 0.5) * 6 // y方向速度
|
||||
}))
|
||||
)
|
||||
|
||||
// 更新车辆位置
|
||||
let updateInterval
|
||||
const updateCarPositions = () => {
|
||||
cars.value = cars.value.map((car) => {
|
||||
// 更新位置
|
||||
let newX = car.x + car.dx
|
||||
let newY = car.y + car.dy
|
||||
|
||||
// 边界检查
|
||||
if (newX <= 0 || newX >= 1600) car.dx *= -1
|
||||
if (newY <= 0 || newY >= 900) car.dy *= -1
|
||||
|
||||
return {
|
||||
...car,
|
||||
x: newX,
|
||||
y: newY
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 每100毫秒更新一次,即每秒10次
|
||||
updateInterval = setInterval(updateCarPositions, 200)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清除定时器
|
||||
if (updateInterval) {
|
||||
clearInterval(updateInterval)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
width: 1200px;
|
||||
.map-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.fullscreen-content {
|
||||
width: 1600px;
|
||||
height: 800px;
|
||||
background-color: beige;
|
||||
background-color: #f0f0f0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ref-line {
|
||||
|
||||
.car {
|
||||
position: absolute;
|
||||
background-color: rgb(219, 89, 110);
|
||||
z-index: 9999;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.v-line {
|
||||
width: 1px;
|
||||
|
||||
.car-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
.h-line {
|
||||
height: 1px;
|
||||
|
||||
/* 全屏模式下的样式 */
|
||||
.fullscreen-content:fullscreen,
|
||||
.fullscreen-content:-webkit-full-screen,
|
||||
.fullscreen-content:-moz-full-screen,
|
||||
.fullscreen-content:-ms-fullscreen {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,151 +0,0 @@
|
||||
export default {
|
||||
list: [
|
||||
{
|
||||
mediaId: 'r6ryvl6LrxBU0miaST4Y-q-G9pdsmZw0OYG4FzHQkKfpLfEwIH51wy2bxisx8PvW',
|
||||
content: {
|
||||
newsItem: [
|
||||
{
|
||||
title: '我是标题(OOO)',
|
||||
author: '我是作者',
|
||||
digest: '我是摘要',
|
||||
content: '我是内容',
|
||||
contentSourceUrl: 'https://www.iocoder.cn',
|
||||
thumbMediaId: 'r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn',
|
||||
showCoverPic: 0,
|
||||
needOpenComment: 0,
|
||||
onlyFansCanComment: 0,
|
||||
url: 'http://mp.weixin.qq.com/s?__biz=MzA3NjM4MzQzOQ==&tempkey=MTIxMl9XaFphcmtJVFh3VEc4Q1MxQWwxQ3R5R0JGTXBDM1Q0N2ZFQm8zeUphOFlwNEpXSWxTYm9RQnJ6cHVuN2QxTE56SFBCYXc2RE9NcUxIeS1CQjJuUHhTWjBlN2VOeGRpRi1fZUhwN1FNQjdrQV9yRU9EU0hibHREZmZoVW5acnZrN3ZjaWsxejR3RGpKczBzTHFIM0dFNFZWVkpBc0dWWlAzUEhlVmpnfn4%3D&chksm=1f6354802814dd969ef83c0f3babe555c614270b30bc383beaf7ffd13b0257f0fe5ced9af694#rd',
|
||||
thumbUrl:
|
||||
'http://test.yudao.iocoder.cn/r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn.png'
|
||||
},
|
||||
{
|
||||
title: '我是标题(XXX)',
|
||||
author: '我是作者',
|
||||
digest: '我是摘要',
|
||||
content: '我是内容',
|
||||
contentSourceUrl: 'https://www.iocoder.cn',
|
||||
thumbMediaId: 'r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn',
|
||||
showCoverPic: 0,
|
||||
needOpenComment: 0,
|
||||
onlyFansCanComment: 0,
|
||||
url: 'http://mp.weixin.qq.com/s?__biz=MzA3NjM4MzQzOQ==&tempkey=MTIxMl9yTlYwOEs1clpwcE5OUEhCQWwxQ3R5R0JGTXBDM1Q0N2ZFQm8zeUphOFlwNEpXSWxTYm9RQnJ6cHVuN0NSMjFqN3N1aUZMbFNVLTZHN2ZDME9qOGp2THk2RFNlSTlKZ3Y1czFVZDdQQm5IeUg3dEppSUtpQUh5SExOOTRkT3dHNUdBdHdWSWlOendlREV3dS1jUEVQbFpiVTZmVW5iRWhZcGdkNTFRfn4%3D&chksm=1f6354802814dd96a403151cd44c7da4eecf0e475d25423e46ecd795b513bafd829a75daef9b#rd',
|
||||
thumbUrl:
|
||||
'http://test.yudao.iocoder.cn/r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn.png'
|
||||
}
|
||||
]
|
||||
},
|
||||
updateTime: 1673655730
|
||||
},
|
||||
{
|
||||
mediaId: 'r6ryvl6LrxBU0miaST4Y-jGpXnO73ihN0lsNXknCRQHapp2xgHMRxHKG50LituFe',
|
||||
content: {
|
||||
newsItem: [
|
||||
{
|
||||
title: '我是标题(修改)',
|
||||
author: '我是作者',
|
||||
digest: '我是摘要',
|
||||
content: '我是内容',
|
||||
contentSourceUrl: 'https://www.iocoder.cn',
|
||||
thumbMediaId: 'r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn',
|
||||
showCoverPic: 0,
|
||||
needOpenComment: 0,
|
||||
onlyFansCanComment: 0,
|
||||
url: 'http://mp.weixin.qq.com/s?__biz=MzA3NjM4MzQzOQ==&tempkey=MTIxMl95WVFXYndIZnZJd0t5cjgvQWwxQ3R5R0JGTXBDM1Q0N2ZFQm8zeUphOFlwNEpXSWxTYm9RQnJ6cHVuN1dlNURPbWswbEF4RDd5dVJTdjQ4cm9Cc0Q1TWhpMUh6SE1hVEE3ZHljaHhlZjZYSGF5N2JNSHpDTlh6ajNZbkpGTGpTcUQ4M3NMdW41ZUpXNFZZQ1VKbVlaMVp5ekxEV1czREdsY1dOYTZnfn4%3D&chksm=1f6354be2814dda8e6238037c2ebd52b1c8e80e93249a861ad80e4d40e5ca7207233475ca689#rd',
|
||||
thumbUrl:
|
||||
'http://test.yudao.iocoder.cn/r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn.png'
|
||||
}
|
||||
]
|
||||
},
|
||||
updateTime: 1673655584
|
||||
},
|
||||
{
|
||||
mediaId: 'r6ryvl6LrxBU0miaST4Y-v5SrbNCPpD6M_p3TmSrYwTjKogs-0DMJgmjMyNZPeMO',
|
||||
content: {
|
||||
newsItem: [
|
||||
{
|
||||
title: '1321',
|
||||
author: '3232',
|
||||
digest: '1333',
|
||||
content: '<p>444</p>',
|
||||
contentSourceUrl: 'http://www.iocoder.cn',
|
||||
thumbMediaId: 'r6ryvl6LrxBU0miaST4Y-tlQmcl3RdC-Jcgns6IQtf7zenGy3b86WLT7GzUcrb1T',
|
||||
showCoverPic: 0,
|
||||
needOpenComment: 0,
|
||||
onlyFansCanComment: 0,
|
||||
url: 'http://mp.weixin.qq.com/s?__biz=MzA3NjM4MzQzOQ==&tempkey=MTIxMl9jelJiaDAzbmdpSkJOZ2M2QWwxQ3R5R0JGTXBDM1Q0N2ZFQm8zeUphOFlwNEpXSWxTYm9RQnJ6cHVuNDNXVVc2ZDRYeTY0Zm1weXR6dE9vQWh1TzEwbEpUVnRfVzJyaGFDNXBkZ0ZXM2JFOTNaRHNhOHRUeFdEanhMeS01X01kMUNWQ1BpRER3cjYwTl9pMnpFLUJhZXFucVVfM1pDUXlTUEl1S25nfn4%3D&chksm=1f6354bc2814ddaa56a90ad5bc3d078601c8d1589ba01827a8170587bc830ff9747b5f59c3a0#rd',
|
||||
thumbUrl:
|
||||
'http://mmbiz.qpic.cn/mmbiz_png/btUmCVHwbJUoicwBiacjVeQbu6QxgBVrukfSJXz509boa21SpH8OVHAqXCJiaiaAaHQJNxwwsa0gHRXVr0G5EZYamw/0?wx_fmt=png'
|
||||
}
|
||||
]
|
||||
},
|
||||
updateTime: 1673628969
|
||||
},
|
||||
{
|
||||
mediaId: 'r6ryvl6LrxBU0miaST4Y-vdWrisK5EZbk4Y3tzh8P0PG0eEUbnQrh0BcsEb3WNP0',
|
||||
content: {
|
||||
newsItem: [
|
||||
{
|
||||
title: 'tudou',
|
||||
author: 'haha',
|
||||
digest: '312',
|
||||
content: '<p>132312</p>',
|
||||
contentSourceUrl: 'http://www.iocoder.cn',
|
||||
thumbMediaId: 'r6ryvl6LrxBU0miaST4Y-pgFtUNLu1foMSAMkoOsrQrTZ8EtTMssBLfTtzP0dfjG',
|
||||
showCoverPic: 0,
|
||||
needOpenComment: 0,
|
||||
onlyFansCanComment: 0,
|
||||
url: 'http://mp.weixin.qq.com/s?__biz=MzA3NjM4MzQzOQ==&tempkey=MTIxMl9qdkJ1ZjBoUmg2Uk9TS3RlQWwxQ3R5R0JGTXBDM1Q0N2ZFQm8zeUphOFlwNEpXSWxTYm9RQnJ6cHVuNVg2aTJsaC1fMkU2eXNacUplN3VDTTZFZkhtMjhuTUZvWkxsNDBRSXExY2tiVXRHb09TaHgtREhzY3doZ0JYeC1TSTZ5eWZldXJsOWtfbV8yMi1aYkcyZ2pOY0haM0Ntb3VSWEtxUGVFRlNBfn4%3D&chksm=1f6354ba2814ddacf0184b24d310483641ef190b1faac098c285eb416c70017e2f54decfa1af#rd',
|
||||
thumbUrl:
|
||||
'http://test.yudao.iocoder.cn/r6ryvl6LrxBU0miaST4Y-pgFtUNLu1foMSAMkoOsrQrTZ8EtTMssBLfTtzP0dfjG.png'
|
||||
}
|
||||
]
|
||||
},
|
||||
updateTime: 1673628760
|
||||
},
|
||||
{
|
||||
mediaId: 'r6ryvl6LrxBU0miaST4Y-u9kTIm1DhWZDdXyxsxUVv2Z5DAB99IPxkIRTUUD206k',
|
||||
content: {
|
||||
newsItem: [
|
||||
{
|
||||
title: '12',
|
||||
author: '333',
|
||||
digest: '123',
|
||||
content: '123',
|
||||
contentSourceUrl: 'https://www.iocoder.cn',
|
||||
thumbMediaId: 'r6ryvl6LrxBU0miaST4Y-jVixJGgnBnkBPRbuVptOW0CHYuQFyiOVNtamctS8xU8',
|
||||
showCoverPic: 0,
|
||||
needOpenComment: 0,
|
||||
onlyFansCanComment: 0,
|
||||
url: 'http://mp.weixin.qq.com/s?__biz=MzA3NjM4MzQzOQ==&tempkey=MTIxMl9qVVhpSDZUaFJWTzBBWWRVQWwxQ3R5R0JGTXBDM1Q0N2ZFQm8zeUphOFlwNEpXSWxTYm9RQnJ6cHVuNWRnTDJWYmF2NER0clV1bThmQ0xUR3hqQnJkZ3BJSUNmNDJmc0lCZ1dadkVnZ3Z5bkN4YWtVUjhoaWZWYzZURUR4NnpMd0Y4Z3U5aUdib0lkMzI4Rjg3SG9JX2FycTMxbUctOHplaTlQVVhnfn4%3D&chksm=1f6354b62814dda076c778af33f06580165d8aa81f7798d55cfabb1886b5c74d9b2124a3535c#rd',
|
||||
thumbUrl:
|
||||
'http://test.yudao.iocoder.cn/r6ryvl6LrxBU0miaST4Y-jVixJGgnBnkBPRbuVptOW0CHYuQFyiOVNtamctS8xU8.jpg'
|
||||
}
|
||||
]
|
||||
},
|
||||
updateTime: 1673626494
|
||||
},
|
||||
{
|
||||
mediaId: 'r6ryvl6LrxBU0miaST4Y-sO24upobaENDmeByfBTfaozB3aOqSMAV0lGy-UkHXE7',
|
||||
content: {
|
||||
newsItem: [
|
||||
{
|
||||
title: '我是标题',
|
||||
author: '我是作者',
|
||||
digest: '我是摘要',
|
||||
content: '我是内容',
|
||||
contentSourceUrl: 'https://www.iocoder.cn',
|
||||
thumbMediaId: 'r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn',
|
||||
showCoverPic: 0,
|
||||
needOpenComment: 0,
|
||||
onlyFansCanComment: 0,
|
||||
url: 'http://mp.weixin.qq.com/s?__biz=MzA3NjM4MzQzOQ==&tempkey=MTIxMl9LT2dqRnpMNUpsR0hjYWtBQWwxQ3R5R0JGTXBDM1Q0N2ZFQm8zeUphOFlwNEpXSWxTYm9RQnJ6cHVuNGNmazZTdlE5WkxvU0tfX2V5cjV2WjJiR0xjQUhyREFSZWo2eWNrUW9EYVh6ZkpWRXBLR3FmTEV6YldBMno3Q2ZvVXBSdzlaVDc3aFhndEpQWUwzWmFMUWt0YVVURE1VZ1FsQTdPMlRtc3JBfn4%3D&chksm=1f6354aa2814ddbcc2637382f963a8742993ac38ebcebe6e3411df5ac82ac7bbdb391be6494a#rd',
|
||||
thumbUrl:
|
||||
'http://test.yudao.iocoder.cn/r6ryvl6LrxBU0miaST4Y-pIcmK-zAAId-9TGgy-DrSLhjVuWbuT3ZBjk9K1yQ0Dn.png'
|
||||
}
|
||||
]
|
||||
},
|
||||
updateTime: 1673534279
|
||||
}
|
||||
],
|
||||
total: 6
|
||||
}
|
Loading…
Reference in New Issue
Block a user