feiyu02
2025-05-15 026f17ebafb85250d9ae9b71b80ae5f07341c172
src/views/sourcetrace/SourceTrace.vue
@@ -1,28 +1,101 @@
<template>
  <BaseCard>
    <template #content>
      <div>
  <el-row>
    <el-col v-show="show" span="10">
      <BaseCard>
        <template #content>
          <!-- <div>
        <el-input type="text" v-model="inputVal" />
        <button @click="handleSend">send</button>
        <button @click="handleLink">link</button>
      </div>
      <div>业务状态中控</div>
      <el-scrollbar ref="scrollbarRef" :height="height">
        <div ref="scrollContentRef">
          <div v-for="(item, index) in streams" :key="index">
            <el-text type="primary">[{{ item }}]: </el-text>
          </div>
        </div>
      </el-scrollbar>
    </template>
  </BaseCard>
      </div> -->
          <div>动态溯源</div>
          <el-scrollbar ref="scrollbarRef" :height="height" class="scrollbar">
            <div ref="scrollContentRef">
              <div v-for="(item, index) in streams" :key="index">
                <!-- <el-text type="primary">{{ item.guid }}</el-text> -->
                <el-space gap="4">
                  <el-tag :type="item.status == 1 ? 'danger' : 'info'">{{
                    item._statusStr
                  }}</el-tag>
                  <el-text type="default">{{ item.exception }}</el-text>
                </el-space>
                <el-row gap="4">
                  <el-text type="primary">发生时间:</el-text>
                  <el-text type="primary">{{
                    item.startTime + ' 至 '
                  }}</el-text>
                  <el-text type="primary">{{
                    item.status == 1 ? '当前' : item.endTime
                  }}</el-text>
                </el-row>
                <el-row>
                  <el-col :span="6">
                    <el-statistic title="因子" :value="item.factorName" />
                  </el-col>
                  <el-col :span="6">
                    <el-statistic title="均值" :value="item.avg" />
                  </el-col>
                  <el-col :span="6">
                    <el-statistic title="峰值" :value="item.max" />
                  </el-col>
                  <el-col :span="6">
                    <el-statistic title="谷值" :value="item.min" />
                  </el-col>
                </el-row>
                <el-row justify="space-between">
                  <el-link
                    type="primary"
                    @click="item.showMore = !item.showMore"
                  >
                    {{
                      (item.showMore ? '收起溯源场景' : '查看溯源场景') +
                      '(' +
                      item.relatedSceneList.length +
                      ')'
                    }}
                  </el-link>
                  <el-link type="primary" @click="drawSector(item)">
                    查看异常
                  </el-link>
                </el-row>
                <SceneTable
                  v-show="item.showMore"
                  :scene-list="item.relatedSceneList"
                ></SceneTable>
                <el-divider />
              </div>
            </div>
          </el-scrollbar>
        </template>
      </BaseCard>
    </el-col>
    <el-col span="2">
      <CardButton name="动态溯源" @click="() => (show = !show)"></CardButton>
    </el-col>
  </el-row>
</template>
<script setup>
/**
 * 动态溯源信息管理
 * 通过websocket方式接收后端推送的异常信息并展示
 */
