You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
332 lines
13 KiB
332 lines
13 KiB
@model List<Node>
|
|
@inject IConfiguration cfg
|
|
@{
|
|
Layout = null;
|
|
}
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>@cfg["name"]</title>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui, viewport-fit=cover">
|
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
<meta name="theme-color" content="#2196f3">
|
|
<link rel="stylesheet" href="~/lib/framework7/css/framework7.ios.min.css">
|
|
<style>
|
|
img.font {
|
|
display: inline-block;
|
|
height: 2em;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
img.icon {
|
|
display: block;
|
|
height: 60px;
|
|
max-width: 160px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
img.btn {
|
|
min-width: 20px;
|
|
width: 30px;
|
|
max-width: 60px;
|
|
}
|
|
|
|
.row .col-auto > div {
|
|
text-align: center;
|
|
}
|
|
|
|
.row .col-auto > div > div {
|
|
text-align: left;
|
|
}
|
|
|
|
div.btn {
|
|
padding: 0 .5em;
|
|
}
|
|
|
|
.col-auto {
|
|
padding: 1em;
|
|
}
|
|
|
|
canvas {
|
|
margin: 0 auto;
|
|
max-width: 100%;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="app">
|
|
<div class="view view-main view-init">
|
|
<div class="page">
|
|
<div class="navbar">
|
|
<div class="navbar-inner sliding">
|
|
<div class="left">
|
|
<a href="/Admin" class="link external" data-panel="left">管理</a>
|
|
</div>
|
|
<div class="title">@(cfg["name"])/{{model.length}}</div>
|
|
<div class="right">
|
|
@if (User.Identity.IsAuthenticated)
|
|
{
|
|
<a href="/Account/Logout" class="link external">退出</a>
|
|
}
|
|
else
|
|
{
|
|
<a href="/Account/Login" class="link external">登录</a>
|
|
}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="page-content">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<div class="left">数量统计:{{_.chain(model).flatMap('Devices').value().length}}</div>
|
|
</div>
|
|
<div class="card-content">
|
|
<div class="row">
|
|
<div class="col-auto">
|
|
<canvas id="DeviceTotal" height="300" width="300"></canvas>
|
|
</div>
|
|
<div class="col-auto">
|
|
<div>
|
|
<img src="/iot/10.png" class="icon">
|
|
</div>
|
|
<div>安防<span class="badge color-red">{{getDeviceTotal('安防')}}</span></div>
|
|
</div>
|
|
<div class="col-auto">
|
|
<div>
|
|
<img src="/iot/20.png" class="icon">
|
|
</div>
|
|
<div>电器<span class="badge color-orange">{{getDeviceTotal('电器')}}</span></div>
|
|
</div>
|
|
<div class="col-auto">
|
|
<div>
|
|
<img src="/iot/30.png" class="icon">
|
|
</div>
|
|
<div>照明<span class="badge color-yellow">{{getDeviceTotal('照明')}}</span></div>
|
|
</div>
|
|
<div class="col-auto">
|
|
<div>
|
|
<img src="/iot/40.png" class="icon">
|
|
</div>
|
|
<div>监测<span class="badge color-green">{{getDeviceTotal('监测')}}</span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card" v-for="node in model">
|
|
<div class="card-header">
|
|
<div class="left">
|
|
{{node.Name}}/{{node.Devices.length}}
|
|
</div>
|
|
<div class="right">
|
|
<a :href="'/Home/Node/'+node.Id" class="link external">控制面板</a>
|
|
</div>
|
|
</div>
|
|
<div class="card-content">
|
|
<div class="row">
|
|
<div v-for="device in node.Devices" class="col-auto">
|
|
<div>
|
|
<img :src="'/iot/'+device.Icon+'.png'" class="icon">
|
|
</div>
|
|
<div><a :href="'/Device/Details/'+device.Id" class="link external">{{device.DisplayName}}</a></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<audio id="alarm" muted style="display:none;" src="~/iot/warning.wav"></audio>
|
|
<script src="~/lib/jquery/jquery.min.js"></script>
|
|
<script src="~/lib/lodash/lodash.min.js"></script>
|
|
<script src="~/lib/Chart.js/Chart.bundle.min.js"></script>
|
|
<script src="~/lib/framework7/js/framework7.min.js"></script>
|
|
<script src="~/lib/vue/vue.min.js"></script>
|
|
<script src="~/lib/signalr/signalr.min.js"></script>
|
|
<script>
|
|
const connection = new signalR.HubConnectionBuilder()
|
|
.withUrl("/hub?group=page")
|
|
.build();
|
|
connection.on("UpdateNode", (message) => {
|
|
var node = JSON.parse(message);
|
|
var oldNode = _.chain(vm.model).filter(o => o.Number === node.Number).first().value();
|
|
oldNode.Name = node.Name;
|
|
console.log('update:' + node.Name);
|
|
});
|
|
connection.on("UpdateDevice", (message) => {
|
|
var device = JSON.parse(message);
|
|
device.Data = null;
|
|
device.Apis = null;
|
|
var update = false;
|
|
var node = _.chain(vm.model).filter(o => o.Number === device.Node.Number).first().value();
|
|
for (var i = 0; i < node.Devices.length; i++) {
|
|
if (node.Devices[i].Number == device.Number) {
|
|
update = true;
|
|
break;
|
|
}
|
|
}
|
|
if (update) {
|
|
node.Devices.splice(i, 1, device);
|
|
}
|
|
else {
|
|
node.Devices.push(device);
|
|
}
|
|
console.log('update:' + device.DisplayName);
|
|
});
|
|
connection.on("DeleteDevice", (message) => {
|
|
var number = message;
|
|
for (var i = 0; i < vm.model.length; i++) {
|
|
var node = vm.model[i];
|
|
for (var j = 0; j < node.Devices.length; j++) {
|
|
if (node.Devices[j].Number == number) {
|
|
node.Devices.splice(j, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
console.log('delete:' + number);
|
|
});
|
|
connection.onclose(function (err) {
|
|
console.error(err.toString());
|
|
setTimeout(connect, 15 * 1000);
|
|
});
|
|
function connect() {
|
|
console.log('start connect to server:' + Date());
|
|
connection.start().then(function () {
|
|
|
|
}).catch(function (err) {
|
|
console.error(err.toString());
|
|
setTimeout(connect, 15 * 1000);
|
|
});
|
|
}
|
|
</script>
|
|
<script>
|
|
var app;
|
|
var deviceTotalChart;
|
|
var vm = new Vue({
|
|
el: '#app',
|
|
data: {
|
|
model: []
|
|
},
|
|
mounted: function () {
|
|
this.init();
|
|
connect();
|
|
var theme = 'ios';
|
|
var plugin = {
|
|
params: {
|
|
theme: theme,
|
|
root: '#app',
|
|
}
|
|
};
|
|
if (Framework7.use) {
|
|
Framework7.use(plugin);
|
|
}
|
|
else if (Framework7.Class && Framework7.Class.use) {
|
|
Framework7.Class.use(plugin);
|
|
}
|
|
app = new Framework7();
|
|
this.renderDeviceTotal(true);
|
|
},
|
|
updated: function () {
|
|
console.log('updated');
|
|
},
|
|
watch: {
|
|
'model': {
|
|
handler(val, oldVal) {
|
|
console.log('watch');
|
|
if (this.getDeviceCount(val, '安防') !== this.getDeviceCount(oldVal, '安防')
|
|
|| this.getDeviceCount(val, '电器') !== this.getDeviceCount(oldVal, '电器')
|
|
|| this.getDeviceCount(val, '照明') !== this.getDeviceCount(oldVal, '照明')
|
|
|| this.getDeviceCount(val, '监测') !== this.getDeviceCount(oldVal, '监测')) {
|
|
this.renderDeviceTotal(false);
|
|
}
|
|
},
|
|
deep: true
|
|
}
|
|
},
|
|
methods: {
|
|
init: function () {
|
|
$.getJSON('/Home/GetNodes', function (response) {
|
|
vm.model = response;
|
|
});
|
|
},
|
|
getDeviceTotal: function (categoryName) {
|
|
return _.chain(this.model).flatMap('Devices').filter(o => o.CategoryName === categoryName).value().length;
|
|
},
|
|
getDeviceCount: function (nodes, categoryName) {
|
|
return _.chain(nodes).flatMap('Devices').filter(o => o.CategoryName === categoryName).value().length;
|
|
},
|
|
getData: function (node, dataName) {
|
|
var data = _.chain(node.Devices).flatMap('Data').filter(o => o.Name === dataName).value()[0];
|
|
if (data != null) {
|
|
var result = data.Value;
|
|
if (data.Unit != null && data.Unit !== '') {
|
|
result += ' ' + data.Unit;
|
|
}
|
|
result += ' ' + data.Description;;
|
|
return result;
|
|
}
|
|
return '';
|
|
},
|
|
renderDeviceTotal: function (create) {
|
|
var datas = [
|
|
this.getDeviceTotal('安防'),
|
|
this.getDeviceTotal('电器'),
|
|
this.getDeviceTotal('照明'),
|
|
this.getDeviceTotal('监测'),
|
|
];
|
|
var total = 0;
|
|
datas.forEach(n => total += n);
|
|
var data = {
|
|
datasets: [{
|
|
data: datas,
|
|
backgroundColor: [
|
|
'red',
|
|
'orange',
|
|
'yellow',
|
|
'green',
|
|
],
|
|
label: 'Dataset 1'
|
|
}],
|
|
labels: [
|
|
'安防' + Number(datas[0] / total * 100).toFixed(2) + '%',
|
|
'电器' + Number(datas[1] / total * 100).toFixed(2) + '%',
|
|
'照明' + Number(datas[2] / total * 100).toFixed(2) + '%',
|
|
'监测' + Number(datas[3] / total * 100).toFixed(2) + '%',
|
|
]
|
|
};
|
|
if (create) {
|
|
var ctx = document.getElementById('DeviceTotal').getContext('2d');
|
|
var cfg = {
|
|
type: 'pie',
|
|
data: data,
|
|
options: {
|
|
responsive: true,
|
|
legend: {
|
|
position: 'right',
|
|
},
|
|
title: {
|
|
display: true,
|
|
text: '设备比例'
|
|
},
|
|
animation: {
|
|
animateScale: true,
|
|
animateRotate: true
|
|
}
|
|
}
|
|
};
|
|
deviceTotalChart = new Chart(ctx, cfg);
|
|
}
|
|
else {
|
|
chart = deviceTotalChart;
|
|
chart.data.labels.pop();
|
|
chart.data = data;
|
|
chart.update();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html> |