forked from drone/command-center-dashboard

chenyao
2025-04-14 4801a4a6f26d62e3c0aa92332805d676ee918db3
Merge branch 'master' of http://139.196.74.78:10010/r/drone/command-center-dashboard
13 files modified
3 files deleted
2 files added
974 ■■■■ changed files
src/components/LiveVideo.vue 9 ●●●● patch | view | raw | blame | history
src/styles/element-ui.scss 56 ●●●●● patch | view | raw | blame | history
src/utils/cesium/mapUtil.js 19 ●●●●● patch | view | raw | blame | history
src/views/Home/EventOverviewDetail/EventOverviewDetailRight.vue 16 ●●●●● patch | view | raw | blame | history
src/views/Home/HomeLeft/components/MachineNestTotal.vue 2 ●●● patch | view | raw | blame | history
src/views/Home/RSide.vue 177 ●●●● patch | view | raw | blame | history
src/views/SignMachineNest/MachineRight/MachineMonitor.vue 2 ●●● patch | view | raw | blame | history
src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceEvent.vue 46 ●●●●● patch | view | raw | blame | history
src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceJob/DeviceJob.vue 9 ●●●● patch | view | raw | blame | history
src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceJob/DeviceJobDetails/DeviceJobDetailsMap.vue 51 ●●●●● patch | view | raw | blame | history
src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/MachineTableDetails.vue 74 ●●●● patch | view | raw | blame | history
src/views/SignMachineNest/SignMachineNest.vue 2 ●●● patch | view | raw | blame | history
src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails.vue 134 ●●●●● patch | view | raw | blame | history
src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails/CurrentTaskDetails.vue 104 ●●●●● patch | view | raw | blame | history
src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails/RealTimeMap.vue 175 ●●●●● patch | view | raw | blame | history
src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue 17 ●●●●● patch | view | raw | blame | history
src/views/TaskManage/TaskIntermediateContent/initPlanarWayline.js patch | view | raw | blame | history
src/views/TaskManage/TaskIntermediateContent/initPointWayline.js 81 ●●●●● patch | view | raw | blame | history
src/components/LiveVideo.vue
@@ -20,6 +20,7 @@
let liveVideoRef = ref(null);
const play = (url) => {
    pause();
  webrtcPlayer = new window.ZLMRTCClient.Endpoint({
    element: liveVideoRef.value, // video 标签
    debug: true, // 是否打印日志
@@ -73,6 +74,7 @@
    )
};
// 销毁
const pause = () => {
  if (webrtcPlayer) {
    webrtcPlayer.close();
@@ -84,7 +86,6 @@
  (newValue) => {
    if (!newValue) return;
    nextTick(() => {
      pause();
      play(newValue);
    });
  },
@@ -92,6 +93,10 @@
    immediate: true
  }
);
onBeforeUnmount(() => {
    pause()
})
onMounted(() => {
  if (props.videoUrl) {
@@ -113,4 +118,4 @@
    object-fit: cover;
  }
}
</style>
</style>
src/styles/element-ui.scss
@@ -167,7 +167,7 @@
    color: var(--text-color);
  }
  .el-select__wrapper {
    background-color: transparent;
    background-color: #021022;
    box-shadow: none;
    border: var(--border);
  }
@@ -270,3 +270,57 @@
    }
  }
}
// 弹框-dialog
.ztzf-dialog {
    background: #0f1929;
    box-shadow: inset 0px -50px 50px 0px rgba(27, 148, 255, 0.13);
    border-radius: 20px 0px 0px 0px;
    border: 2px solid;
    padding: 0 !important;
    border-image: linear-gradient(
            180deg,
            rgba(81, 168, 255, 0),
            rgba(48, 111, 202, 1),
            rgba(255, 255, 255, 1),
            rgba(27, 148, 255, 1)
        )
        2 2;
  // 头部
  .el-dialog__header {
        width: 100%;
        height: 47px;
        margin-bottom: 14px;
        background: url('/src/assets/images/home/homeLeft/inspection-vector.png') no-repeat center;
        background-size: 100% 100%;
        font-weight: bold;
        font-size: 16px;
        line-height: 47px;
    }
  .el-dialog__title {
        width: 112px;
        height: 19px;
        font-family: Segoe UI, Segoe UI;
        font-weight: bold;
        font-size: 16px;
        line-height: 16px;
        text-shadow: 0px 0px 5px rgba(154, 218, 255, 0.6);
        text-align: left;
        font-style: normal;
        text-transform: none;
        background: linear-gradient(90deg, #fbfdff 0%, #86d4ff 100%);
        margin-left: 16px;
        -webkit-background-clip: text; /* 背景被裁剪成文字的前景色 */
        -webkit-text-fill-color: transparent; /* 文字填充颜色变透明 */
    }
  .el-dialog .el-dialog__header {
        /* margin: 0px !important; */
        padding: 0px !important;
        padding-left: 0px !important;
    }
  .el-scrollbar__thumb {
        background: #13c6ff !important;
    }
}
src/utils/cesium/mapUtil.js
@@ -347,3 +347,22 @@
        return true
    }
}
/**
 * 飞到中心点并且所有点在可视范围
 * @param lngLatArr
 * @param viewer
 * @param rangeMultiple 范围倍数越大飞的越高默认2
 */