import { reactive, ref, onMounted, onUnmounted, inject } from 'vue';
import { ws } from '@/api/index';
import websocket from '@/api/websocket';
import sector from '@/utils/map/sector';
import mapUtil from '@/utils/map/util';
import { sceneTypes, sceneIcon } from '@/constant/scene-types';
import marks from '@/utils/map/marks';
import { FactorDatas } from '@/model/FactorDatas';
const props = defineProps({
  factorType: String
});
const emits = defineEmits(['update:factorType']);
const height = `60vh`;
const show = ref(false);
const scrollContentRef = ref();
const scrollbarRef = ref();
@@ -33,50 +106,135 @@
  }, 100);
}
const streams = reactive([]);
function scrollToTop() {
  setTimeout(() => {
    scrollbarRef.value.setScrollTop(0);
  }, 100);
}
let socket;
const streams = reactive([]);
const inputVal = ref('');
const handleSend = () => {
  if (socket) {
    socket.send(inputVal.value);
  }
};
const handleLink = () => {
  if (socket) {
    socket.close();
  }
  const url = 'ws://' + ws + '/ws';
  // socket = new WebSocket(`ws://192.168.0.138:8080/workstream`)
  socket = new WebSocket(url);
  // 与服务器建立连接:发送消息到服务器
  socket.onopen = () => {
    console.log('connect: ');
  };
  // 收到服务器发送的消息:event处理服务器返回的数据
  socket.onmessage = (event) => {
    console.log('receive: ', event.data);
    putWorkStream(event.data);
  };
  // 连接或通信过程中发生错误
  socket.onerror = (event) => {
    console.log('errror: ', event);
  };
  // 与服务器断开连接
  socket.onclose = (event) => {
    console.log('close: ', event.code);
  };
  websocket.send(inputVal.value);
};
/**
 * 添加一条工作流信息
 * @param {*} data
 */
function putWorkStream(data) {
const putWorkStream = (data) => {
  const obj = JSON.parse(data);
  streams.push(obj);
  scrollToBottom();
  console.log('sourcetrace: ', obj);
  obj._statusStr = exceptionStatus(obj.status);
  if (streams.length == 0) {
    streams.push(obj);
  } else {
    const index = streams.findIndex((v) => {
      return v.guid == obj.guid;
    });
    if (index != -1) {
      const old = streams[index];
      obj.showMore = old.showMore;
      old.relatedSceneList.forEach((s) => {
        const index = obj.relatedSceneList.findIndex((v) => {
          return v.guid == s.guid;
        });
        if (index == -1) {
          obj.relatedSceneList.push(s);
        }
      });
      streams.splice(index, 1, obj);
    } else {
      streams.unshift(obj);
    }
    show.value = true;
  }
  // scrollToBottom();
  scrollToTop();
  const excObj = streams.find((v) => {
    return v.factorId + '' == props.factorType;
  });
  if (excObj) {
    drawSector(excObj);
  }
};
function exceptionStatus(value) {
  switch (value) {
    case 1:
      return '异常发生中';
    case 2:
      return '异常已结束';
    default:
      return '异常已结束';
  }
}
let lastDrawObjGuid;
function drawSector(exceptionObj) {
  emits('update:factorType', exceptionObj.factorId + '');
  setTimeout(() => {
    if (exceptionObj.guid != lastDrawObjGuid) {
      sector.clearSectorPt();
      lastDrawObjGuid = exceptionObj.guid;
    }
    // 1. 绘制新扇形区域
    const datavo = exceptionObj.midData;
    const factorDatas = new FactorDatas();
    factorDatas.setData([datavo], 0, () => {
      const pr = sector.drawSectorPt(factorDatas, 0);
      // 调整视角居中显示
      // mapUtil.setCenter(pr.p);
      drawMarks(exceptionObj.relatedSceneList);
    });
  }, 1000);
}
let layer = undefined;
function drawMarks(sceneList) {
  if (layer != undefined) {
    mapUtil.removeViews(layer);
    // layer = undefined;
  }
  if (sceneList.length != 0) {
    const icons = [];
    sceneList.forEach((s) => {
      icons.push(sceneIcon(s.typeId));
    });
    layer = marks.createLabelMarks(icons, sceneList, true);
  }
}
onMounted(() => {
  websocket.registerReceiveEvent(putWorkStream);
});
onUnmounted(() => {
  websocket.removeReceiveEvent(putWorkStream);
  sector.clearSectorPt();
  mapUtil.clearMap();
  // if (layer != undefined) {
  //   mapUtil.removeViews(layer);
  //   layer = undefined;
  // }
});
</script>
<style scoped>
:deep(.el-statistic) {
  --el-statistic-title-color: rgb(215, 215, 215);
  --el-statistic-content-color: white;
}
:deep(.el-text) {
  --el-text-color: white;
}
.scrollbar {
  min-width: 300px;
}
</style>