Former-commit-id: 2920090ca164794b7f2deef1df29213737f70dfd
TangShanKaiPing
wanggang 6 years ago
parent 8f5256af36
commit cbe440d648

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -37,6 +37,9 @@ namespace Application.Domain.Entities
[Display(Name = "禁用")]
public bool Disabled { get; set; }
[Display(Name = "类型")]
public string Type { get; set; }
public List<Device> Devices { get; set; } = new List<Device>();
public List<Scene> Scenes { get; set; } = new List<Scene>();
}

@ -20,6 +20,9 @@ namespace Application.Models
[Required(ErrorMessage = nameof(RequiredAttribute))]
public string Icon { get; set; }
[Display(Name = "图片")]
public string Image { get; set; }
[Display(Name = "是否在线")]
public bool IsOnline { get; set; }
@ -32,6 +35,12 @@ namespace Application.Models
[Display(Name = "海拔")]
public decimal Altitude { get; set; }
[Display(Name = "类型")]
public string Type { get; set; }
[Display(Name = "标记")]
public string Tag { get; set; }
[Display(Name = "禁用")]
public bool Disabled { get; set; }
}

@ -20,6 +20,7 @@
 <ItemGroup>
<EmbeddedResource Include="wwwroot\**\*" />
</ItemGroup><ItemGroup>
<None Remove="wwwroot\iot\classroom.jpg" />
<None Remove="wwwroot\iot\electric.png" />
<None Remove="wwwroot\iot\gateway.png" />
<None Remove="wwwroot\iot\lighting.png" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

@ -54,7 +54,9 @@ namespace IoTNode
Name = $"½Úµã{cpuid}",
Number = cpuid,
Icon = "classroom",
IsOnline = true
IsOnline = true,
Type = "智慧教室",
Image = "/iot/classroom.jpg"
});
dbContext.SaveChanges();
base.Seed(dbContext, serviceProvider, configuration);