export function flyVisual(lngLatArr,viewer,rangeMultiple = 2) {
    if (!Array.isArray(lngLatArr) || lngLatArr.length === 0) return
    const positions = lngLatArr.map(([lon, lat]) =>
        Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat))
    )
    // 计算包围盒 BoundingSphere(所有点的外接球)
    const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
    viewer.camera.flyToBoundingSphere(boundingSphere, {
        duration: 0,
        offset: new Cesium.HeadingPitchRange(0, 0, boundingSphere.radius * rangeMultiple),
    })
}
src/views/Home/EventOverviewDetail/EventOverviewDetailRight.vue
@@ -68,13 +68,7 @@
                <div class="eventListItem" v-for="item in list">
                    <img
                        class="eventListItemImg"
                        :src="
                            item.photo_url
                                ? item.photo_url.substring(0, item.photo_url.lastIndexOf('.')) +
                                  '_small' +
                                  item.photo_url.substring(item.photo_url.lastIndexOf('.'))
                                : ''
                        "
                        :src="getSmallImg(item.photo_url)"
                        alt=""
                    />
                    <div class="eventListItemPosition" :title="item.address" @click="positioning(item)">
@@ -138,6 +132,10 @@
    current: 1,
    size: 8,
})
const getSmallImg = (url) => {
    return url ? url.substring(0, url.lastIndexOf('.')) + '_small' + url.substring(url.lastIndexOf('.')) : ''
}
const isMore = ref(false)
const leftArrowFun = () => {
@@ -234,7 +232,7 @@
    }
    getEventPage(params.value, pageParams).then(res => {
        const resData = res.data.data
        list.value = resData?.records || []
        list.value = (resData?.records || [])
        total.value = resData.total
    })
}
@@ -280,7 +278,7 @@
        .leftArrow {
            position: absolute;
            left: 0;
            left: -11px;
            top: 50%;
            transform: translateY(-50%);
            cursor: pointer;
src/views/Home/HomeLeft/components/MachineNestTotal.vue
@@ -7,7 +7,7 @@
        <div class="name">机巢总数</div>
      </div>
      <div class="status">
        <div class="item" v-for="(item, index) in listNum">
        <div class="item" v-for="(item, index) in listNum" >
          <div>
            <div :style="{ color: item.color }" class="value">{{ item.value }}</div>
            <div class="name">{{ item.name }}</div>
src/views/Home/RSide.vue
@@ -1,35 +1,33 @@
<template>
  <div class="ai-chat">
    <el-popover
      placement="bottom"
      :visible="visible"
      :width="200"
      trigger="click"
    >
      <template #reference>
        <div class="chat" id="chatID" v-drag:chatID  @mousedown="handleMouseDown"
             @mouseup="handleMouseUp"/>
      </template>
      <div>
        快和我对话吧
        <el-input/>
      </div>
    </el-popover>
    <img class="chat-bottom" src="../../assets/images/chat-bottom.png" alt="">
  </div>
  <div class="r-side">
        <img v-for="(item, index) in images"
                 :key="index" :class="item.class"
                 :src="activeIndex === index ? item.activeSrc : item.src" alt=""
                 @click="activeChange(index)"
                 @mouseenter="enterHover(index)"
                 @mouseleave="logIndex=3"
        >
  </div>
  <div v-if="logIndex===0" class="r-side-positioning">返回当前位置</div>
  <div v-if="logIndex===1" class="r-side-measuring">量尺</div>
  <div v-if="logIndex===2" class="r-side-layer">切换地图模式</div>
  <MeasuringDistance v-if="activeIndex === 1"/>
    <div class="ai-chat">
        <el-popover placement="bottom" :visible="visible" :width="200" trigger="click">
            <template #reference>
                <div class="chat" id="chatID" v-drag:chatID @mousedown="handleMouseDown" @mouseup="handleMouseUp" />
            </template>
            <div>
                快和我对话吧
                <el-input />
            </div>
        </el-popover>
        <img class="chat-bottom" src="../../assets/images/chat-bottom.png" alt="" />
    </div>
    <div class="r-side">
        <img
            v-for="(item, index) in images"
            :key="index"
            :class="item.class"
            :src="activeIndex === index ? item.activeSrc : item.src"
            alt=""
            @click="activeChange(index)"
            @mouseenter="enterHover(index)"
            @mouseleave="logIndex = 3"
        />
    </div>
    <div v-if="logIndex === 0" class="r-side-positioning">返回当前位置</div>
    <div v-if="logIndex === 1" class="r-side-measuring">量尺</div>
    <div v-if="logIndex === 2" class="r-side-layer">切换地图模式</div>
    <MeasuringDistance v-if="activeIndex === 1" />
</template>
<script setup>
@@ -43,45 +41,48 @@
import tc1 from '@/assets/images/rSide/tc1.png'
import cesiumOperation from '@/utils/cesium-tsa'
let logIndex = ref(3);
const enterHover = (value) => {
    logIndex.value = value;
let logIndex = ref(3)
const enterHover = value => {
    logIndex.value = value
}
const { flyTo } = cesiumOperation()
const store = useStore();
const currentAreaPosition = computed(() => store.state.home.currentAreaPosition);
const store = useStore()
const currentAreaPosition = computed(() => store.state.home.currentAreaPosition)
let activeIndex = ref(null);
const activeChange = (value) => {
    if(value === 0) {
let activeIndex = ref(null)
const activeChange = value => {
    if (value === 0) {
        flyTo(currentAreaPosition.value, 0, currentAreaPosition.value.height)
    }
    if (value === 1){
        activeIndex.value = activeIndex.value === 1 ? null : value;
    if (value === 1) {
        activeIndex.value = activeIndex.value === 1 ? null : value
    }
    if (value === 2) {
        activeIndex.value = activeIndex.value === 2 ? null : value
    }
}
const visible = ref(false);
let pressStart = 0;
const visible = ref(false)
let pressStart = 0
const handleMouseDown = () => {
  pressStart = Date.now(); // 记录按下时间
};
    pressStart = Date.now() // 记录按下时间
}
const handleMouseUp = () => {
  const pressDuration = Date.now() - pressStart; // 计算按下时长
  if (pressDuration < 150) {
    // 如果按下时间小于200ms,认为是点击
    visible.value = !visible.value;
  }
};
    const pressDuration = Date.now() - pressStart // 计算按下时长
    if (pressDuration < 150) {
        // 如果按下时间小于200ms,认为是点击
        visible.value = !visible.value
    }
}
// 添加: 定义图片数组
const images = [
    { class: 'positioning', src: dw,activeSrc:dw1 },
    { class: 'measuring-scale', src: lc,activeSrc:lc1 },
    { class: 'layer', src: tc,activeSrc:tc1 }
];
    { class: 'positioning', src: dw, activeSrc: dw1 },
    { class: 'measuring-scale', src: lc, activeSrc: lc1 },
    { class: 'layer', src: tc, activeSrc: tc1 },
]
</script>
<style scoped lang="scss">
@@ -106,45 +107,47 @@
  }
}
.r-side {
  position: absolute;
  bottom: 122px;
  right: 463px;
  cursor: pointer;
  img {
    display: block;
    width: 48px;
    height: 48px;
  }
  .positioning {
    position: relative;
    bottom: 24px;
  }
  .measuring-scale {
    position: relative;
    bottom: 12px;
  }
    position: absolute;
    bottom: 122px;
    right: 463px;
    cursor: pointer;
    img {
        display: block;
        width: 48px;
        height: 48px;
    }
    .positioning {
        position: relative;
        bottom: 24px;
    }
    .measuring-scale {
        position: relative;
        bottom: 12px;
    }
}
.r-side-positioning, .r-side-measuring, .r-side-layer {
  position: absolute;
  right: 514px;
  width: 130px;
  height: 48px;
  font-family: Source Han Sans CN, Source Han Sans CN;
  font-weight: 400;
  font-size: 18px;
  color: #FFFFFF;
  line-height: 48px;
  text-align: center;
  background: url('@/assets/images/cursor.png');
.r-side-positioning,
.r-side-measuring,
.r-side-layer {
    position: absolute;
    right: 514px;
    width: 130px;
    height: 48px;
    font-family: Source Han Sans CN, Source Han Sans CN;
    font-weight: 400;
    font-size: 18px;
    color: #ffffff;
    line-height: 48px;
    text-align: center;
    background: url('@/assets/images/cursor.png');
}
.r-side-positioning {
  bottom: 242px;
    bottom: 242px;
}
.r-side-measuring {
  bottom: 182px;
    bottom: 182px;
}
.r-side-layer {
  bottom: 122px;
    bottom: 122px;
}
</style>
src/views/SignMachineNest/MachineRight/MachineMonitor.vue
@@ -78,4 +78,4 @@
    justify-content: space-between;
    padding: 12px 10px 0;
 }
