Merge branch 'dev-3.5' of https://git.mesalab.cn/nezha/nezha-fronted into dev-3.5

This commit is contained in:
wenzhijie
2022-08-19 14:26:02 +08:00
1313 changed files with 6013 additions and 192 deletions

View File

@@ -5943,6 +5943,30 @@
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-2.2.2.tgz",
"integrity": "sha512-0D9P8TRj6qDAtHhRQn6EfdOtHMfsUWanl3yb/84C4DqpZ+VsgfI5iTVRNRbELCfNvRfpMr8OrqqUTQ6ANGCijw=="
},
"d3-sankey": {
"version": "0.12.3",
"resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz",
"integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==",
"requires": {
"d3-array": "1 - 2",
"d3-shape": "^1.2.0"
},
"dependencies": {
"d3-path": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
"integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="
},
"d3-shape": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
"integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
"requires": {
"d3-path": "1"
}
}
}
},
"d3-scale": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",

View File

@@ -37,6 +37,7 @@
"cytoscape": "^3.15.2",
"d3": "^6.7.0",
"d3-hexbin": "^0.2.2",
"d3-sankey": "^0.12.3",
"d3-zoom": "^3.0.0",
"echarts": "^5.2.2",
"element-ui": "^2.15.3",

View File

@@ -427,6 +427,7 @@ td .nz-icon-gear:before {
}
.chart-bar,
.chart-gauge,
.chart-sankey,
.chart-time-series,
.chart-treemap,
.chart-pie,

View File

