|
@@ -0,0 +1,307 @@
|
|
|
|
+<style lang="scss">
|
|
|
|
+@import '@/styles/index.scss'; //导入样式
|
|
|
|
+
|
|
|
|
+.btstopology {
|
|
|
|
+ -webkit-box-sizing: border-box;
|
|
|
|
+ -moz-box-sizing: border-box;
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
+ width: 100%;
|
|
|
|
+ min-height: calc(100vh - 50px); //最小高度
|
|
|
|
+ background-color: #1f2e3d; //rgba(10, 10, 10, 0.8);
|
|
|
|
+
|
|
|
|
+ &__search {
|
|
|
|
+ -webkit-box-sizing: border-box;
|
|
|
|
+ -moz-box-sizing: border-box;
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
+ width: 100%;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ &__scatterChart {
|
|
|
|
+ -webkit-box-sizing: border-box;
|
|
|
|
+ -moz-box-sizing: border-box;
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
+ padding: 10px;
|
|
|
|
+ width: 100%;
|
|
|
|
+ overflow: auto; //自动出现滚动条
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+</style>
|
|
|
|
+
|
|
|
|
+<template>
|
|
|
|
+ <div class="btstopology">
|
|
|
|
+ <div class="btstopology__search">
|
|
|
|
+ <app-search>
|
|
|
|
+ <el-form :inline="true" :size="controlSize">
|
|
|
|
+ <el-form-item label="大楼编号">
|
|
|
|
+ <el-select v-model="tableData.listData.search.build_id" clearable placeholder="请选择"
|
|
|
|
+ style="width:120px">
|
|
|
|
+ <el-option v-for="(item, index) in mainData.buildIdList" :key="index" :label="item.label"
|
|
|
|
+ :value="item.value">
|
|
|
|
+ </el-option>
|
|
|
|
+ </el-select>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item label="楼层编号">
|
|
|
|
+ <el-select v-model="tableData.listData.search.layer_id" clearable placeholder="请选择"
|
|
|
|
+ style="width:120px">
|
|
|
|
+ <el-option v-for="(item, index) in mainData.layerIdList" :key="index" :label="item.label"
|
|
|
|
+ :value="item.value">
|
|
|
|
+ </el-option>
|
|
|
|
+ </el-select>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ <el-form-item>
|
|
|
|
+ <el-button type="primary" @click="hGetBaseParam">查询</el-button>
|
|
|
|
+ <el-button type="success" @click="openAddBaseParamDlg"
|
|
|
|
+ v-icanvisit="'add_topology_bts'">新增基站</el-button>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ </el-form>
|
|
|
|
+ </app-search>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!--画布-->
|
|
|
|
+ <div class="btstopology__scatterChart" id="scrollContainer">
|
|
|
|
+ <canvas ref="scatterChart" id="myCanvas" @click="hScatterChartClick" width="600" height="400"></canvas>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <!--基站基本参数-->
|
|
|
|
+ <el-dialog width="55%" v-drag-dialog :title="dialogData.editData.title" center
|
|
|
|
+ :visible.sync="dialogData.editData.show" :modal-append-to-body="false" custom-class="editDataDialog"
|
|
|
|
+ :close-on-click-modal="false" :close-on-press-escape="false">
|
|
|
|
+ <bts-param-dlg :isUpdate="dialogData.editData.isUpdate" :id="dialogData.editData.id"
|
|
|
|
+ @get="hGetDataBaseParam" @close="hCloseBaseParamDlg" v-if="dialogData.editData.show" />
|
|
|
|
+ </el-dialog>
|
|
|
|
+ </div>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+<script>
|
|
|
|
+import BtsParamDlg from "./component/btsparamdlg.vue"
|
|
|
|
+
|
|
|
|
+import { genBuildIdList, genLayerIdList } from "./basestaticdata.js"
|
|
|
|
+import { getBtsBaseParamList } from "@/api/bparam"
|
|
|
|
+export default {
|
|
|
|
+ name: 'btstopology',
|
|
|
|
+ components: {
|
|
|
|
+ BtsParamDlg
|
|
|
|
+ },
|
|
|
|
+ data: function () {
|
|
|
|
+ return {
|
|
|
|
+ tableData: {
|
|
|
|
+ listData: {
|
|
|
|
+ loading: false,
|
|
|
|
+ disabled: false,
|
|
|
|
+ search: {
|
|
|
|
+ build_id: 1,
|
|
|
|
+ layer_id: 1,
|
|
|
|
+ },
|
|
|
|
+ columns: [
|
|
|
|
+ ],
|
|
|
|
+ formatter: {
|
|
|
|
+ },
|
|
|
|
+ items: []
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ dialogData: {
|
|
|
|
+ editData: {
|
|
|
|
+ show: false,
|
|
|
|
+ title: '基站参数',
|
|
|
|
+ isUpdate: false,
|
|
|
|
+ id: 0,
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ mainData: {
|
|
|
|
+ buildIdList: genBuildIdList(),//大楼编号列表
|
|
|
|
+ layerIdList: genLayerIdList(),//楼层列表
|
|
|
|
+ freqColor: ["#DE000B", "#F76625", "#FFF701", "#00BE02", "#019CFE", "#070083", "#B106E0", "#000000", "#FFFFFF"],
|
|
|
|
+ slotColor: ["#FFA7A7", "#CAE8AA", "#81C9FF", "#FFFF9B"],
|
|
|
|
+ coordScaleX: 15, // X轴比例尺
|
|
|
|
+ coordScaleY: 15, // Y轴比例尺
|
|
|
|
+ offset: {//坐标偏移
|
|
|
|
+ xOffset: 0,
|
|
|
|
+ yOffset: 0
|
|
|
|
+ },
|
|
|
|
+ pageWidth: 0,
|
|
|
|
+ pageHeight: 0,
|
|
|
|
+ maxY: 0,//y坐标最大值
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ watch: {
|
|
|
|
+ updateTime(val, old) { //监听到改变后,重新初始化组件
|
|
|
|
+ this.init()
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ computed: {
|
|
|
|
+ controlSize() {
|
|
|
|
+ return this.$store.getters.controlSize
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ beforeMount() {
|
|
|
|
+ },
|
|
|
|
+ mounted() {
|
|
|
|
+ this.$nextTick(function () {
|
|
|
|
+ const rootDiv = document.querySelector('.btstopology');
|
|
|
|
+ if (rootDiv) {
|
|
|
|
+ this.mainData.pageWidth = rootDiv.offsetWidth
|
|
|
|
+ this.mainData.pageHeight = rootDiv.offsetHeight - 100
|
|
|
|
+ }
|
|
|
|
+ this.init()
|
|
|
|
+ })
|
|
|
|
+ },
|
|
|
|
+ methods: {
|
|
|
|
+ init() {
|
|
|
|
+ this.getBtsBaseParamList()
|
|
|
|
+ },
|
|
|
|
+ //重新设置画布大小
|
|
|
|
+ reSetCanvasSize(size) {
|
|
|
|
+ const width = size.gapX >= this.mainData.pageWidth ? size.gapX : this.mainData.pageWidth
|
|
|
|
+ const height = size.gapY >= this.mainData.pageHeight ? size.gapY : this.mainData.pageHeight
|
|
|
|
+
|
|
|
|
+ var canvas = document.getElementById('myCanvas')
|
|
|
|
+ canvas.width = width + 50
|
|
|
|
+ canvas.height = height + 50
|
|
|
|
+ },
|
|
|
|
+ //画散点图(注意:画布是一个整体,无法为画布上的某个元素单独增加事件)
|
|
|
|
+ drawScatterChart(dataList, offset) {
|
|
|
|
+ const ctx = this.$refs.scatterChart.getContext('2d')//获得画布2D绘图上下文句柄
|
|
|
|
+ ctx.clearRect(0, 0, this.$refs.scatterChart.width, this.$refs.scatterChart.height)//清除画布上的内容
|
|
|
|
+
|
|
|
|
+ //画所有基站信息
|
|
|
|
+ dataList.forEach(point => {
|
|
|
|
+ const coordX = (point.coord_x / this.mainData.coordScaleX) + offset.xOffset
|
|
|
|
+ let coordY = (point.coord_y / this.mainData.coordScaleY) + offset.yOffset
|
|
|
|
+ coordY = this.mainData.maxY / this.mainData.coordScaleY - coordY + 50//Y坐标取反
|
|
|
|
+
|
|
|
|
+ // 绘制上半圆(频率)
|
|
|
|
+ ctx.beginPath()
|
|
|
|
+ ctx.arc(coordX, coordY, 13, Math.PI, 2 * Math.PI)
|
|
|
|
+ ctx.fillStyle = this.mainData.freqColor[point.carrier_id - 1]
|
|
|
|
+ ctx.fill()
|
|
|
|
+
|
|
|
|
+ // 绘制下半圆(时隙)
|
|
|
|
+ ctx.beginPath()
|
|
|
|
+ ctx.arc(coordX, coordY, 13, 0, Math.PI)
|
|
|
|
+ ctx.fillStyle = this.mainData.slotColor[point.timeslot - 1]
|
|
|
|
+ ctx.fill()
|
|
|
|
+
|
|
|
|
+ // 标注文本
|
|
|
|
+ ctx.fillStyle = "#FFFFFF"
|
|
|
|
+ ctx.font = "11px Arial"
|
|
|
|
+ ctx.fillText(point.location_type, coordX - 3, coordY - 3) // 上半文本(定位类型)
|
|
|
|
+ ctx.fillText(point.location_type, coordX - 3, coordY + 10) // 下半文本(定位类型)
|
|
|
|
+ ctx.fillText(`ID${point.bts_id}`, coordX - 18, coordY + 30)//基站ID
|
|
|
|
+ ctx.fillText(`[${point.coord_x / 100}, ${point.coord_y / 100}]`, coordX - 18, coordY + 46)//坐标
|
|
|
|
+ ctx.fillText(`F${point.carrier_id}`, coordX + 15, coordY - 3)//载波
|
|
|
|
+ ctx.fillText(`T${point.timeslot}`, coordX + 15, coordY + 12)//时隙
|
|
|
|
+ })
|
|
|
|
+ },
|
|
|
|
+ //散点图点击事件处理函数
|
|
|
|
+ // 因为画布是一个整体,无法为画布上的某个元素单独增加事件,
|
|
|
|
+ // 所以只能通过鼠标点击的位置来判断点击的是哪个基站)
|
|
|
|
+ hScatterChartClick(event) {
|
|
|
|
+ const rect = this.$refs.scatterChart.getBoundingClientRect()
|
|
|
|
+ const x = event.clientX - rect.left
|
|
|
|
+ let y = event.clientY - rect.top
|
|
|
|
+
|
|
|
|
+ // 坐标转换公式(反向操作)
|
|
|
|
+ const coordX = (x - this.mainData.offset.xOffset) * this.mainData.coordScaleX
|
|
|
|
+ let coordY = (y - this.mainData.offset.yOffset) * this.mainData.coordScaleY
|
|
|
|
+ coordY = this.mainData.maxY - coordY//y轴翻转
|
|
|
|
+
|
|
|
|
+ const found = this.tableData.listData.items.find(bts => {
|
|
|
|
+ const dx = bts.coord_x - coordX
|
|
|
|
+ const dy = bts.coord_y - coordY
|
|
|
|
+ const distance = Math.sqrt(dx * dx + dy * dy)
|
|
|
|
+ return distance < 150 // 保持原阈值
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ if (found) {
|
|
|
|
+ this.dialogData.editData.id = found.id
|
|
|
|
+ this.dialogData.editData.isUpdate = true
|
|
|
|
+ this.dialogData.editData.show = true
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ hGetDataBaseParam() {
|
|
|
|
+ this.getBtsBaseParamList()
|
|
|
|
+ },
|
|
|
|
+ openAddBaseParamDlg() {
|
|
|
|
+ this.dialogData.editData.isUpdate = false
|
|
|
|
+ this.dialogData.editData.title = '新增基站参数'
|
|
|
|
+ this.dialogData.editData.show = true
|
|
|
|
+ },
|
|
|
|
+ hCloseBaseParamDlg() {
|
|
|
|
+ this.dialogData.editData.show = false
|
|
|
|
+ },
|
|
|
|
+ hGetBaseParam() {
|
|
|
|
+ this.getBtsBaseParamList()
|
|
|
|
+ },
|
|
|
|
+ getBtsBaseParamList() {//获得基站基本参数数据
|
|
|
|
+ let search = {}
|
|
|
|
+ let itemId = 0
|
|
|
|
+
|
|
|
|
+ itemId = parseInt(this.tableData.listData.search.build_id)
|
|
|
|
+ if (itemId > 0) {
|
|
|
|
+ search.build_id = itemId
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ itemId = parseInt(this.tableData.listData.search.layer_id)
|
|
|
|
+ if (itemId != 0) {
|
|
|
|
+ search.layer_id = itemId
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //使能标识符
|
|
|
|
+ search.enable_flag = 1//启用(只拉取启用的设备)
|
|
|
|
+
|
|
|
|
+ let param = { search: search }
|
|
|
|
+ let resultData = {}
|
|
|
|
+
|
|
|
|
+ //查询数据
|
|
|
|
+ this.tableData.listData.loading = true
|
|
|
|
+ getBtsBaseParamList(param).then((res) => {
|
|
|
|
+ this.tableData.listData.items = res.data
|
|
|
|
+ this.tableData.listData.loading = false
|
|
|
|
+
|
|
|
|
+ resultData = this.findMinCoordValue(this.tableData.listData.items);
|
|
|
|
+
|
|
|
|
+ //重新设置画布大小
|
|
|
|
+ this.reSetCanvasSize(resultData.gap)
|
|
|
|
+
|
|
|
|
+ //画基站散点图
|
|
|
|
+ this.drawScatterChart(this.tableData.listData.items, resultData.offset)
|
|
|
|
+ }).catch(() => {
|
|
|
|
+ this.tableData.listData.loading = false
|
|
|
|
+ })
|
|
|
|
+ },
|
|
|
|
+ //找出X和Y坐标的最小值
|
|
|
|
+ findMinCoordValue(dataList) {
|
|
|
|
+ let minX = Infinity, maxX = -Infinity
|
|
|
|
+ let minY = Infinity, maxY = -Infinity
|
|
|
|
+
|
|
|
|
+ dataList.forEach(point => {
|
|
|
|
+ minX = Math.min(minX, point.coord_x)
|
|
|
|
+ maxX = Math.max(maxX, point.coord_x)
|
|
|
|
+ minY = Math.min(minY, point.coord_y)
|
|
|
|
+ maxY = Math.max(maxY, point.coord_y)
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ this.mainData.maxY = maxY
|
|
|
|
+
|
|
|
|
+ // 偏移量计算
|
|
|
|
+ const offset = {
|
|
|
|
+ xOffset: minX < 0 ? (-minX / this.mainData.coordScaleX + 30) : 30,
|
|
|
|
+ yOffset: minY < 0 ? (-minY / this.mainData.coordScaleY + 30) : 30
|
|
|
|
+ }
|
|
|
|
+ this.mainData.offset = offset//保存偏移值
|
|
|
|
+
|
|
|
|
+ // 关键修复:画布间隙计算
|
|
|
|
+ const gap = {
|
|
|
|
+ gapX: ((maxX - minX) / this.mainData.coordScaleX) + 100,
|
|
|
|
+ gapY: ((maxY - minY) / this.mainData.coordScaleY) + 100
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return { offset, gap }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+</script>
|