</style>
</style>
src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceEvent.vue
@@ -18,28 +18,22 @@
                </el-table-column>
            <el-table-column prop="id" label="事件编号" />
            <el-table-column prop="event_name" label="事件名称" />
            <el-table-column prop="event_name" label="所属单位 " />
            <el-table-column show-overflow-tooltip prop="media_type" label="事件内容" />
            <el-table-column prop="create_user" label="所属单位 " />
            <el-table-column show-overflow-tooltip prop="remark" label="事件内容" />
            <el-table-column show-overflow-tooltip prop="ai_types" label="关联算法" />
            <el-table-column prop="status" label="事件状态">
                <template #default="scope">
                    <el-tag v-if="scope.row.status === 0" type="info">待处理</el-tag>
                    <el-tag v-if="scope.row.status === 1" type="success">待分拨</el-tag>
                    <el-tag v-if="scope.row.status === 2" type="warning">待处理</el-tag>
                    <el-tag v-if="scope.row.status === 3" type="success">处理中</el-tag>
                    <el-tag v-if="scope.row.status === 4" type="success">已完成</el-tag>
                    <el-tag v-if="scope.row.status === 5" type="success">已完结</el-tag>
                    <div class="pending" v-if="scope.row.status === 0">待处理</div>
                    <div class="reviewed" v-if="scope.row.status === 2">待审核</div>
                    <div class="processing" v-if="scope.row.status === 3">处理中</div>
                    <div class="done" v-if="scope.row.status === 4">已完成</div>
                    <div class="ended" v-if="scope.row.status === 5">已完结</div>
                </template>
            </el-table-column>
            <el-table-column label="操作" width="80">
                <template #default="scope">
                <div class="ztzf-view" @click="distribution(scope.row)">查看</div>
                </template>
                <!-- <template #default="scope" >
                    <el-button type="success" link @click="examine(scope.row)">审核</el-button>
                    <el-button type="primary" link @click="distribution(scope.row)">查看</el-button>
                </template> -->
            </el-table-column>
        </el-table>
        </div>