@@ -449,8 +449,8 @@
}
.leaflet-pane.leaflet-my-pane {
position: absolute;
top: -150px;
left: -175px;
top: -0px;
left: -0px;
.leaflet-tooltip {
transform: unset !important;

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 358.52 351.84"><defs><style>.cls-1{fill:#080430;}.cls-2{fill:#0a093a;}</style></defs><title>Gas-1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M79.83,335.09c33.16-34,60.94-32.19,107.59-41.09,6.37-1.22,13.19-2.6,17.72-7.24,8.86-9.07,4.05-24,5.16-36.64,1.86-21.09,20.94-36.07,39.18-46.81a395,395,0,0,1,62.72-29.84c6.13-2.28,12.5-4.5,17.37-8.87,11.86-10.6,10.78-29.42,6.5-44.74s-10.84-31.23-6.74-46.6C332.4,61.78,341,52.73,348,43.13s12.93-21.62,9.59-33c-1.19-4.06-3.81-8-7.79-9.44-2.9-1-6.1-.66-9.11,0-24.6,5.3-42.89,25.57-57.87,45.78s-29.31,42.29-51.34,54.44c-19.76,10.9-44,12.73-62.16,26.16-20.21,15-28.35,40.75-38.3,63.84s-25.94,49.95-51.07,50.85c-50.74,1.81-76.38,9-79.67,47.84S26.45,378.46,79.83,335.09Z"/><path class="cls-2" d="M142,210.5c9.94-19.16,22.64-38.16,41.83-48,9.31-4.79,19.68-7.18,29.08-11.76a68.68,68.68,0,0,0,23.51-18.81c5.75-7.19,10-15.46,15.26-23s11.95-14.61,20.62-17.74,19.6-1.3,25,6.17c3,4.19,4,9.59,3.67,14.75-.94,16.29-13.16,29.81-26.63,39s-28.83,15.62-41.46,26c-18.83,15.42-29.9,38.07-43.9,58s-34,38.73-58.31,39.33c-14.23.35-28.68-11.68-17.93-25.72,4-5.2,10.56-8.43,14.8-13.65C133.49,227.63,137.62,218.89,142,210.5Z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 231.44 169.14"><defs><style>.cls-1{fill:#080430;}.cls-2{fill:#0a093a;}</style></defs><title>Gas-2_1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M3.5,103c-3.38,7.61-4,16.2-3.21,24.49.37,4.05,1.09,8.19,3.17,11.68,2.36,4,6.28,6.79,10.41,8.86,19,9.51,41.71,5.06,62.59,8.78,22.9,4.08,46.15,18.07,67.85,9.7,13.55-5.23,22.62-18.23,28.15-31.66s8.35-27.87,14.11-41.2a101.42,101.42,0,0,1,28-37.48c4.22-3.52,8.8-6.77,12-11.21,6.55-9,6.28-22,.08-31.25S209-.58,198,.05c-5.71.33-11.24,2-16.8,3.36-18,4.3-35.77,4-54,1.73C116.06,3.76,105,1.63,95.77,9.55c-12.7,10.92-12.49,30-23.14,42.37C63.48,62.57,49.93,68.21,37.7,74.4,24.37,81.15,9.9,88.65,3.5,103Z"/><path class="cls-2" d="M79.47,79.19c-11.15,5.47-23,12.67-26.57,24.57a16.94,16.94,0,0,0-.73,7.1c.56,4.27,3.22,8,6.46,10.87,10.84,9.49,27.65,9.41,40.69,3.3s23-17.15,32-28.39,17.55-23.11,29-31.87c7.34-5.63,16.92-12.42,14.92-21.46-4-18.18-39.93-14.87-50.84-5.78-6.76,5.62-10.83,14.14-16.16,21C100.52,68.46,90.65,73.71,79.47,79.19Z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 353.51 256.8"><defs><style>.cls-1{fill:#080430;}</style></defs><title>Gas-3_1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M133,121.83c-11.11,2.6-22.72,1.73-34,3.26-27.86,3.76-51.53,21.52-73.8,38.68C16.2,170.67,6.89,178,2.32,188.34S-.2,212.89,9.75,218.26c8.57,4.63,19.13,1.25,28.82,2.3,11.69,1.27,21.51,8.91,31.48,15.13A148.61,148.61,0,0,0,113,253.8c19.29,4.74,41.5,5,57-7.47,17.9-14.35,22.28-41.82,41.83-53.82,23.25-14.28,59.44-1.14,76.88-22.14,8.73-10.5,8.65-25.43,10.26-39,1.83-15.45,6.59-30.89,16.1-43.21,13.56-17.55,37.14-30.34,38.45-52.48C354.55,17.27,337,1.23,318.55.11c-46.31-2.82-48.12,51.16-82.79,65.58-16.83,7-32.51,10.54-47.81,21.53C170.39,99.82,154.78,116.73,133,121.83Z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 812 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 356.14 220.95"><defs><style>.cls-1{fill:#080430;}.cls-2{fill:#0a093a;}</style></defs><title>Gas-4_1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M127,56.49c21-.45,41.34-6.92,62-10.69a244,244,0,0,1,46.26-3.93c9.68.11,19.72.89,28.19,5.57,15.33,8.46,22.15,27.6,37.13,36.65C312.92,91.51,328.84,91,341,98.69c15.33,9.78,20,32.94,9.65,47.89-11.09,16-34.24,20.51-44.5,37.06-4.38,7.06-6.08,15.88-12,21.66-5,4.85-12.08,6.68-18.85,8.27-31.3,7.32-65.71,12.8-94.37-1.76-20.14-10.24-34.79-29.25-55-39.35-20.06-10-44.27-10.6-63-22.93A58.63,58.63,0,0,1,38.42,115c-2.16-8.73-2.32-18-6.13-26.19-3.16-6.74-8.57-12.11-13.5-17.68C10.46,61.75,3,51,.68,38.61S2,12.14,12.32,4.9c9.51-6.7,22.89-6.17,33.11-.61,14,7.64,18.81,22.58,29.49,33.35C88.55,51.38,108.07,56.91,127,56.49Z"/><path class="cls-2" d="M246.9,98c9,5.42,16.79,13.2,26.69,16.84,3.4,1.26,7.05,2,10.09,4,7.16,4.67,8.7,14.67,7.19,23.08a47.29,47.29,0,0,1-25.44,33.6c-9.26,4.49-19.79,5.8-30.08,5.58-9.28-.19-18.63-1.6-27.18-5.21-11.8-5-21.44-13.85-31.89-21.25-21.47-15.2-46.71-24.44-68.14-39.7-16.53-11.76-30.53-26.8-43.8-42-3.08-3.53-7.33-6.11-4.27-10.55,2.41-3.5,9.28-2.77,12.65-1.93,8.72,2.18,16.37,9.87,24,14.37,24.95,14.65,56.39,12,84.36,12C203.36,86.76,227.13,86.23,246.9,98Z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 281.71 151.31"><defs><style>.cls-1{fill:#0d0635;}</style></defs><title>Gas-5_1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M18.44,100.85c-10.63,6.46-20.22,17.78-18.16,30,1.68,10,11,17.48,21,19.6s20.3-.15,29.92-3.45c25.07-8.59,48.8-24.36,75.29-23.55,18.64.58,38.16,9.33,55.15,1.66,17.75-8,24.31-30.38,40.28-41.51,10.23-7.13,23.14-8.84,34.89-13s23.75-12.73,24.85-25.15c1.19-13.59-10.88-24.31-22.07-32.1S235.48-2,222.13.46C200.35,4.45,191,32,170.58,40.69c-22.72,9.65-49.74-7.61-73.08.44C81.18,46.76,69.67,63.27,57.9,75.08,46,87.06,32.75,92.17,18.44,100.85Z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 683 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 351.44 371.77"><defs><style>.cls-1{fill:#080430;}.cls-2{fill:#0a093a;}</style></defs><title>Gas-6_1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M12.39,255.31c1.09,6.23,4,12.1,4.39,18.41,1,15.91-13.4,28.73-16.32,44.4C-3,336.53,13.35,356,32.08,355.76c10.54-.12,21.57-5.35,31.17-1,6.5,2.94,10.54,9.6,16.59,13.38,7.77,4.85,17.81,4.27,26.6,1.7s17-6.92,25.85-9.27c14.76-3.93,30.43-2.13,45.51-4.49s31.19-11,34.35-25.93c2.37-11.23-3.25-22.9-1.38-34.23,2.8-16.91,20.57-26.62,36.8-32.16s34.52-10.55,43.64-25.06c17.51-27.85-12.18-65.72-.32-96.41,8.17-21.11,34.4-33.52,36.49-56.05.73-7.9-1.77-16.08.55-23.67,2.38-7.8,9.27-13.18,14.94-19.06s10.59-14.2,7.74-21.85c-1.3-3.48-4-6.2-6.68-8.8C336,5.1,325.12-3.2,315,1.25c-5,2.2-8.31,7.1-10.86,12-5.61,10.71-9.07,22.44-14.79,33.09S274.89,66.88,263.21,70c-9.67,2.55-19.84.08-29.8-.84s-21.3.33-27.63,8.07c-3,3.61-4.41,8.18-5.85,12.61q-5.94,18.42-12.81,36.53c-7.3,19.26-15.91,39-31.71,52.19-10.41,8.69-23.21,13.91-35.78,19-25.14,10.17-48.76,17.68-75.78,19.9C24.73,219,8.69,234.2,12.39,255.31Z"/><path class="cls-2" d="M45.15,264.26c-5.9,9.7-5.35,23.85,3.47,31,5.75,4.66,13.55,5.55,20.71,7.42,20.43,5.32,39,19.81,60,18.47,2.71-.18,5.54-.68,7.67-2.37,3-2.39,3.89-6.51,5.11-10.16A40.51,40.51,0,0,1,163.4,285c6.11-2.81,13-4.09,18.54-7.84,9.57-6.44,13.42-19.57,10.45-30.72s-11.88-20.15-22.36-25c-21-9.72-40.63,13-61.37,17.23C87,243,58.11,242.92,45.15,264.26Z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240.93 162.09"><defs><style>.cls-1{fill:#0a093a;}</style></defs><title>Gas-7_1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M161.5,61c-12.75,14.53-32.93,19.52-51.79,23.76L44.38,99.45C32,102.24,19,105.33,9.58,113.88s-13.38,24.46-5,34c9.2,10.49,25.75,7.44,39.67,8.5,17.52,1.33,36,10.4,51.71,2.47,11.55-5.83,17.28-19.14,27.55-27,15.89-12.2,38.55-9,57.42-15.69,28.81-10.27,43.81-41.19,55.91-69.28,10.45-24.25,1.21-56.61-31.38-44.1C183.1,11.37,176,44.51,161.5,61Z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 587 B

View File

@@ -134,7 +134,9 @@ export default {
case 'table' :
case 'stat' :
case 'gauge' :
case 'sankey' :
case 'pie' :
case 'bubble' :
case 'treemap' :
case 'log' :
case 'hexagon' :

View File

@@ -108,6 +108,15 @@
:is-fullscreen="isFullscreen"
@chartIsNoData="chartIsNoData"
></chart-gauge>
<chart-sankey
:ref="'chart' + chartInfo.id"
v-if="isSankey(chartInfo.type)"
:chart-data="chartData"
:chart-info="chartInfo"
:chart-option="chartOption"
:is-fullscreen="isFullscreen"
@chartIsNoData="chartIsNoData"
></chart-sankey>
<chart-diagram
:ref="'chart' + chartInfo.id"
v-if="isDiagram(chartInfo.type)"
@@ -205,6 +214,7 @@ import chartClock from './chart/chartClock'
import chartDiagram from './chart/chartDiagram'
import chartEndpointInfo from './chart/chartEndpointInfo'
import chartGauge from './chart/chartGauge'
import chartSankey from './chart/chartSankey'
import chartGroup from './chart/chartGroup'
import chartLog from './chart/chartLog'
import chartNoData from './chart/chartNoData'
@@ -220,7 +230,7 @@ import chartValue from './chart/chartValue'
import chartHexagonD3 from './chart/chartHexagonD3'
import chartMap from './chart/chartMap'
import chartTopology from './chart/chartTopology'
import { getOption, isTimeSeries, isHexagon, isUrl, isText, isChartPie, isChartBubble, isChartBar, isTreemap, isLog, isStat, isDiagram, isGroup, isAutotopology, isMap, isAssetInfo, isEndpointInfo, isTable, isGauge, isClock, isTopology } from './chart/tools'
import { getOption, isTimeSeries, isHexagon, isUrl, isText, isChartPie, isChartBubble, isChartBar, isTreemap, isLog, isStat, isDiagram, isGroup, isAutotopology, isMap, isAssetInfo, isEndpointInfo, isTable, isGauge, isSankey, isClock, isTopology } from './chart/tools'
import lodash from 'lodash'
export default {
@@ -234,6 +244,7 @@ export default {
chartDiagram,
chartEndpointInfo,
chartGauge,
chartSankey,
chartGroup,
chartLog,
chartNoData,
@@ -321,6 +332,7 @@ export default {
isMap,
isTable,
isGauge,
isSankey,
isClock,
isTopology,
chartIsNoData (flag) {

View File

@@ -48,7 +48,6 @@ export default {
data () {
return {
colorList: [],
chartDot: 2,
isInit: true, // 是否是初始化初始化时为true图表初始化结束后设为false
chartId: '',
bubbleData: [],
@@ -276,11 +275,7 @@ export default {
},
mounted () {
this.colorList = initColor(20)
this.chartInfo.loaded && this.initChart(this.chartOption)
this.chartInfo.loaded && this.initChart()
}
}
</script>
<style scoped>
</style>

View File

@@ -4,7 +4,54 @@
<div :id="'map' + (isFullscreen ? '-screen-' : '' ) + chartInfo.id " style="height: 100%; width: 100%" :style="theme=='dark'? 'filter: invert(1) hue-rotate(0.5turn);opacity: 0.75;': ''"></div>
</div>
<!--自定义地图鼠标悬浮提示dom避免被overflowhidden裁剪-->
<div :style="{'left': `${tooltip.x}px`, 'top': `${tooltip.y}px`}" class="my-pane" :class="'my-pane-' + chartId"></div>
<div :style="{'left': `${tooltip.x}px`, 'top': `${tooltip.y}px`}" class="my-pane" :class="'my-pane-' + chartId" v-if="tooltip.show">
<div class="leaflet-pane leaflet-my-pane">
<div class="leaflet-tooltip leaflet-zoom-animated leaflet-tooltip-left" style="opacity: 0.9; transform: translate3d(251px, 196px, 0px);">
<div class="nz-tooltip-bac tooltip-map" style="z-index: 11111;width: 175px">
<div class="tooltip--title">{{dcStat.name}}</div>
<div class="tooltip--row">
<div class="legend-value legend-value-asset">
<div class="map-asset">
<div class="progress-title">{{$t('panel.assetOk')}}</div>
<div class="success-progress progress-box">
<div class="top-progress" :style="{width: (dcStat.asset.ok / dcStat.asset.total) * 100 + '%' }"></div>
<div style="width: 100%" class="bottom-progress"></div>
</div>
<div class="success-progress progress-content">{{dcStat.asset.ok}}</div>
</div>
<div class="map-asset">
<div class="progress-title">{{$t('panel.assetAlarm')}}</div>
<div class="error-progress progress-box">
<div class="top-progress" :style="{width: (dcStat.asset.alarm / dcStat.asset.total) * 100 + '%'}"></div>
<div style="width: 100%" class="bottom-progress"></div>
</div>
<div class="error-progress progress-content">{{dcStat.asset.alarm}}</div>
</div>
</div>
<div class="partition"></div>
<div class="legend-value legend-value-agent">
<div class="map-asset">
<div class="progress-title">{{$t('overall.agent')}} {{$t('config.agent.up')}}}</div>
<div class="success-progress progress-box">
<div class="top-progress" :style="{width: (dcStat.agent.up / (dcStat.agent.up + dcStat.agent.down)) * 100 + '%'}"></div>
<div style="width: 100%" class="bottom-progress"></div>
</div>
<div class="success-progress progress-content">{{dcStat.agent.up}}</div>
</div>
<div class="map-asset">
<div class="progress-title">{{$t('overall.agent')}} {{$t('asset.down')}}</div>
<div class="error-progress progress-box">
<div class="top-progress" :style="{width: (dcStat.agent.down / (dcStat.agent.up + dcStat.agent.down)) * 100 + '%'}"></div>
<div style="width: 100%" class="bottom-progress"></div>
</div>
<div class="error-progress progress-content">{{dcStat.agent.down}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
@@ -27,14 +74,25 @@ export default {
map: null,
tooltip: {
x: 0,
y: 0
y: 0,
show: false
},
dcStat: {},
theme: localStorage.getItem(`nz-user-${localStorage.getItem('nz-user-id')}-theme`),
timer: null
timer: null,
requestAnimationFrame: '',
pbfUrl: {
m: [],
c: []
}
}
},
mounted () {
this.chartInfo.loaded && this.initChart()
// for (let i = 0; i < 65535; i += 1) {
// const url = 'https://api.maptiler.com/fonts/Roboto Medium,Noto Sans Regular/' + i + '-' + (i + 255) + '.pbf?key=rB2y2a2rG8i9SEjOXQXl'
// window.open(url)
// }
},
methods: {
initChart () {
@@ -48,26 +106,21 @@ export default {
this.timer = setTimeout(() => {
let loadPromise
this.loadMapConfig().then((mapConfig) => {
// if (mapConfig && this.map) {
// loadPromise = this.loadDataCenterMapData()
// }
})
this.isInit = false
return loadPromise
}, 500)
},
loadMapConfig () {
const self = this
return new Promise(resolve => {
const DefaultIcon = L.icon({
iconUrl: icon,
shadowUrl: iconShadow
})
L.Marker.prototype.options.icon = DefaultIcon
const param = { paramKey: 'map_center_config' }
this.$get('sysConfig', param).then(response => {
if (response.code == 200) {
const mapConfig = JSON.parse(response.data.paramKey.map_center_config)
mapStyle.center = [Number(mapConfig.longitude), Number(mapConfig.latitude)]
mapStyle.zoom = Number(mapConfig.zoom)
if (this.map) {
this.map.remove()
this.map = null
@@ -78,10 +131,9 @@ export default {
style: mapStyle,
maxZoom: 7,
minZoom: 0,
renderWorldCopies: false,
// renderWorldCopies: false,
hash: false,
transformRequest: function (url, resourceType) {
console.log(url, resourceType)
if (resourceType === 'Tile' && url.indexOf('https://api.maptiler.com/tiles/v3') > -1) {
const urlParams = url.split('.pbf')[0].split('/')
const z = urlParams[urlParams.length - 3]
@@ -95,8 +147,27 @@ export default {
// Include cookies for cross-origin requests
}
}
if (resourceType === 'SpriteJSON') {
return {
url: 'nzMap://static/Titles/sprite.json',
credentials: 'include',
method: 'GET'
// Include cookies for cross-origin requests
}
}
if (resourceType === 'SpriteImage') {
return {
url: 'nzMap://static/Titles/sprite.png',
credentials: 'include',
method: 'GET'
// Include cookies for cross-origin requests
}
}
}
})
this.map.on('load', function () {
self.loadDataCenterMapData()
})
maplibregl.addProtocol('nzMap', (params, callback) => { // 切片显示接口 防止跨域的问题
fetch(`${params.url.split('://')[1]}`)
.then(t => {
@@ -119,6 +190,7 @@ export default {
})
},
loadDataCenterMapData () {
const self = this
return new Promise(resolve => {
const requests = [axios.get('dc?pageSize=-1'), axios.get('/stat/overview/map')]
axios.all(requests).then(result => {
@@ -131,9 +203,9 @@ export default {
const dc = dcInfos.find(i => i.name === s.name)
dc && (s = { ...s, latitude: dc.latitude, longitude: dc.longitude })
})
const bigScatter = 10
const mediumScatter = 8
const smallScatter = 6
const bigScatter = 18
const mediumScatter = 13
const smallScatter = 10
const maxAssetTotal = dcInfos[0] ? dcInfos[0].assetNum : '' // 获取asset数量最大值
const bigBoundary = Number.parseInt(maxAssetTotal / 3 * 2) // 根据最大值定下大图标、中图标的阈值
const mediumBoundary = Number.parseInt(maxAssetTotal / 3)
@@ -147,85 +219,104 @@ export default {
} else {
symbolSize = smallScatter
}
let shadowMarker
let marker
if (dcStat.color === 1) {
shadowMarker = L.circleMarker([dcStat.latitude, dcStat.longitude], { opacity: 0, fillOpacity: 0, radius: symbolSize + 10 })
marker = L.circleMarker([dcStat.latitude, dcStat.longitude], { interactive: false, color: '#23BF9A', opacity: 0.42, fillColor: '#23BF9A', fillOpacity: 0.5, radius: symbolSize, className: 'real-marker' })
} else if (dcStat.color === 2) {
shadowMarker = L.circleMarker([dcStat.latitude, dcStat.longitude], { opacity: 0, fillOpacity: 0, radius: symbolSize + 10 })
marker = L.circleMarker([dcStat.latitude, dcStat.longitude], { interactive: false, color: '#EC7F66', opacity: 0.42, fillColor: '#EC7F66', fillOpacity: 0.5, radius: symbolSize, className: 'real-marker error-item' })
} else if (dcStat.color === 3) {
shadowMarker = L.circleMarker([dcStat.latitude, dcStat.longitude], { opacity: 0, fillOpacity: 0, radius: symbolSize + 10 })
marker = L.circleMarker([dcStat.latitude, dcStat.longitude], { interactive: false, color: '#9e9c98', opacity: 0.42, fillColor: '#9e9c98', fillOpacity: 0.5, radius: symbolSize, className: 'real-marker' })
}
shadowMarker.bindTooltip(this.mapTooltipFormatter(dcStat), { sticky: false, pane: 'myPane', direction: 'left', data: dcStat })
shadowMarker.on('mouseover', (param) => {
const point = param.containerPoint
const event = param.originalEvent
const boxWidth = window.innerWidth / 2
this.tooltip.x = event.clientX + point.x - event.layerX - 5
this.tooltip.y = event.clientY + point.y - event.layerY
if (boxWidth > this.tooltip.x) {
this.tooltip.x = this.tooltip.x + 175 + 10
}
})
shadowMarker.addTo(this.map)
marker.addTo(this.map)
dcStat.symbolSize = symbolSize
}
})
this.renderPoint(dcStats)
self.map.on('mouseenter', 'pointLayer', function (param) {
console.log('mouseenter', param)
const point = param.point
const event = param.originalEvent
const boxWidth = window.innerWidth / 2
const boxHeight = window.innerHeight / 2
self.tooltip.x = event.clientX + point.x - event.layerX + 15
self.tooltip.y = event.clientY + point.y - event.layerY + 15
if (self.tooltip.x > boxWidth) {
self.tooltip.x = self.tooltip.x - 200 - 15
}
if (self.tooltip.y > boxHeight) {
self.tooltip.y = self.tooltip.y - 160 - 15
}
self.dcStat = {
...param.features[0].properties,
asset: JSON.parse(param.features[0].properties.asset),
agent: JSON.parse(param.features[0].properties.agent)
}
self.tooltip.show = true
})
self.map.on('mouseleave', 'pointLayer', function () {
console.log('mouseleave')
self.tooltip.show = false
self.dcStat = ''
})
self.pointAnimation(0)
})
})
},
mapTooltipFormatter (dcStat) {
const self = this
return `<div class="nz-tooltip-bac tooltip-map" style="z-index: 11111;width: 175px">
<div class="tooltip--title">${dcStat.name}</div>
<div class="tooltip--row">
<div class="legend-value legend-value-asset">
<div class="map-asset">
<div class="progress-title">${self.$t('panel.assetOk')}</div>
<div class="success-progress progress-box">
<div class="top-progress" style="width: ${(dcStat.asset.ok / dcStat.asset.total) * 100}%"></div>
<div style="width: 100%" class="bottom-progress"></div>
</div>
<div class="success-progress progress-content">${dcStat.asset.ok}</div>
</div>
<div class="map-asset">
<div class="progress-title">${self.$t('panel.assetAlarm')}</div>
<div class="error-progress progress-box">
<div class="top-progress" style="width: ${(dcStat.asset.alarm / dcStat.asset.total) * 100}%"></div>
<div style="width: 100%" class="bottom-progress"></div>
</div>
<div class="error-progress progress-content">${dcStat.asset.alarm}</div>
</div>
</div>
<div class="partition"></div>
<div class="legend-value legend-value-agent">
<div class="map-asset">
<div class="progress-title">${self.$t('overall.agent')} ${self.$t('config.agent.up')}</div>
<div class="success-progress progress-box">
<div class="top-progress" style="width: ${(dcStat.agent.up / (dcStat.agent.up + dcStat.agent.down)) * 100}%"></div>
<div style="width: 100%" class="bottom-progress"></div>
</div>
<div class="success-progress progress-content">${dcStat.agent.up}</div>
</div>
<div class="map-asset">
<div class="progress-title">${self.$t('overall.agent')} ${self.$t('asset.down')}</div>
<div class="error-progress progress-box">
<div class="top-progress" style="width: ${(dcStat.agent.down / (dcStat.agent.up + dcStat.agent.down)) * 100}%"></div>
<div style="width: 100%" class="bottom-progress"></div>
</div>
<div class="error-progress progress-content">${dcStat.agent.down}</div>
</div>
</div>
</div>
</div>`
// return tooltip;
renderPoint (dcStats) {
const arr = []
dcStats.forEach(dcStat => {
arr.push({
type: 'Feature',
properties: {
...dcStat
},
geometry: {
type: 'Point',
coordinates: [dcStat.longitude, dcStat.latitude]
}
})
})
this.map.addSource('pointData', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: arr
}
})
this.map.addLayer({
id: 'pointLayer',
type: 'circle',
source: 'pointData',
paint: {
'circle-radius': [
'step',
['get', 'symbolSize'],
10,
13,
13,
18,
18
],
'circle-color': [
'step',
['get', 'color'],
'#23BF9A',
2,
'#EC7F66',
3,
'#9e9c98'
],
'circle-opacity': 0.5
}
})
},
pointAnimation (timeStep) {
const opacity = 0.5 + (timeStep % 1000) / 1000 / 2
this.map.setPaintProperty('pointLayer', 'circle-opacity', [
'step',
['get', 'color'],
0.5,
2,
opacity,
3,
0.5
])
requestAnimationFrame(this.pointAnimation)
},
resize () {
this.initMap()
this.map.resize()
}
},
beforeDestroy () {

View File

@@ -0,0 +1,387 @@
<template>
<div
ref="pie-chart-box"
class="nz-chart__component nz-chart__component--time-series" @mouseenter="mouseEnterChart"
@mouseleave="mouseLeaveChart"
index="300"
>
<div :id="`chart-canvas-${chartId}`" class="chart__canvas">
<svg :id="`sankey-svg-${chartId}`" width="100%" height="100%"></svg>
</div>
<div :class="`chart-canvas-tooltip-${chartId}`" :id="`chart-canvas-tooltip-${chartId}`" class="chart-canvas-tooltip" :style="{left:tooltip.x+'px',top:tooltip.y+'px'}" v-if="tooltip.show">
<div class="chart-canvas-tooltip-title tooltip-title">
{{tooltip.title}}
</div>
<div class="chart-canvas-tooltip-content">
<div>value</div>
<div>
<div v-if="tooltip.mapping && tooltip.mapping.icon" style="display: inline-block">
<i :class="tooltip.mapping.icon" :style="{color: tooltip.mapping.color.icon}"></i>
</div>
<div style="display: inline-block">{{tooltip.value}}</div>
</div>
</div>
</div>
</div>
</template>
<script>
import chartMixin from '@/components/chart/chartMixin'
import chartFormat from '@/components/chart/chartFormat'
import * as d3 from 'd3'
import * as d3Sankey from 'd3-sankey'
import { getMetricTypeValue } from '@/components/common/js/tools'
import chartDataFormat from '@/components/chart/chartDataFormat'
import { initColor } from '@/components/chart/chart/tools'
import { randomcolor } from '@/components/common/js/radomcolor/randomcolor'
import lodash from 'lodash'
export default {
name: 'chart-sankey',
mixins: [chartMixin, chartFormat],
props: {
chartInfo: Object,
chartData: Array,
chartOption: Object,
isFullscreen: Boolean
},
data () {
return {
colorList: [],
isInit: true, // 是否是初始化初始化时为true图表初始化结束后设为false
chartId: '',
linksData: [],
nodesData: [],
tooltip: {
x: 0,
y: 0,
title: 0,
value: 0,
mapping: {},
show: false
}
}
},
methods: {
initChart () {
this.linksData = this.initsankeyData(this.chartInfo, this.chartData) // 生成links
this.isNoData = !this.linksData.length
this.$emit('chartIsNoData', this.isNoData)
if (this.isNoData) {
return
}
// 根据link获取node
this.linksData.forEach(item => {
this.nodesData.push({ node: item.source })
this.nodesData.push({ node: item.target })
})
// 去重相同的node
for (let i = 0; i < this.nodesData.length; i++) {
for (let j = i + 1; j < this.nodesData.length; j++) {
if (this.nodesData[i].node === this.nodesData[j].node) {
this.nodesData.splice(j, 1)
j = j - 1
}
}
}
/* 使用setTimeout延迟渲染图表避免样式错乱 */
setTimeout(() => {
this.drawSankeyChart()
this.isInit = false
}, 200)
},
initsankeyData (chartInfo, originalDatas) {
this.linksData = []
this.nodesData = []
const sankeyData = []
const decimals = this.chartInfo.param.decimals || 2
originalDatas.forEach((originalData) => {
originalData.forEach((data, dataIndex) => {
this.isNoData = false
const value = getMetricTypeValue(data.values, chartInfo.param.statistics)
const obj = {
value: value,
realValue: value,
labels: data.metric,
dataIndex: dataIndex
}
if (data.metric[chartInfo.param.sourceLabel] && data.metric[chartInfo.param.targetLabel]) {
obj.source = data.metric[chartInfo.param.sourceLabel]
obj.target = data.metric[chartInfo.param.targetLabel]
sankeyData.push(obj)
}
})
})
// 汇总 sourcetarget 相同的数据
const links = []
const tempObj = {}
sankeyData.forEach((item) => {
const key = item.source + '-' + item.target
if (!Object.prototype.hasOwnProperty.call(tempObj, key)) {
tempObj[key] = item
tempObj[key].showValue = chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(tempObj[key].value, null, -1, decimals)
} else {
const num1 = parseFloat(tempObj[key].value)
const num2 = parseFloat(item.value)
tempObj[key].value = num1 + num2
tempObj[key].showValue = chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(tempObj[key].value, null, -1, decimals)
}
})
for (const key in tempObj) {
links.push(tempObj[key])
}
return links
},
drawSankeyChart () {
this.$nextTick(() => {
// 清空作图区域
d3.select(`#sankey-svg-${this.chartId}`).selectAll('g').remove()
// 获取svg宽高 初始化画布
const svgDom = document.getElementById(`sankey-svg-${this.chartId}`)
const width = svgDom && svgDom.getBoundingClientRect().width
const height = svgDom && svgDom.getBoundingClientRect().height
const margin1 = 100
const margin2 = 50
const svg = d3.select(`#sankey-svg-${this.chartId}`)
const chart = svg.append('g').attr('transform', `translate(${margin2}, ${margin2})`)
// 创建桑基图生成器
const sankey = d3Sankey
.sankey()
.nodeWidth(20)
.nodePadding(20)
.size([width - 2 * margin1, height - 2 * margin2])
.nodeId((d) => d.node)
const nodesData = lodash.cloneDeep(this.nodesData)
const linksData = lodash.cloneDeep(this.linksData)
const { nodes, links } = sankey({
nodes: nodesData,
links: linksData
})
// 设置节点颜色
nodes.forEach((item, index) => {
if (index >= 20) {
const colorRandom = randomcolor()
this.colorList.push(colorRandom)
}
const mapping = this.selectMapping(item.value, this.chartInfo.param.valueMapping, this.chartInfo.param.enable && this.chartInfo.param.enable.valueMapping)
item.mapping = mapping
item.background = mapping ? mapping.color.bac : this.colorList[index]
const decimals = this.chartInfo.param.decimals || 2
item.showValue = chartDataFormat.getUnit(this.chartInfo.unit ? this.chartInfo.unit : 2).compute(item.value, null, -1, decimals)
})
// 创建一个连线绘制组,绑定连线数据(links)
chart
.append('g')
.attr('fill', 'none')
.selectAll()
.data(links)
.join('path')
.attr('linkNodes', (d) => { // 设置与当前连线相连的节点(必须以字母开头)
return 'i-' + d.source.index + ' ' + 'i-' + d.target.index
})
.attr('d', d3Sankey.sankeyLinkHorizontal())
.attr('stroke', (d, i) => {
return d.source.background
})
.attr('stroke-width', (d) => d.width)
.style('stroke-opacity', '0.5')
.attr('cursor', 'pointer')
.style('transition', 'all 0.3s')
// 创建一个节点绘制组,绑定节点数据(nodes)。
chart
.append('g')
.selectAll()
.data(nodes)
.join('g')
.attr('class', 'node')
.attr('linkNodes', (d) => { // 设置与当前节点相连的节点(必须以字母开头)
let nodeStr = ''
d.targetLinks.forEach(link => {
nodeStr += 'i-' + link.source.index + ' '
})
nodeStr += 'i-' + d.index
d.sourceLinks.forEach(link => {
nodeStr += ' ' + 'i-' + link.target.index
})
return nodeStr
})
.attr('index', (d) => { // 设置标识(必须以字母开头)
return 'i-' + d.index
})
.append('rect')
.attr('fill', (d, i) => {
return d.background
})
.attr('x', (d) => d.x0)
.attr('y', (d) => d.y0)
.attr('height', (d) => d.y1 - d.y0)
.attr('width', (d) => d.x1 - d.x0)
.attr('cursor', 'pointer')
.style('transition', 'all 0.3s')
// 节点添加文字
chart
.selectAll('.node')
.append('foreignObject')
// .attr('width', 20)
.attr('height', function (d) { return d.y1 - d.y0 })
.attr('x', function (d) { return d.x0 + 30 })
.attr('y', function (d) { return d.y0 })
.style('overflow', 'visible')
.style('cursor', 'pointer')
.style('transition', 'all 0.3s')
.html((d) => {
return this.sankeyFormatterLabel(d)
})
// 划过连线
chart.selectAll('path')
.on('mouseover', (e, d) => {
chart.selectAll('.node, path').style('fill-opacity', '0.1').style('stroke-opacity', '0.1')
chart.selectAll('.node').selectAll('foreignObject').style('opacity', '0.1')
const hoverNodes = d3.select(e.target).style('stroke-opacity', '0.8').attr('linkNodes').split(' ')
hoverNodes.forEach((index) => {
chart.selectAll('[index=' + index + ']').style('fill-opacity', '1').selectAll('foreignObject').style('opacity', '1')
})
// 显示悬浮框
this.tooltip.title = d.source.node + ' ——> ' + d.target.node
this.tooltip.value = d.showValue
this.tooltip.mapping = ''
this.tooltip.show = true
this.setPosition(e)
})
.on('mousemove', (e) => {
if (this.tooltip.show) {
this.setPosition(e)
}
})
.on('mouseleave', () => {
chart.selectAll('.node, path').style('fill-opacity', '1').style('stroke-opacity', '0.5')
chart.selectAll('.node').selectAll('foreignObject').style('opacity', '1')
// 隐藏悬浮框
this.tooltip.show = false
})
// 划过节点
chart.selectAll('.node')
.on('mouseover', (e, d) => {
chart.selectAll('.node, path').style('fill-opacity', '0.1').style('stroke-opacity', '0.1')
chart.selectAll('.node').selectAll('foreignObject').style('opacity', '0.1')
chart.selectAll('[linkNodes~=' + 'i-' + d.index + ']')
.style('fill-opacity', '1')
.style('stroke-opacity', '0.8')
.selectAll('foreignObject')
.style('opacity', '1')
// 显示悬浮框
this.tooltip.title = d.node
this.tooltip.value = d.showValue
this.tooltip.mapping = d.mapping
this.tooltip.show = true
this.setPosition(e)
})
.on('mousemove', (e) => {
if (this.tooltip.show) {
this.setPosition(e)
}
})
.on('mouseleave', () => {
chart.selectAll('.node, path').style('fill-opacity', '1').style('stroke-opacity', '0.5')
chart.selectAll('.node').selectAll('foreignObject').style('opacity', '1')
// 隐藏悬浮框
this.tooltip.show = false
})
})
},
setPosition (e) {
const windowWidth = window.innerWidth// 窗口宽度
const windowHeight = window.innerHeight// 窗口高度
const box = document.getElementById(`chart-canvas-tooltip-${this.chartId}`)
if (box) {
const boxWidth = box.offsetWidth
const boxHeight = box.offsetHeight
if (e.pageX < (windowWidth / 2)) { // 说明鼠标在左边放不下提示框
this.tooltip.x = e.pageX + 15
} else {
this.tooltip.x = e.pageX - boxWidth - 15
}
if (e.pageY + 50 + boxHeight < windowHeight) { // 说明鼠标上面放不下提示框
this.tooltip.y = e.pageY + 15
} else {
this.tooltip.y = e.pageY - boxHeight - 10
}
} else {
this.tooltip.y = e.pageY + 15
this.tooltip.x = e.pageX + 15
}
},
// 处理label
sankeyFormatterLabel (data) {
let str = ''
let valueStr = ''
if (this.chartInfo.param.text === 'all') {
str += data.node
valueStr = data.mapping && data.mapping.display ? this.handleDisplay(data.mapping.display, { value: data.showValue }) : data.showValue
}
if (this.chartInfo.param.text === 'value' || !this.chartInfo.param.text) {
valueStr = data.mapping && data.mapping.display ? this.handleDisplay(data.mapping.display, { value: data.showValue }) : data.showValue
}
if (this.chartInfo.param.text === 'legend') {
str += data.node
}
if (this.chartInfo.param.text === 'none') {
str += ''
}
if (str && valueStr) {
return `
<div style="width:auto;height: 100%;display: flex;justify-content: center;flex-direction: column;color:#000000">
<p style="cursor:pointer;white-space: nowrap;color: ${data.mapping && data.mapping.color && data.mapping.color.text};">
<span>${str}</span>
</p>
<p style="cursor:pointer;white-space: nowrap;color: ${data.mapping && data.mapping.color && data.mapping.color.text};">
<i class="${data.mapping && data.mapping.icon}" style="color: ${data.mapping && data.mapping.color && data.mapping.color.icon};font-size:1em;"></i>
<span>${valueStr}</span>
</p>
</div>
`
} else if (str) {
return `
<div style="width:auto;height: 100%;display: flex;justify-content: center;flex-direction: column;color:#000000">
<p style="cursor:pointer;white-space: nowrap;color: ${data.mapping && data.mapping.color && data.mapping.color.text};">
<i class="${data.mapping && data.mapping.icon}" style="color: ${data.mapping && data.mapping.color && data.mapping.color.icon};font-size:1em;"></i>
<span>${str}</span>
</p>
</div>
`
} else if (valueStr) {
return `
<div style="width:auto;height: 100%;display: flex;justify-content: center;flex-direction: column;color:#000000">
<p style="cursor:pointer;white-space: nowrap;color: ${data.mapping && data.mapping.color && data.mapping.color.text};">
<i class="${data.mapping && data.mapping.icon}" style="color: ${data.mapping && data.mapping.color && data.mapping.color.icon};font-size:1em;"></i>
<span>${valueStr}</span>
</p>
</div>
`
}
},
resize () {
setTimeout(() => {
this.drawSankeyChart()
}, 50)
}
},
mounted () {
this.colorList = initColor(20)
this.chartInfo.loaded && this.initChart()
}
}
</script>

File diff suppressed because one or more lines are too long

View File

@@ -114,6 +114,9 @@ export function isTable (type) {
export function isGauge (type) {
return type === chartType.gauge
}
export function isSankey (type) {
return type === chartType.sankey
}
export function isClock (type) {
return type === chartType.clock
}

View File

@@ -1165,7 +1165,6 @@ export default {
/* topology 方法 */
/* topology 方法 */
onDrag (event, node) {
console.log(event, node)
this.dragFlag = false
const timer = setTimeout(() => {
this.dragFlag = true
@@ -1174,7 +1173,6 @@ export default {
event.dataTransfer.setData('Text', JSON.stringify({ ...node.data, data: { imageId: node.data.imageId } }))
},
dragFlagChange (node) {
console.log(node)
getTopology(this.topologyIndex).addPen(
{
...node.data,

View File

@@ -0,0 +1,32 @@
<template>
<div class="document-copy-block">
<span class="document-copy-text">
<slot name="copy-text"></slot>
</span>
<i v-if="copyData" class="nz-icon nz-icon-override" style="visibility: hidden" @click="onCopy(copyData)" :title="$t('overall.copyText')"></i>
</div>
</template>
<script>
export default {
name: 'copy',
props: {
copyData: String
},
data () {
return {
}
},
methods: {
onCopy (txt) {
this.$copyText(txt).then(() => {
this.$message.success({ message: this.$t('overall.copySuccess') })
})
}
}
}
</script>
<style>
</style>

View File

@@ -408,6 +408,7 @@ export const chartType = {
table: 'table',
stat: 'stat',
gauge: 'gauge',
sankey: 'sankey',
pie: 'pie',
bubble: 'bubble',
treemap: 'treemap',

View File

@@ -14,7 +14,7 @@
<div class="input-box-item" style="margin-right: unset !important;width: 30px !important;flex:unset;" @click="mapConfigVisible = true"><i class="nz-icon nz-icon-weizhi" style="color:rgb(238, 157, 63)"></i></div>
</div>
<el-dialog :visible.sync="mapConfigVisible" :title="$t('config.system.basic.mapTitle')" width="calc(50% - 10px)" @close="mapClose" @opened="initMap" class=" nz-dialog map-config-dialog" :modal-append-to-body="appendToBody">
<div id="map" style="height: 100%; width: 100%" :style="theme=='dark'? 'filter: invert(1) hue-rotate(0.5turn);opacity: 0.75;': ''"></div>
<div id="map" style="height: 100%; width: 100%"></div>
</el-dialog>
</div>
</template>
@@ -22,9 +22,8 @@
<script>
// 引入Leaflet对象 挂载到Vue上便于全局使用也可以单独页面中单独引用
import 'leaflet/dist/leaflet.css'
import * as L from 'leaflet'
import icon from 'leaflet/dist/images/marker-icon.png'
import iconShadow from 'leaflet/dist/images/marker-shadow.png'
import maplibregl from 'maplibre-gl'
import mapStyle from '@/components/chart/chart/mapStyle'
export default {
name: 'latlngPicker',
props: {
@@ -37,7 +36,7 @@ export default {
theme: localStorage.getItem(`nz-user-${localStorage.getItem('nz-user-id')}-theme`),
lnglat: '',
oldlnglat: '',
mapParam: { longitude: 116.39, latitude: 39.9, zoom: 4, minZoom: 1, maxZoom: 10 },
mapParam: { longitude: 116.39, latitude: 39.9, zoom: 4, minZoom: 1, maxZoom: 7 },
map: null,
marker: null,
mapConfigVisible: false,
@@ -56,6 +55,16 @@ export default {
}
},
mounted () {
const arr = []
mapStyle.layers.forEach(item => {
if (item.layout['text-font']) {
const font = item.layout['text-font'].join(',')
if (arr.indexOf(font) === -1) {
arr.push(font)
}
}
})
console.log(arr)
},
methods: {
initMap () {
@@ -64,53 +73,112 @@ export default {
this.map = null
}
this.setLatlng()
const DefaultIcon = L.icon({
iconUrl: icon,
iconSize: [26, 40],
iconAnchor: [13, 40],
shadowUrl: iconShadow
mapStyle.center = [Number(this.mapParam.longitude), Number(this.mapParam.latitude)]
mapStyle.zoom = Number(this.zoom)
const mapId = document.getElementById('map')
this.map = new maplibregl.Map({
container: mapId,
style: mapStyle,
maxZoom: 7,
minZoom: 1,
// renderWorldCopies: false,
hash: false,
transformRequest: function (url, resourceType) {
console.log(resourceType)
if (resourceType === 'Tile' && url.indexOf('https://api.maptiler.com/tiles/v3') > -1) {
const urlParams = url.split('.pbf')[0].split('/')
const z = urlParams[urlParams.length - 3]
const x = urlParams[urlParams.length - 2]
const y = urlParams[urlParams.length - 1]
const newUrl1 = `nzMap://static/Titles/${z}/${x}/${y}.pbf`
return {
url: newUrl1,
credentials: 'include',
method: 'GET'
// Include cookies for cross-origin requests
}
}
if (resourceType === 'SpriteJSON') {
return {
url: 'nzMap://static/Titles/sprite.json',
credentials: 'include',
method: 'GET'
// Include cookies for cross-origin requests
}
}
if (resourceType === 'SpriteImage') {
return {
url: 'nzMap://static/Titles/sprite.png',
credentials: 'include',
method: 'GET'
// Include cookies for cross-origin requests
}
}
}
})
L.Marker.prototype.options.icon = DefaultIcon
const map = L.map('map', {
minZoom: this.mapParam.minZoom,
maxZoom: this.mapParam.maxZoom,
attributionControl: false,
zoomControl: false,
maxBounds: L.latLngBounds(L.latLng(-90, -180), L.latLng(90, 180))
}).setView([this.mapParam.latitude, this.mapParam.longitude], this.mapParam.zoom)
L.tileLayer(
'/static/Tiles/{z}/{x}/{y}.png',
{ noWrap: true }
).addTo(map)
const attribution = L.control.attribution({ position: 'bottomright', prefix: '' })
attribution.addAttribution(' © OpenStreetMap contributors')
attribution.addTo(map)
L.control.zoom({
position: 'bottomright',
zoomInText: '<i class="nz-icon nz-icon-enlarge"></i>',
zoomOutText: '<i class="nz-icon nz-icon-narrow"></i>',
zoomInTitle: '',
zoomOutTitle: ''
}).addTo(map)
const marker = L.marker([this.mapParam.latitude, this.mapParam.longitude]).addTo(map)
map.on('click', function (e) {
const latitude = e.latlng.lat
const longitude = e.latlng.lng
marker.setLatLng([latitude, longitude])
maplibregl.addProtocol('nzMap', (params, callback) => { // 切片显示接口 防止跨域的问题
fetch(`${params.url.split('://')[1]}`)
.then(t => {
if (t.status == 200) {
t.arrayBuffer().then(arr => {
callback(null, arr, null, null)
})
} else {
callback(new Error(`Tile fetch error: ${t.statusText}`))
}
})
.catch(e => {
callback(new Error(e))
})
return { cancel: () => { } }
})
this.map = map
this.marker = marker
const marker = ''
const self = this
const geoJSON = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: {},
geometry: {
type: 'Point',
coordinates: [Number(self.mapParam.longitude), Number(self.mapParam.latitude)]
}
}
]
}
this.map.on('load', function () {
self.map.addSource('centerMarker',
{
type: 'geojson',
data: geoJSON
})
self.map.addLayer({
id: 'centerMarkerLayer',
type: 'circle',
source: 'centerMarker',
paint: {
'circle-radius': 8,
'circle-color': '#23BF9A',
'circle-opacity': 1
}
})
})
// const marker = L.marker([this.mapParam.latitude, this.mapParam.longitude]).addTo(map)
this.map.on('click', function (e) {
console.log(e, e.lngLat)
const coords = e.lngLat
geoJSON.features[0].geometry.coordinates = [coords.lng, coords.lat]
self.map.getSource('centerMarker').setData(geoJSON)
})
// this.marker = marker
},
mapClose () {
this.changZoom = true
this.mapConfigVisible = false
const latlng = this.marker.getLatLng()
this.mapParam.longitude = latlng.lng.toFixed(7)
this.mapParam.latitude = latlng.lat.toFixed(7)
const latlng = this.map.getSource('centerMarker')._data.features[0].geometry.coordinates
this.mapParam.longitude = latlng[0].toFixed(7)
this.mapParam.latitude = latlng[1].toFixed(7)
this.lnglat = this.mapParam.longitude + ',' + this.mapParam.latitude
this.zoom = this.map.getZoom()
this.setLatlng()
@@ -137,20 +205,6 @@ export default {
this.lnglat = this.mapParam.longitude + ',' + this.mapParam.latitude
}
},
changeLnglat () {
const lnglat = this.lnglat.split(',')
if (lnglat.length !== 2) {
this.$message.error(this.$t('tip.lnglatError'))
return false
}
const lngReg = /^[\-\+]?(0?\d{1,2}\.\d{1,7}|1[0-7]?\d{1}\.\d{1,7}|180\.0{1,7}|0?\d{1,2}|1[0-7]?\d{1}|180)$/ // 经度正则验证
const latReg = /^[\-\+]?([1-8]?\d{1}\.\d{1,7}|90\.0{1,7}|[1-8]?\d{1}|90)$/ // 纬度正则验证
if (!lngReg.test(lnglat[0]) || !latReg.test(lnglat[1])) {
// this.lnglat = this.oldlnglat
this.$message.error(this.$t('tip.lnglatError'))
return false
}
},
queryDefaultMapConfig (data) {
return new Promise(resolve => {
this.$get('/sysConfig?paramKey=map_center_config').then(response => {

View File

@@ -1,6 +1,9 @@
<template>
<div class="login" id="login-bgimg">
<div class="login" id="login-bgimg">
<div class="model"></div>
<div class="stars-wrapper" id="stars-wrapper" v-if="!this.bgImg">
<img src="../../assets/img/starCloud1.svg">
</div>
<div class="login-main">
<div class="logo"><img src="../../assets/img/logo-big.png"></div>
<div class='login-box'>
@@ -116,6 +119,8 @@
import { mapActions } from 'vuex'
import QRCode from 'qrcodejs2'
import bus from '@/libs/bus.js'
import { SVG } from '@svgdotjs/svg.js'
import svgCloud1 from '@/assets/img/starCloud1.svg'
import { get } from '@/http'
export default {
name: 'login',
@@ -371,6 +376,26 @@ export default {
bus.$on('profile-dialog', () => {
this.authBindShow = true
})
},
initStar () {
document.getElementById('login-bgimg').style['background-image'] = 'url()'
const box = document.getElementById('stars-wrapper')
for (let i = 0; i < 3; i++) {
const svg = SVG().addTo(box).size('100%', '100%')
svg.attr('class', 'stars' + i)
svg.attr('width', '100%')
svg.attr('height', '100%')
svg.attr('preserveAspectRatio', 'none')
for (let j = 0; j < 100; j++) {
const circle = svg.circle(r(0.5, 3))
circle.attr('class', 'star')
circle.attr('cx', r(0, 100) + '%')
circle.attr('cy', r(0, 100) + '%')
}
}
function r (m, n) {
return (Math.random() * (n - m) + m).toFixed(2)
}
}
},
watch: {
@@ -386,6 +411,8 @@ export default {
this.bgImg = localStorage.getItem('nz-sys-bgImg')
if (this.bgImg) {
document.getElementById('login-bgimg').style['background-image'] = `url(${this.bgImg})`
} else {
this.initStar()
}
}
}
@@ -406,4 +433,65 @@ export default {
.license-upload .el-upload-list{
display: none;
}
:root {
--twinkle-duration: 4s;
}
.stars-wrapper {
position: absolute;
pointer-events: none;
width: 100vw;
height: 100vh;
background: -webkit-gradient(linear, left top, left bottom, from(#16161d), color-stop(#080430), to(#0D0635));
background: linear-gradient(#16161d, #080430, #0D0635);
overflow: hidden;
z-index: 1;
}
.stars0,
.stars1,
.stars2 {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
-webkit-animation: twinkle var(--twinkle-duration) ease-in-out infinite;
animation: twinkle var(--twinkle-duration) ease-in-out infinite;
}
.stars0 {
-webkit-animation-delay: calc(var(--twinkle-duration) * -0.22);
animation-delay: calc(var(--twinkle-duration) * -0.22);
}
.stars1 {
-webkit-animation-delay: calc(var(--twinkle-duration) * -0.44);
animation-delay: calc(var(--twinkle-duration) * -0.44);
}
.stars2 {
-webkit-animation-delay: calc(var(--twinkle-duration) * -0.66);
animation-delay: calc(var(--twinkle-duration) * -0.66);
}
@-webkit-keyframes twinkle {
25% {
opacity: 0;
}
}
@keyframes twinkle {
25% {
opacity: 0;
}
}
.star {
fill: white;
}
.star:nth-child(3n) {
opacity: 0.8;
}
.star:nth-child(7n) {
opacity: 0.6;
}
.star:nth-child(13n) {
opacity: 0.4;
}
.star:nth-child(19n) {
opacity: 0.2;
}
</style>

View File

@@ -6,7 +6,6 @@ export default {
},
methods: {
appPen (pen) {
console.log(pen)
this.modulesDiff()
const data = pen[0]
if (data.type === 0 && !data.data.moduleId) {
@@ -108,7 +107,6 @@ export default {
getTopology(this.topologyIndex).setValue(data)
},
pensActive (pens) {
console.log(pens)
this.props = {
node: null,
line: null,

View File

@@ -340,6 +340,7 @@
</el-select>
</el-form-item>
</div>
<div class="form-items--half-width-group" v-if="isGauge(chartConfig.type)">
<!--min-->
<el-form-item :label="$t('dashboard.panel.chartForm.min')" class="form-item--half-width">
@@ -360,6 +361,40 @@
show-word-limit v-model="chartConfig.param.max"/>
</el-form-item>
</div>
<div class="form-items--half-width-group" v-if="isSankey(chartConfig.type)" key="sankey">
<!--Source label-->
<el-form-item class="form-item--half-width" :label="$t('dashboard.panel.chartForm.sourceLabel')" prop="param.sourceLabel"
:rules="[
{ required: true, message: $t('validate.required'), trigger: 'blur'},
{ pattern: /^[a-zA-Z_][a-zA-Z0-9_]*$/, message: $t('dashboard.panel.matchRegex'), trigger: 'blur'},
{ validator: labelValidator,trigger: 'blur'},
]"
>
<el-input
size="small"
style="margin-top: 2px"
:placeholder="$t('overall.placeHolder')"
@change="change"
v-model="chartConfig.param.sourceLabel"/>
</el-form-item>
<!--Target label-->
<el-form-item class="form-item--half-width" :label="$t('dashboard.panel.chartForm.targetLabel')" prop="param.targetLabel"
:rules="[
{ required: true, message: $t('validate.required'), trigger: 'blur'},
{ pattern: /^[a-zA-Z_][a-zA-Z0-9_]*$/, message: $t('dashboard.panel.matchRegex'), trigger: 'blur'},
{ validator: labelValidator,trigger: 'blur'},
]"
>
<el-input
size="small"
style="margin-top: 2px"
:placeholder="$t('overall.placeHolder')"
@change="change"
v-model="chartConfig.param.targetLabel"/>
</el-form-item>
</div>
<div class="form-items--half-width-group" v-if="isShowDecimals(chartConfig.type)">
<!--decimals-->
<el-form-item :label="$t('overall.decimal')" class="form-item--half-width">
@@ -898,7 +933,7 @@ import chartTypeShow from '@/components/common/rightBox/chart/chartTypeShow'
import VueTagsInput from '@johmun/vue-tags-input'
import draggable from 'vuedraggable'
import { randomcolor, ColorReverse } from '@/components/common/js/radomcolor/randomcolor'
import { isGauge } from '@/components/chart/chart/tools'
import { isGauge, isSankey } from '@/components/chart/chart/tools'
export default {
name: 'chartConfig',
@@ -969,6 +1004,10 @@ export default {
id: 'gauge',
name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label')
},
{
id: 'sankey',
name: this.$t('dashboard.panel.chartForm.typeVal.sankey.label')
},
{
id: 'treemap',
name: this.$t('dashboard.panel.chartForm.typeVal.treemap.label')
@@ -995,6 +1034,7 @@ export default {
},
methods: {
isGauge,
isSankey,
beforeInit () {
this.promqlType = this.type
this.chartTypeList = this[this.type + 'ChartTypeList']
@@ -1017,6 +1057,18 @@ export default {
this.expressionChange()
}
},
// 变量名校验 防止重复
labelValidator (rule, value, callback) {
const sourceLabel = this.chartConfig.param.sourceLabel
const targetLabel = this.chartConfig.param.targetLabel
setTimeout(() => {
if (sourceLabel === targetLabel) {
callback(new Error(this.$t('error.labelEqual')))
} else {
callback()
}
}, 100)
},
chartTypeChange (type) {
switch (type) {
case 'line':
@@ -1053,8 +1105,9 @@ export default {
case 'stat':
case 'hexagon':
case 'gauge':
case 'sankey':
case 'bubble':
if (this.oldType === 'stat' || this.oldType === 'gauge' || this.oldType === 'hexagon' || this.oldType === 'bubble') {
if (this.oldType === 'stat' || this.oldType === 'gauge' || this.oldType === 'sankey' || this.oldType === 'hexagon' || this.oldType === 'bubble') {
break
}
this.chartConfig.param = {

View File

@@ -74,6 +74,7 @@ export default {
case 'gauge':
case 'pie':
case 'bubble':
case 'sankey':
return true
default: return false
}
@@ -91,6 +92,7 @@ export default {
case 'stat':
case 'hexagon':
case 'gauge':
case 'sankey':
case 'bubble':
return false
default: return false
@@ -106,6 +108,7 @@ export default {
case 'stat':
case 'hexagon':
case 'gauge':
case 'sankey':
case 'treemap':
case 'pie':
case 'bubble':
@@ -134,6 +137,7 @@ export default {
case 'hexagon':
case 'bar':
case 'gauge':
case 'sankey':
case 'treemap':
case 'pie':
case 'bubble':
@@ -151,6 +155,7 @@ export default {
case 'stat':
case 'hexagon':
case 'gauge':
case 'sankey':
return false
case 'line':
case 'area':
@@ -169,6 +174,7 @@ export default {
case 'stat':
case 'hexagon':
case 'gauge':
case 'sankey':
return true
case 'line':
case 'area':
@@ -201,6 +207,7 @@ export default {
case 'stat':
case 'hexagon':
case 'gauge':
case 'sankey':
return true
default: return false
}

View File

@@ -247,6 +247,10 @@ export default {
id: 'gauge',
name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label')
},
{
id: 'sankey',
name: this.$t('dashboard.panel.chartForm.typeVal.sankey.label')
},
{
id: 'treemap',
name: this.$t('dashboard.panel.chartForm.typeVal.treemap.label')
@@ -294,6 +298,10 @@ export default {
id: 'gauge',
name: this.$t('dashboard.panel.chartForm.typeVal.gauge.label')
},
{
id: 'sankey',
name: this.$t('dashboard.panel.chartForm.typeVal.sankey.label')
},
{
id: 'treemap',
name: this.$t('dashboard.panel.chartForm.typeVal.treemap.label')

View File

@@ -39,16 +39,22 @@
</template>
<template slot-scope="scope" :column="item">
<template v-if="item.prop === 'name'">
<div class="document-copy-block">
<!-- <div class="document-copy-block">
<span class="document-copy-text">{{scope.row.name ? scope.row.name : '-'}}</span>
<i v-if="scope.row.name" class="nz-icon nz-icon-override" style="visibility: hidden" @click="onCopy(scope.row.name)" :title="$t('overall.copyText')"></i>
</div>
</div> -->
<copy :copyData='scope.row.name'>
<template slot="copy-text">
{{scope.row.name ? scope.row.name : '-'}}
</template>
</copy>
</template>
<template v-else-if="item.prop === 'manageIp'">
<div class="document-copy-block">
<span class="document-copy-text">{{scope.row.manageIp ? scope.row.manageIp : '-'}}</span>
<i v-if="scope.row.manageIp" class="nz-icon nz-icon-override" style="visibility: hidden" @click="onCopy(scope.row.manageIp)" :title="$t('overall.copyText')"></i>
</div>
<copy :copyData='scope.row.manageIp'>
<template slot="copy-text">
{{scope.row.manageIp ? scope.row.manageIp : '-'}}
</template>
</copy>
</template>
<template v-else-if="item.prop === 'type'">{{scope.row.type ? scope.row.type.name : '-'}}</template>
<template v-else-if="item.prop === 'state'">{{scope.row.state ? scope.row.state.name : '-'}}</template>
@@ -188,6 +194,7 @@
import table from '@/components/common/mixin/table'
import { showTableTooltip, hideTableTooltip } from '@/components/common/js/tools'
// import bus from '@/libs/bus'
import copy from '@/components/common/copy'
import alertLabel from '@/components/common/alert/alertLabel'
import alertDaysInfo from '@/components/common/alert/alertDaysInfo'
import alertLabelMixin from '@/components/common/mixin/alertLabelMixin'
@@ -196,7 +203,8 @@ export default {
mixins: [table, alertLabelMixin],
components: {
alertLabel: alertLabel,
alertDaysInfo
alertDaysInfo,
copy
},
props: {
showOption: {

View File

@@ -37,16 +37,18 @@
</template>
<template slot-scope="scope" :column="item">
<template v-if="item.prop === 'name'">
<div class="document-copy-block">
<span class="document-copy-text">{{scope.row.name ? scope.row.name : '-'}}</span>
<i v-if="scope.row.name" class="nz-icon nz-icon-override" style="visibility: hidden" @click="onCopy(scope.row.name)" :title="$t('overall.copyText')"></i>
</div>
<copy :copyData='scope.row.name'>
<template slot="copy-text">
{{scope.row.name ? scope.row.name : '-'}}
</template>
</copy>
</template>
<template v-else-if="item.prop === 'type'">
<div class="document-copy-block">
<span class="document-copy-text">{{scope.row.type ? scope.row.type : '-'}}</span>
<i v-if="scope.row.type" class="nz-icon nz-icon-override" style="visibility: hidden" @click="onCopy(scope.row.type)" :title="$t('overall.copyText')"></i>
</div>
<copy :copyData='scope.row.type'>
<template slot="copy-text">
{{scope.row.type ? scope.row.type : '-'}}
</template>
</copy>
</template>
<template v-else-if="item.prop === 'reporter'">
<template>{{scope.row[item.prop] ? scope.row[item.prop].name : '-'}}</template>
@@ -120,9 +122,14 @@
<script>
import table from '@/components/common/mixin/table'
import copy from '@/components/common/copy'
export default {
name: 'issueTable',
mixins: [table],
components: {
copy
},
props: {
loading: Boolean
},

View File

@@ -38,10 +38,11 @@
</template>
<template slot-scope="scope" :column="item">
<template v-if="item.prop === 'name'">
<div class="document-copy-block">
<span class="document-copy-text">{{scope.row.name ? scope.row.name : '-'}}</span>
<i v-if="scope.row.name" class="nz-icon nz-icon-override" style="visibility: hidden" @click="onCopy(scope.row.name)" :title="$t('overall.copyText')"></i>
</div>
<copy :copyData='scope.row.name'>
<template slot="copy-text">
{{scope.row.name ? scope.row.name : '-'}}
</template>
</copy>
</template>
<template v-else-if="item.prop === 'type'">
<template v-if="scope.row[item.prop]">
@@ -51,10 +52,11 @@
<template v-else>{{"-"}}</template>
</template>
<template v-else-if="item.prop === 'expr'">
<div class="document-copy-block">
<span class="document-copy-text">{{scope.row[item.prop] ? scope.row[item.prop] : '-'}}</span>
<i v-if="scope.row[item.prop]" class="nz-icon nz-icon-override" style="visibility: hidden" @click="onCopy(scope.row[item.prop])" :title="$t('overall.copyText')"></i>
</div>
<copy :copyData='scope.row[item.prop]'>
<template slot="copy-text">
{{scope.row[item.prop] ? scope.row[item.prop] : '-'}}
</template>
</copy>
</template>
<template v-else-if="item.prop === 'inr'">
<template>{{scope.row[item.prop] ? scope.row[item.prop] : '-'}}</template>
@@ -134,13 +136,15 @@
<script>
import lodash from 'lodash'
import copy from '@/components/common/copy'
import table from '@/components/common/mixin/table'
import nzAlertTag from '../../../page/alert/nzAlertTag'
import alertLabelMixin from '@/components/common/mixin/alertLabelMixin'
export default {
name: 'recordRuleTable',
components: {
nzAlertTag
nzAlertTag,
copy
},
mixins: [table, alertLabelMixin],
props: {

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 10240-10495

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 10496-10751

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 10752-11007

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 11008-11263

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 12032-12287

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 12288-12543

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 12544-12799

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 12800-13055

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 13056-13311

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 13312-13567

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 13568-13823

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 13824-14079

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 14080-14335

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 14336-14591

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 14592-14847

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 14848-15103

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 15104-15359

View File

@@ -0,0 +1,3 @@

Noto Sans Regular 1536-1791

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 15360-15615

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 15616-15871

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 15872-16127

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 16128-16383

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 16384-16639

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 16640-16895

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 16896-17151

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 17152-17407

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 17408-17663

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 17664-17919

View File

@@ -0,0 +1,3 @@

Noto Sans Regular 1792-2047

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 17920-18175

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 18176-18431

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 18432-18687

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 18688-18943

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 18944-19199

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 19200-19455

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 19456-19711

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 19712-19967

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 19968-20223

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 20224-20479

View File

@@ -0,0 +1,3 @@

Noto Sans Regular 2048-2303

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 20480-20735

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 20736-20991

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 20992-21247

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 21248-21503

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 21504-21759

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 21760-22015

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 22016-22271

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 22272-22527

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 22528-22783

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 22784-23039

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 23040-23295

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 23296-23551

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 23552-23807

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 23808-24063

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 24064-24319

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 24320-24575

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 24576-24831

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 24832-25087

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 25088-25343

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 25344-25599

View File

@@ -0,0 +1,3 @@

Noto Sans Regular 2560-2815

View File

@@ -0,0 +1,3 @@
Noto Sans Regular 25600-25855

Some files were not shown because too many files have changed in this diff Show More