@ -53,10 +53,8 @@ namespace IoTCenter.Controllers
public IActionResult GetNodeList(string token)
{
var userName = this._jwtHelper.GetPayload(token)["UserName"].ToString();
var nodes = this._nodeRepo.ReadOnlyTable()
var model = this._nodeRepo.ReadOnlyTable()
.Include(o => o.Scenes)
.ToList();
var model = nodes
.Select(o => new
{
o.Id,
@ -64,9 +62,9 @@ namespace IoTCenter.Controllers
o.Name,
o.DisplayOrder,
o.Icon,
Image = this._deviceRepo.ReadOnlyTable().Include(d => d.Data).FirstOrDefault(c => c.Name == "摄像头" && c.Tag == "front")?.Data.FirstOrDefault(d => d.Key == "Snapshot")?.Value,
Scenes = o.Scenes.Where(s => s.Tag == "front"),
Count = this._deviceRepo.ReadOnlyTable().Where(d => d.NodeId == o.Id).Count()
o.Image,
o.Scenes,
Count = o.Devices.Count
})
.ToList();
return Json(model);

@ -13,18 +13,79 @@ namespace IoTCenter.Controllers
{
private readonly IRepository<Node> _nodeRepo;
private readonly IRepository<Device> _deviceRepo;
private readonly IRepository<Data> _dataRepo;
private readonly IRepository<Category> _categoryRepo;
private readonly IRepository<Product> _productRepo;
public HomeController(IRepository<Node> nodeRepo, IRepository<Device> deviceRepo)
public HomeController(IRepository<Node> nodeRepo, IRepository<Device> deviceRepo, IRepository<Data> dataRepo, IRepository<Category> categoryRepo, IRepository<Product> productRepo)
{
this._nodeRepo = nodeRepo;
this._deviceRepo = deviceRepo;
this._dataRepo = dataRepo;
this._categoryRepo = categoryRepo;
this._productRepo = productRepo;
}
[Authorize]
public IActionResult Index()
{
return View();
}
public IActionResult GetNodeList()
{
var categorys = this._deviceRepo.ReadOnlyTable()
.GroupBy(o => o.Product.Category.Name)
.Select(g => new { g.Key, Count = g.Count() });
var nodes = this._nodeRepo.ReadOnlyTable()
.GroupBy(o => o.Type)
.Select(g => new { g.Key, Count = g.Count() });
var energy = this._dataRepo.ReadOnlyTable()
.Where(o => o.Key == "Electricity")
.Select(o => new
{
o.Device.Node.Name,
o.Value
}).ToList()
.Select(o => new
{
o.Name,
Value = Convert.ToDouble(o.Value)
})
.GroupBy(o => o.Name)
.Select(g => new { g.Key, Sum = g.Sum(o => o.Value) });
var model = new
{
NodeChart = new
{
total = this._nodeRepo.ReadOnlyTable().Count(),
online = this._nodeRepo.ReadOnlyTable().Count(o => o.IsOnline),
offline = this._nodeRepo.ReadOnlyTable().Count(o => !o.IsOnline),
data = nodes.Select(o => o.Count),
labels = nodes.Select(o => o.Key)
},
DeviceChart = new
{
total = this._deviceRepo.ReadOnlyTable().Count(),
online = this._deviceRepo.ReadOnlyTable().Count(o => o.IsOnline),
offline = this._deviceRepo.ReadOnlyTable().Count(o => !o.IsOnline),
data = categorys.Select(o => o.Count),
labels = categorys.Select(o => o.Key)
},
EnergyChart = new
{
total = energy.Sum(o => o.Sum),
data = energy.Select(o => o.Sum),
labels = energy.Select(o => o.Key)
},
Nodes = this._nodeRepo.ReadOnlyTable().Where(o => !o.Disabled).Include(o => o.Scenes).ToList()
};
return Json(model, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
}
//public IActionResult GetNodes()
//{
// var model = this._nodeRepo.ReadOnlyTable()
@ -33,11 +94,21 @@ namespace IoTCenter.Controllers
// return Json(model, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
//}
//public IActionResult Node(Guid id)
//{
// var model = this._nodeRepo.ReadOnlyTable().FirstOrDefault(o => o.Id == id);
// return View(model);
//}
[Route("/Node")]
public IActionResult Node(string number)
{
return View(model: number);
}
public IActionResult GetNode(string number)
{
var model = this._nodeRepo.ReadOnlyTable()
.Include(o => o.Scenes)
.Include(o => o.Devices)
.ThenInclude(o => o.Data)
.FirstOrDefault(o => o.Number == number);
return Json(model, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });
}
//public IActionResult GetNode(Guid id)
//{

@ -3,6 +3,14 @@
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
</PropertyGroup>
<ItemGroup>
<Content Remove="wwwroot\home.default.html" />
<Content Remove="wwwroot\node.default.html" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="wwwroot\node.default.html" />
<EmbeddedResource Include="wwwroot\home.default.html" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />

File diff suppressed because it is too large Load Diff

@ -0,0 +1,165 @@
@model string
@inject IConfiguration cfg
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport" />
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="stylesheet" href="~/lib/font-awesome/css/font-awesome.min.css" />
<link rel="stylesheet" href="~/lib/bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/lib/admin-lte/css/AdminLTE.min.css" />
<link rel="stylesheet" href="~/lib/admin-lte/css/skins/_all-skins.min.css" />
<link rel="stylesheet" href="~/lib/fancybox/jquery.fancybox.min.css" />
<link rel="stylesheet" href="~/lib/jquery-datetimepicker/jquery.datetimepicker.min.css" />
<link rel="stylesheet" href="~/lib/layer/skin/layer.css" />
<link rel="stylesheet" href="~/lib/select2/css/select2.min.css" />
<link rel="stylesheet" href="~/lib/tree-multiselect/dist/jquery.tree-multiselect.min.css" />
<link rel="stylesheet" href="~/lib/kindeditor/themes/default/default.css" />
<link rel="stylesheet" href="~/css/site.css">
<style>
input.switch {
display: none;
}
label.switch {
display: inline-block;
width: 60px;
height: 30px;
border-radius: 30px;
}
label.switch.on {
background-color: green;
}
label.switch.off {
background-color: grey;
}
label.switch.on::before {
content: '';
display: block;
width: 30px;
height: 30px;
border-radius: 30px;
background-color: white;
position: absolute;
left: 0;
}
label.switch.off::after {
content: '';
display: block;
width: 30px;
height: 30px;
border-radius: 30px;
background-color: white;
position: absolute;
right: 0;
}
</style>
<title>@HtmlTitle | @cfg["name"]</title>
</head>
<body class="hold-transition skin-blue-light layout-top-nav fixed">
<div class="wrapper">
<header class="main-header">
<nav class="navbar navbar-static-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="@Url.Action("Index","Home")"><img style="display:inline;max-height:30px;margin:-5px 0;" src="@cfg["logo"]" /> | @cfg["name"]</a>
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
<i class="fa fa-bars"></i>
</button>
</div>
<div class="navbar-custom-menu">
@if (User.Identity.IsAuthenticated)
{
using (Html.BeginForm("Logout", "Account"))
{
@Html.AntiForgeryToken()
<ul class="nav navbar-nav">
<li>
<a href="@Url.Action("Index", "Account" , new { area="" })">@User.Identity.Name</a>
</li>
<li>
<a href="@Url.Action("Logout", "Account" , new { area="" })">退出</a>
</li>
</ul>
}
}
else
{
<ul class="nav navbar-nav">
<li>
<a href="@Url.Action("Register","Account",new { area="" })">注册</a>
</li>
<li>
<a href="@Url.Action("Login","Account",new { area="" })">登录</a>
</li>
</ul>
}
</div>
</div>
</nav>
</header>
<noscript>
<div class="callout callout-danger">
<h4><i class="icon fa fa-ban"></i> 警告!</h4>
<p>Javascript处于禁用状态</p>
</div>
</noscript>
<div class="content-wrapper">
<div class="container">
<section class="content" id="template">
</section>
</div>
</div>
<footer class="main-footer">
<div class="container">
<div class="pull-left">
@Html.Raw(cfg["copyright"].Replace("{now}", DateTime.Now.Year.ToString()))
</div>
<div class="pull-right hidden-xs">
<span>版本:@Html.Raw(cfg["version"]) 设备Id@DeviceAttribute.DeviceId </span>
@if (User.Identity.IsAuthenticated)
{
<a href="@Url.Action("Index","Home",new { area="Admin" })">管理</a>
}
</div>
</div>
</footer>
</div>
<a id="backTop" class="btn btn-back-top bg-light-blue-active"></a>
<script src="~/lib/jquery/jquery.min.js"></script>
<script src="~/lib/vue/vue.min.js"></script>
<script src="~/lib/jquery-validation/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
<script src="~/lib/jquery-ajax-unobtrusive/jquery.unobtrusive-ajax.min.js"></script>
<script src="~/lib/bootstrap/js/bootstrap.min.js"></script>
<script src="~/lib/admin-lte/js/adminlte.min.js"></script>
<script src="~/lib/fancybox/jquery.fancybox.min.js"></script>
<script src="~/lib/jquery-datetimepicker/jquery.datetimepicker.full.min.js"></script>
<script src="~/lib/layer/layer.js"></script>
<script src="~/lib/select2/js/select2.full.min.js"></script>
<script src="~/lib/select2/js/i18n/zh-CN.js"></script>
<script src="~/lib/URI.js/URI.min.js"></script>
<script src="~/lib/tree-multiselect/dist/jquery.tree-multiselect.min.js"></script>
<script src="~/lib/kindeditor/kindeditor-all-min.js"></script>
<script src="~/lib/Chart.js/Chart.bundle.min.js"></script>
<script>
$(function () {
var template = 'node.default.html';//get template name from server
var url = template + '?number=' + new URI().query(true).number;
$.get(template, function (html) {
$('#template').html(html);
})
});
</script>
<script src="~/js/site.js"></script>
</body>
</html>

@ -0,0 +1,162 @@
<div class="row" v-if="ViewModel">
<div class="col-md-6">
<div class="box box-primary">
<div class="box-body row">
<div class="col-md-6">
<canvas id="NodeChart" style="width:100%;height:250px;"></canvas>
<div class="description-block border-right">
<span class="description-text">节点:{{ViewModel.NodeChart.total}} 在线:{{ViewModel.NodeChart.online}} 离线:{{ViewModel.NodeChart.offline}}</span>
</div>
</div>
<div class="col-md-6">
<canvas id="DeviceChart" style="width:100%;height:250px;"></canvas>
<div class="description-block">
<span class="description-text">设备:{{ViewModel.DeviceChart.total}} 在线:{{ViewModel.DeviceChart.online}} 离线:{{ViewModel.DeviceChart.offline}}</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="box box-primary">
<div class="box-body row">
<div class="col-md-12">
<canvas id="EnergyChart" style="width:100%;height:250px;"></canvas>
<div class="description-block">
<span class="description-text">总电量:{{ViewModel.EnergyChart.total}} 千瓦时</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row" v-if="ViewModel">
<div class="col-md-4">
<select class="form-control select search">
<option value="">选择教室</option>
<option v-for="node in ViewModel.Nodes" value="node.Number">{{node.Name}}</option>
</select>
</div>
<div class="col-md-2">
</div>
<div class="col-md-1">
<button class="btn btn-block btn-primary">一键开</button>
</div>
<div class="col-md-1">
<button class="btn btn-block btn-primary">一键关</button>
</div>
<div class="col-md-1">
<button class="btn btn-block btn-primary">开关开</button>
</div>
<div class="col-md-1">
<button class="btn btn-block btn-primary">开关关</button>
</div>
<div class="col-md-1">
<button class="btn btn-block btn-primary">插座开</button>
</div>
<div class="col-md-1">
<button class="btn btn-block btn-primary">插座关</button>
</div>
</div>
<hr />
<div class="row" v-if="ViewModel">
<div class="col-md-4" v-for="node in ViewModel.Nodes">
<div class="box box-primary">
<div class="box-header">
<h3 class="box-title">{{node.Name}}</h3>
<div class="pull-right box-tools">
<label class="switch on"><input class="switch" type="checkbox" checked /></label>
</div>
</div>
<div class="box-body">
<a :href="'/Node?number='+node.Number"><img :src="node.Image" style="max-width:100%;height:auto;border-radius:10px;" /></a>
</div>
<div class="box-footer">
<div class="row">
<div class="col-md-6">
<button class="btn btn-block btn-primary">一键开</button>
</div>
<div class="col-md-6">
<button class="btn btn-block btn-primary">一键关</button>
</div>
</div>
<hr />
<div class="row" v-if="node.Scenes.length">
<template v-for="scene in node.Scenes">
<div class="col-md-3" v-if="scene.Tag=='home'">
<button class="btn btn-block btn-info">{{scene.Name}}</button>
</div>
</template>
</div>
</div>
</div>
</div>
</div>
<script>
function UpdateChart(id, title, data, labels, colors, type) {
var ctx = document.getElementById(id).getContext('2d');
colors = colors || $.map(data, function (item) { return Color16(item); });
var data = {
datasets: [{
label: title,
data: data,
backgroundColor: colors
}],
labels: labels
};
var options = {
responsive: true,
legend: {
position: 'right',
},
title: {
display: true,
text: title
},
animation: {
duration: 0
}
};
var chart = new Chart(ctx, {
type: type,
data: data,
options: options
});
}
function Color16() {//十六进制颜色随机
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
var color = '#' + r.toString(16) + g.toString(16) + b.toString(16);
return color;
}
$('body').on('change', 'input.switch', function (e) {
if ($(this).is(':checked')) {
$(this).parent('label').addClass('on');
$(this).parent('label').removeClass('off');
} else {
$(this).parent('label').addClass('off');
$(this).parent('label').removeClass('on');
}
});
</script>
<script>
var vm = new Vue({
el: '#template',
data() {
return { ViewModel: null }
},
mounted() {
var url = '/Home/GetNodeList';
$.get(url, function (data) {
vm.ViewModel = data;
Vue.nextTick(function () {
UpdateChart('NodeChart', '节点', vm.ViewModel.NodeChart.data, vm.ViewModel.NodeChart.labels, null, 'pie');
UpdateChart('DeviceChart', '设备', vm.ViewModel.DeviceChart.data, vm.ViewModel.DeviceChart.labels, null, 'doughnut');
UpdateChart('EnergyChart', '用电', vm.ViewModel.EnergyChart.data, vm.ViewModel.EnergyChart.labels, null, 'bar');
});
});
}
});
</script>

@ -0,0 +1,75 @@
<div class="row" v-if="ViewModel">
<div class="col-md-12">
</div>
</div>
<hr />
<div class="row" v-if="ViewModel">
节点详情
</div>
<script>
function UpdateChart(id, title, data, labels, colors, type) {
var ctx = document.getElementById(id).getContext('2d');
colors = colors || $.map(data, function (item) { return Color16(item); });
var data = {
datasets: [{
label: title,
data: data,
backgroundColor: colors
}],
labels: labels
};
var options = {
responsive: true,
legend: {
position: 'right',
},
title: {
display: true,
text: title
},
animation: {
duration: 0
}
};
var chart = new Chart(ctx, {
type: type,
data: data,
options: options
});
}
function Color16() {//十六进制颜色随机
var r = Math.floor(Math.random() * 256);
var g = Math.floor(Math.random() * 256);
var b = Math.floor(Math.random() * 256);
var color = '#' + r.toString(16) + g.toString(16) + b.toString(16);
return color;
}
$('body').on('change', 'input.switch', function (e) {
if ($(this).is(':checked')) {
$(this).parent('label').addClass('on');
$(this).parent('label').removeClass('off');
} else {
$(this).parent('label').addClass('off');
$(this).parent('label').removeClass('on');
}
});
</script>
<script>
var vm = new Vue({
el: '#template',
data() {
return { ViewModel: null }
},
mounted() {
var url = '/Home/GetNode' + '?number=' + new URI().query(true).number;
$.get(url, function (data) {
vm.ViewModel = data;
Vue.nextTick(function () {
//UpdateChart('NodeChart', '节点', vm.ViewModel.NodeChart.data, vm.ViewModel.NodeChart.labels, null, 'doughnut');
//UpdateChart('DeviceChart', '设备', vm.ViewModel.DeviceChart.data, vm.ViewModel.DeviceChart.labels, null, 'pie');
//UpdateChart('EnergyChart', '用电', vm.ViewModel.EnergyChart.data, vm.ViewModel.EnergyChart.labels, null, 'bar');
});
});
}
});
</script>
Loading…
Cancel
Save