@@ -49,7 +43,7 @@
            :page-sizes="[10, 20, 30, 50]"
            v-model:current-page="sizeParams.current"
            v-model:page-size="sizeParams.size"
            layout="prev, pager, next, jumper"
            layout="prev, pager, next,sizes, jumper"
            :total="total"
            @change="pageChange"
        />
@@ -145,10 +139,24 @@
    background: center center no-repeat none !important;
    color: #8eb8ea !important;
}
:deep(.el-tag){
background: none !important;
border:none !important
// 待处理
.pending{
color: #FF7411;
}
// 待审核
.reviewed{
color: #8CFEA7;
}
// 处理中
.processing{
color: #FFC398;
}
// 已完成
.done{
color: #AFD9FB;
}
// 已完结
.ended{
color: #11C4FF;
}
</style>
src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceJob/DeviceJob.vue
@@ -28,7 +28,6 @@
                <el-table-column label="操作" width="80">
                    <template #default="scope">
                        <div class="ztzf-view" @click="viewJob(scope.row)">查看</div>
                        <!-- <el-button type="warning" link @click="viewJob(scope.row)">查看</el-button> -->
                    </template>
                </el-table-column>
            </el-table>
@@ -39,7 +38,7 @@
            :page-sizes="[10, 20, 30, 50]"
            v-model:current-page="sizeParams.current"
            v-model:page-size="sizeParams.size"
            layout=" prev, pager, next, jumper"
            layout=" prev, pager, next,sizes, jumper"
            :total="total"
            @change="pageChange"
        />
@@ -96,12 +95,6 @@
    getList()
})
</script>
<style lang="scss">
.devicejob-container {
    // 表格
}
</style>
<style scoped lang="scss">
// 标题
.machineTableDetailsTitle {
src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceJob/DeviceJobDetails/DeviceJobDetailsMap.vue
@@ -8,44 +8,12 @@
import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz'
import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial'
import lineImg from '@/assets/images/arrow-right-blue.png'
import uavImg from '@/assets/images/home/useUavHome/uavImg.png'
import rwqfdImg from '@/assets/images/signMachineNest/rwqfd.png'
import endPointImg from '@/assets/images/EndPointicon.png'
import { addBlueFilter } from '@/utils/cesium/common'
import { flyVisual } from '@/utils/cesium/mapUtil'
const props = defineProps(['detailsData'])
const filterLayer = (options,viewer) => {
    const { bInvertColor, bFilterColor, filterColor } = options
    const color = new Cesium.Color.fromCssColorString(filterColor)
    const filterRGB = [
        Math.round(color.red * 255),
        Math.round(color.green * 255),
        Math.round(color.blue * 255)
    ]
    let fragShader = viewer.scene.globe._surfaceShaderSet.baseFragmentShaderSource.sources
    for (let i = 0; i < fragShader.length; i++) {
        const strS = 'color = czm_saturation(color, textureSaturation);\n#endif\n'
        let strT = 'color = czm_saturation(color, textureSaturation);\n#endif\n'
        if (bInvertColor) {
            strT += `
          color.r = 1.0 - color.r;
          color.g = 1.0 - color.g;
          color.b = 1.0 - color.b;
        `
        }
        if (bFilterColor) {
            strT += `
          color.r = color.r * ${filterRGB[0]}.0/255.0;
          color.g = color.g * ${filterRGB[1]}.0/255.0;
          color.b = color.b * ${filterRGB[2]}.0/255.0;
        `
        }
        fragShader[i] = fragShader[i].replace(strS, strT)
    }
}
const imageryProvider_ammapSL = new Cesium.UrlTemplateImageryProvider({
    url: 'https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
@@ -131,20 +99,6 @@
    })
}
// 飞到中心点
function flyToPoints(lngLatArr) {
    if (!Array.isArray(lngLatArr) || lngLatArr.length === 0) return
    const positions = lngLatArr.map(([lon, lat]) =>
        Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat))
    )
    // 计算包围盒 BoundingSphere(所有点的外接球)
    const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
    viewer.camera.flyToBoundingSphere(boundingSphere, {
        duration: 0,
        offset: new Cesium.HeadingPitchRange(0, 0, boundingSphere.radius * 2),
    })
}
// 异步解析kmz文件
const analysis = async url => {
    return new Promise(async resolve => {
@@ -160,11 +114,10 @@
const drawLine = async () => {
    const res = await Promise.all(props.detailsData.way_lines.map(item => analysis(item.url)))
    res.map(item => renderingLine(item))
    console.log(res,'jiexi')
    const allPoint = res
        .flatMap(item => item.Placemark)
        .map(item => item.Point.coordinates.split(','))
    flyToPoints(allPoint)
    flyVisual(allPoint,viewer)
}
const removeMap = () => {
src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/MachineTableDetails.vue
@@ -1,12 +1,11 @@
<!-- 机巢列表详情 -->
<template>
    <el-dialog
        class="machineTableDetails"
        class="machineTableDetails ztzf-dialog"
        v-model="isShowDetails"
        :width="pxToRem(1500)"
        :close-on-click-modal="false"
        :destroy-on-close="true"
    >
        <template #header="{ titleId, titleClass }">
            <div class="my-header">
@@ -18,25 +17,28 @@
            <div class="machineTableDetailsTitle"><span>详情</span></div>
            <div class="infoBox">
                <div class="itemBox" v-for="(item, index) in infoList" :key="index">
                    <div class="itemTitle">{{ item.name }} : </div>
                    <div class="itemTitle">{{ item.name }} :</div>
                    <div v-if="item.name == '任务成果'" class="missionOutcomes">
                        <span>{{item.value ? item.value :0}}</span>
                        个
                    </div>
                    <div
                        v-if="item.name == '机巢状态'"
                        :class="{
                            active: item.value === 'WORKING',
                            freetime: item.value === 'LEISURE',
                            offine: item.value === 'OFFLINE',
                        }"
                        v-if="item.name == '机巢状态'"
                    >
                        {{ item.value === 'OFFLINE' ? '离线中' : item.value === 'WORKING' ? '作业中' : '空闲中' }}
                    </div>
                    <div class="itemValue" v-else> {{ item.value }}</div>
                    <div class="itemValue" v-else>{{ item.value }}</div>
                </div>
            </div>
        </div>
        <DeviceJob v-if="isShowDetails" />
        <DeviceEvent v-if="isShowDetails" />
    </el-dialog>
</template>
<script setup>
@@ -68,22 +70,9 @@
<style lang="scss">
.machineTableDetails {
width: 1270px;
    width: 1270px;
    height: 856px;
    background: #0f1929;
    box-shadow: inset 0px -50px 50px 0px rgba(27, 148, 255, 0.13);
    border-radius: 20px 0px 0px 0px;
    border: 2px solid;
    padding: 0 !important;
    border-image: linear-gradient(
            180deg,
            rgba(81, 168, 255, 0),
            rgba(48, 111, 202, 1),
            rgba(255, 255, 255, 1),
            rgba(27, 148, 255, 1)
        )
        2 2;
    .el-pagination {
        text-align: left;
        padding: 20px 20px 0 20px;
@@ -93,46 +82,10 @@
        vertical-align: middle;
        margin-left: 12px;
    }
    /* 头部 */
    .el-dialog__header {
        width: 1270px;
        height: 47px;
        margin-bottom: 14px;
        background: url('/src/assets/images/home/homeLeft/inspection-vector.png') no-repeat center;
        background-size: 100% 100%;
        font-weight: bold;
        font-size: 16px;
        line-height: 47px;
    }
    .el-dialog .el-dialog__header {
        /* margin: 0px !important; */
        padding: 0px !important;
        padding-left: 0px !important;
    }
    /* 头部 */
    .el-dialog__title {
        width: 112px;
        height: 19px;
        font-family: Segoe UI, Segoe UI;
        font-weight: bold;
        font-size: 16px;
        line-height: 16px;
        text-shadow: 0px 0px 5px rgba(154, 218, 255, 0.6);
        text-align: left;
        font-style: normal;
        text-transform: none;
        background: linear-gradient(90deg, #fbfdff 0%, #86d4ff 100%);
        margin-left: 16px;
        -webkit-background-clip: text; /* 背景被裁剪成文字的前景色 */
        -webkit-text-fill-color: transparent; /* 文字填充颜色变透明 */
    }
}
</style>
<style lang="scss" scoped>
.infoBox {
    display: flex;
    justify-content: space-between;
@@ -144,7 +97,7 @@
        display: flex;
        align-items: center;
        .itemTitle {
        margin-right: 5px;
            margin-right: 5px;
        }
    }
@@ -175,6 +128,13 @@
        margin-bottom: 8px;
    }
}
.missionOutcomes {
    span {
        color: #ffa500;
        font-size: 14px;
        font-weight: bold;
    }
}
// 离线中
.offine {
    width: 53px;
src/views/SignMachineNest/SignMachineNest.vue
@@ -6,7 +6,7 @@
<script setup>
import MachineLeft from '@/views/SignMachineNest/MachineLeft/MachineLeft.vue'
import MachineRight from '@/views/SignMachineNest/MachineRight/MachineRight.vue';
import { useConnectWebSocket } from '../../utils/websocket/connect-websocket';
import { useConnectWebSocket } from '@/utils/websocket/connect-websocket';
import { getWebsocketUrl } from '@/websocket/util/config';
import { EBizCode } from '@/utils/staticData/enums.js';
import { EModeCode } from '@/utils/staticData/device.js';
src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails.vue
File was deleted
src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails/CurrentTaskDetails.vue
New file
@@ -0,0 +1,104 @@
<!--当前任务详情-->
<template>
  <el-dialog
        modal-class="current-task-details"
        v-model="isShow"
        title="当前任务详情"
        :width="pxToRem(1500)"
        :close-on-click-modal="false"
        :destroy-on-close="true">
        <div class="content-container" v-if="isShow">
      <!-- 视频直播 -->
      <div class="video-container">
        <LiveVideo :videoUrl="machineNestUrl"/>
      </div>
            <!-- 展示地图 -->
            <RealTimeMap />
    </div>
  </el-dialog>
</template>
<script setup>
import { pxToRem } from '@/utils/rem';
import LiveVideo from '@/components/LiveVideo.vue';
import { liveStart } from '@/api/home/machineNest';
import { getJobDetails } from '@/api/home/task';
import RealTimeMap from '@/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails/RealTimeMap.vue'
import { getWebsocketUrl } from '@/websocket/util/config'
import { useConnectWebSocket } from '@/utils/websocket/connect-websocket'
const isShow = defineModel('show');
const props = defineProps({
  rowData: { // 任务列表row数据
    type: Object,
    default: () => ({})
  }
});
let taskDetails = ref({})
const machineNestUrl = ref('');
provide('taskDetails', taskDetails);
// 获取机巢直播地址
const getVideoUrl = (deviceSn) => {
  liveStart(deviceSn).then(res => {
    if (res.data.code !== 0) return;
    machineNestUrl.value = res.data.data.rtcs_url;
  });
};
// 获取任务详情获取航线文件
const getTaskDetails = () => {
    getJobDetails({ wayLineJobInfoId: props.rowData.id }).then(res => {
        taskDetails.value = res.data.data
        getVideoUrl(taskDetails.value.device_sns[0]);
        console.log('taskDetails', taskDetails.value)
        createWsConnect(taskDetails.value.way_lines[0].workspace_id)
    })
}
const messageHandler = (result) => {
    let payload = JSON.parse(result) // 为了兼容聊天消息
    console.log('result,6666666', payload)
}
let connectWs
const createWsConnect = (workspaceId) => {
    let webSocketUrl = getWebsocketUrl() + '&workspace-id=' + workspaceId;
    // 监听ws 消息
    connectWs = useConnectWebSocket(messageHandler, webSocketUrl);
};
// 监听 rowData 变化
watch(isShow, (newVal) => {
  if (newVal) {
    getTaskDetails();
  }else{
        connectWs?.close();
    }
});
onBeforeUnmount(() => {
    connectWs?.close();
})
onMounted(() => {
});
</script>
<style lang="scss" scoped>
.current-task-details {
  display: flex;
  justify-content: space-between;
  .content-container {
    display: flex;
    // gap: 20px;
    height: 600px;
    .video-container {
      width: 50%;
      padding-right: 10px;
    }
  }
}
</style>
src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails/RealTimeMap.vue
New file
@@ -0,0 +1,175 @@
<template>
    <div id="currentTaskMap">
    </div>
</template>
<script setup>
import * as Cesium from 'cesium'
import AmapMercatorTilingScheme from '@/utils/cesium/AmapMercatorTilingScheme'
import { Cartesian3, Terrain, Viewer } from 'cesium'
import endPointImg from '@/assets/images/EndPointicon.png'
import startPointImg from '@/assets/images/Startingpointicon.png'
import { addBlueFilter } from '@/utils/cesium/common'
import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz'
import rwqfdImg from '@/assets/images/signMachineNest/rwqfd.png'
import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial'
import lineImg from '@/assets/images/arrow-right-blue.png'
import { flyVisual } from '@/utils/cesium/mapUtil'
const imageryProvider_ammapSL = new Cesium.UrlTemplateImageryProvider({
    url: 'https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
    layer: 'tdtVecBasicLayer',
    style: 'default',
    format: 'image/png',
    tileMatrixSetID: 'GoogleMapsCompatible',
    subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
    maximumLevel: 18,
    tilingScheme: new AmapMercatorTilingScheme(),
    credit: 'amap_SL',
})
let viewer = null
const initMap = () => {
    viewer = new Viewer('currentTaskMap', {
        terrain: Terrain.fromWorldTerrain(),
        infoBox: false, // 禁用沙箱,解决控制台报错
        animation: false, // 左下角的动画仪表盘
        baseLayerPicker: false, // 右上角的图层选择按钮
        geocoder: false, // 搜索框
        homeButton: false, // home按钮
        sceneModePicker: false, // 模式切换按钮
        timeline: false, // 底部的时间轴
        navigationHelpButton: false, // 右上角的帮助按钮,
        selectionIndicator: false, // 是否显示选择指示器
        baseLayer: false,
        fullscreenButton: false,
    })
    const gdLayer = viewer.imageryLayers.addImageryProvider(imageryProvider_ammapSL)
    const options = {
        bInvertColor: true,
        bFilterColor: true,
        filterColor: '#4e70a6'
    }
    // 添加蓝色滤镜
    addBlueFilter(options,viewer,gdLayer)
    viewer.scene.morphTo2D(0)
    //设置默认点
    viewer.camera.setView({
        destination: Cartesian3.fromDegrees(115.763819, 28.787374,5000),
    })
}
const drawWayline = lineObj => {
    const positions = lineObj.Placemark.map(item => {
        const [lon, lat] = item.Point.coordinates.split(',')
        return Cartesian3.fromDegrees(Number(lon), Number(lat))
    })
    // 起点
    viewer.entities.add({
        position: positions[0],
        billboard: {
            image: new Cesium.ConstantProperty(rwqfdImg),
            width: 70,
            height: 70,
        },
    })
    // 终点
    viewer.entities.add({
        position: positions[positions.length-1],
        billboard: {
            image: new Cesium.ConstantProperty(endPointImg),
            width: 30,
            height: 30,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐
        },
    })
    // 路径线
    viewer.entities.add({
        polyline: {
            width: 4,
            positions: positions,
            material: new ImageTrailMaterial({
                color: { alpha: 1, blue: 1, green: 1, red: 1 },
                speed: 20,
                image: lineImg,
                repeat: { x: Math.floor(40), y: 1 },
            }),
            clampToGround: false,
        },
    })
}
// 解析kmz文件
const parsingFiles = async (url) => {
    const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`)
    const waylinesXML = await res.fileInfoObj['wpmz/waylines.wpml']
    const waylinesXMLJSON = XMLToJSON(waylinesXML)?.['Document'];
    const waylinesXMLObj = removeTextKey(waylinesXMLJSON.Folder)
    if (!waylinesXMLObj.Placemark.length) return;
    const allPoint = waylinesXMLObj.Placemark.map(item => item.Point.coordinates.split(','))
    flyVisual(allPoint,viewer)
    drawWayline(waylinesXMLObj)
};
const removeMap = () => {
    viewer.entities.removeAll()
    viewer.destroy()
}
const taskDetails = inject('taskDetails')
watch(taskDetails, () => {
    if (taskDetails.value.way_lines.length){
        parsingFiles(taskDetails.value.way_lines[0].url)
    }
})
onBeforeUnmount(() => {
    removeMap()
})
onMounted(() => {
    nextTick(() => {
        initMap()
    })
})
</script>
<style scoped lang="scss">
#currentTaskMap {
    width: 50%;
    padding-left: 10px;
    height: 100%;
    :deep() {
        .cesium-viewer {
            width: 100%;
            height: 100%;
            overflow: hidden;
            .cesium-viewer-cesiumWidgetContainer {
                width: 100%;
                height: 100%;
                .cesium-widget {
                    width: 100%;
                    height: 100%;
                    canvas {
                        width: 100%;
                        height: 100%;
                    }
                }
            }
        }
        .cesium-viewer-bottom {
            display: none;
        }
    }
}
</style>
src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue
@@ -19,9 +19,9 @@
        <el-table-column prop="ai_type_str" label="关联算法" show-overflow-tooltip />
        <el-table-column label="任务状态" >
          <template #default="scope">
            <span :style="{
              color: scope.row.status === 1 ? '#e36913' :
                     scope.row.status === 2 ? '#ffc398' :
            <span :style="{
              color: scope.row.status === 1 ? '#e36913' :
                     scope.row.status === 2 ? '#ffc398' :
                     scope.row.status === 3 ? '#afd9fb' :
                     scope.row.status === 4 ? '#11c4ff' : '8cfea7'
            }">
@@ -65,8 +65,9 @@
<script setup>
import SearchBox from '../SearchBox.vue';
import AddTask from './AddTask.vue';
import CurrentTaskDetails from './CurrentTaskDetails.vue';
import CurrentTaskDetails from './CurrentTaskDetails/CurrentTaskDetails.vue';
import { jobList } from '@/api/home/task';
import { ElMessage } from 'element-plus'
const jobListParams = reactive({
  current: 1,
@@ -100,8 +101,12 @@
let isShowCurrentTaskDetails = ref(false);
let rowData = ref({});
const handleDetail = (row) => {
  isShowCurrentTaskDetails.value = true;
  rowData.value = row? row : {};
    if (row.device_sns.length === 1){
        isShowCurrentTaskDetails.value = true;
        rowData.value = row? row : {};
    }else{
        ElMessage.warning('即将跳转到集群调度');
    }
};
// 分页大小改变
src/views/TaskManage/TaskIntermediateContent/initPlanarWayline.js
src/views/TaskManage/TaskIntermediateContent/initPointWayline.js
File was deleted