Introduction of JS based pages
This commit is contained in:
parent
c4cb9ff458
commit
7d64788154
24 changed files with 24446 additions and 30 deletions
|
|
@ -18,8 +18,8 @@ subprojects {
|
|||
apply plugin: 'java-library'
|
||||
|
||||
dependencies {
|
||||
implementation 'se.koc:zutil:1.0.314'
|
||||
//implementation 'se.koc:zutil:1.0.0-SNAPSHOT'
|
||||
//implementation 'se.koc:zutil:1.0.314'
|
||||
implementation 'se.koc:zutil:1.0.0-SNAPSHOT'
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation 'org.hamcrest:hamcrest-core:2.2'
|
||||
|
|
|
|||
0
gradlew
vendored
Executable file → Normal file
0
gradlew
vendored
Executable file → Normal file
40
hal-core/resources/web/js/hal.js
vendored
40
hal-core/resources/web/js/hal.js
vendored
|
|
@ -22,7 +22,7 @@ $(function(){
|
|||
return null;
|
||||
}
|
||||
|
||||
var obj = {};
|
||||
let obj = {};
|
||||
$.each(this[0].attributes, function() {
|
||||
if(this.specified) {
|
||||
obj[this.name] = this.value;
|
||||
|
|
@ -42,26 +42,32 @@ $(function(){
|
|||
// Converts all timestamps to human readable time and date
|
||||
$.fn.relTimestamp = function() {
|
||||
return this.each(function() {
|
||||
var timestamp = parseInt($(this).text());
|
||||
var timestampNow = Date.now();
|
||||
var timeDiff = timestampNow - timestamp;
|
||||
let timestamp = parseInt($(this).text());
|
||||
|
||||
if(timeDiff < 10 * 60 * 1000) // less than 10 min
|
||||
$(this).text(moment(timestamp).fromNow());
|
||||
else if(timeDiff < 24 * 60 * 60 * 1000) // less than 24 hours
|
||||
$(this).text(moment(timestamp).fromNow() + " ("+moment(timestamp).format("HH:mm")+")");
|
||||
else
|
||||
$(this).text(moment(timestamp).format("YYYY-MM-DD HH:mm"));
|
||||
$(this).text(getRelTimestamp(timestamp));
|
||||
return this;
|
||||
});
|
||||
};
|
||||
|
||||
// Converts all timestamps to human readable time and date
|
||||
function getRelTimestamp(timestamp) {
|
||||
let timestampNow = Date.now();
|
||||
let timeDiff = timestampNow - timestamp;
|
||||
|
||||
if(timeDiff < 10 * 60 * 1000) // less than 10 min
|
||||
return moment(timestamp).fromNow();
|
||||
else if(timeDiff < 24 * 60 * 60 * 1000) // less than 24 hours
|
||||
return moment(timestamp).fromNow() + " ("+moment(timestamp).format("HH:mm")+")";
|
||||
else
|
||||
return moment(timestamp).format("YYYY-MM-DD HH:mm");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Chart functions
|
||||
// --------------------------------------------------------
|
||||
|
||||
function createChart(elementId, url, updateTime=-1){
|
||||
var tickConf = {count: 20};
|
||||
let tickConf = {count: 20};
|
||||
if (updateTime < 60*60*1000)
|
||||
tickConf['format'] = '%H:%M';
|
||||
else if (updateTime < 24*60*60*1000)
|
||||
|
|
@ -116,10 +122,10 @@ function updateChart(chart, url, updateTime=-1){
|
|||
}
|
||||
}
|
||||
function getChartData(json){
|
||||
var dataXaxis = {};
|
||||
var dataYaxis = {};
|
||||
var data = [];
|
||||
var labels = [];
|
||||
let dataXaxis = {};
|
||||
let dataYaxis = {};
|
||||
let data = [];
|
||||
let labels = [];
|
||||
|
||||
json.forEach(function(sensor, i) {
|
||||
var index = 'data' + i;
|
||||
|
|
@ -166,8 +172,8 @@ function initDynamicModalForm(modalId, formTemplateId = null, templateID = null)
|
|||
|
||||
// click event
|
||||
$("#" + modalId).on('show.bs.modal', function (event) {
|
||||
var button = $(event.relatedTarget);
|
||||
var modal = $(this);
|
||||
let button = $(event.relatedTarget);
|
||||
let modal = $(this);
|
||||
|
||||
modal.find(" input, select").val('').change(); // Reset all inputs
|
||||
|
||||
|
|
|
|||
39
hal-core/resources/web/js/vue/components/AlertComponent.js
vendored
Normal file
39
hal-core/resources/web/js/vue/components/AlertComponent.js
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
export default {
|
||||
props: [
|
||||
"level", // ERROR, WARNING, SUCCESS, INFO
|
||||
"message"
|
||||
],
|
||||
data() {
|
||||
return { }
|
||||
},
|
||||
computed: {
|
||||
levelClass() {
|
||||
switch(this.level) {
|
||||
case "ERROR": return "alert-danger";
|
||||
case "WARNING": return "alert-warning";
|
||||
case "SUCCESS": return "alert-success";
|
||||
case "INFO":
|
||||
default: return "alert-info";
|
||||
}
|
||||
},
|
||||
levelIcon() {
|
||||
switch(this.level) {
|
||||
case "ERROR": return "glyphicon-minus-sign";
|
||||
case "WARNING": return "glyphicon-warning-sign";
|
||||
case "SUCCESS": return "glyphicon-ok-circle";
|
||||
case "INFO":
|
||||
default: return "glyphicon-info-sign";
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div id="" data-alert-id="" class="alert alert-dismissible fade in" :class="levelClass">
|
||||
<button type="button" class="close" data-dismiss="alert">
|
||||
<span>×</span>
|
||||
</button>
|
||||
<span class="glyphicon" :class="levelIcon"></span>
|
||||
<strong class="alert-title">{{message}}</strong>
|
||||
<span class="alert-description"></span>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
29
hal-core/resources/web/js/vue/components/AlertListComponent.js
vendored
Normal file
29
hal-core/resources/web/js/vue/components/AlertListComponent.js
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import Alert from 'AlertComponent'
|
||||
import { alertStore } from 'AlertStore'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
alertStore
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Alert,
|
||||
},
|
||||
mounted() {
|
||||
alertStore.enableAutoLoad(true);
|
||||
},
|
||||
template: `
|
||||
<div v-if="alertStore.loading" class="modal fade in" tabindex="-1" role="dialog" aria-hidden="true" data-backdrop="false">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content modal-body">
|
||||
<i class="glyphicon glyphicon-refresh glyphicon-refresh-animate"></i>
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="alert-container">
|
||||
<Alert v-for="a in alertStore.alerts" v-bind="a" />
|
||||
</div>`
|
||||
}
|
||||
17
hal-core/resources/web/js/vue/components/App.js
vendored
Normal file
17
hal-core/resources/web/js/vue/components/App.js
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import AlertList from 'AlertListComponent'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
components: {
|
||||
AlertList,
|
||||
},
|
||||
template: `
|
||||
<div class="container col-md-12">
|
||||
<AlertList />
|
||||
|
||||
<router-view></router-view>
|
||||
</div>`
|
||||
}
|
||||
35
hal-core/resources/web/js/vue/components/EventActionComponent.js
vendored
Normal file
35
hal-core/resources/web/js/vue/components/EventActionComponent.js
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import {eventStore} from 'EventStore'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
'id': {default: 0},
|
||||
},
|
||||
data() {
|
||||
let internalEvent = eventStore.getEvent(this.id);
|
||||
return {
|
||||
event: internalEvent,
|
||||
checkboxChecked: internalEvent.data?.valueStr == 'ON',
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="modify">
|
||||
<input type="hidden" name="action-id" :value="event.id">
|
||||
|
||||
<div class="btn-toolbar pull-right">
|
||||
<template v-if="event.dataType === 'ColorEventData'">
|
||||
<input type="hidden" name="type" value="color">
|
||||
<input type="color" name="data" onchange="this.form.submit()" :value="event.data?.valueStr">
|
||||
</template>
|
||||
<template v-else-if="event.dataType === 'LevelEventData'">
|
||||
<input type="hidden" name="type" value="level">
|
||||
<input class="styled-slider slider-progress" type="range" name="data" min="0" max="100" step="10" onchange="this.form.submit()" :value="event.data?.value * 100">
|
||||
</template>
|
||||
<template v-else-if="event.dataType === 'OnOffEventData'">
|
||||
<input type="hidden" name="type" value="on-off">
|
||||
<input class="switch" type="checkbox" name="data" onchange="this.form.submit()" v-model="checkboxChecked">
|
||||
</template>
|
||||
</div>
|
||||
</form>
|
||||
`
|
||||
}
|
||||
88
hal-core/resources/web/js/vue/components/EventDetailPageComponent.js
vendored
Normal file
88
hal-core/resources/web/js/vue/components/EventDetailPageComponent.js
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import {eventStore} from 'EventStore'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
var id = this.$route.params.id;
|
||||
return {
|
||||
id: id,
|
||||
event: eventStore.getEvent(id),
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<h1 class="page-header">Details for <a href="?id={{event.id}}">{{event.name}}</a></h1>
|
||||
|
||||
<div class="col-md-5">
|
||||
<div class="panel panel-default drop-shadow">
|
||||
<div class="panel-heading">Configuration</div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-right">Event ID:</th>
|
||||
<th>{{event.id}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right">Name:</th>
|
||||
<th>{{event.name}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
<th class="text-right">Type:</th>
|
||||
<td>{{event.configType}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right">Owner:</th>
|
||||
<td>{{event.owner}} <p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right">State:</th>
|
||||
<td>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="modify">
|
||||
<input type="hidden" name="action-id" value="{{event.id}}">
|
||||
|
||||
<div class="btn-toolbar pull-left">
|
||||
<input class="toggle-switch" type="checkbox" name="enabled"
|
||||
data-on-color="danger">
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr v-for="(confName, confValue) in event.config">
|
||||
<th class="text-right">{{name}}:</th>
|
||||
<td>{{confValue}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
<div class="panel panel-default drop-shadow">
|
||||
<div class="panel-heading">History data</div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-hover table-condensed">
|
||||
<thead>
|
||||
<th class="col-md-6">Timestamp</th>
|
||||
<th class="col-md-2">Raw Data</th>
|
||||
</thead>
|
||||
|
||||
<tr>
|
||||
<td><span class="timestamp">{{event.data.timestamp}}</span></td>
|
||||
<td>{{event.data.value}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(function (){
|
||||
$(".toggle-switch").on("switchChange.bootstrapSwitch", function (event, state) {
|
||||
$(this).closest('form').submit();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
`
|
||||
}
|
||||
21
hal-core/resources/web/js/vue/components/EventOverviewPageComponent.js
vendored
Normal file
21
hal-core/resources/web/js/vue/components/EventOverviewPageComponent.js
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import EventTable from 'EventTableComponent'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
},
|
||||
components: {
|
||||
EventTable,
|
||||
},
|
||||
template: `
|
||||
<h1 class="page-header">Event Overview</h1>
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default drop-shadow">
|
||||
<div class="panel-heading">Local Events</div>
|
||||
<div class="panel-body">
|
||||
<EventTable />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
34
hal-core/resources/web/js/vue/components/EventTableComponent.js
vendored
Normal file
34
hal-core/resources/web/js/vue/components/EventTableComponent.js
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import {eventStore} from 'EventStore'
|
||||
import EventTableRow from 'EventTableRowComponent'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
eventStore,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
EventTableRow,
|
||||
},
|
||||
mounted() {
|
||||
eventStore.enableAutoLoad(true);
|
||||
},
|
||||
unmounted() {
|
||||
eventStore.enableAutoLoad(false);
|
||||
},
|
||||
template: `
|
||||
<table id="event-device-table2" class="table table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-md-4">Name</th>
|
||||
<th class="col-md-3">Type</th>
|
||||
<th class="col-md-1">Data</th>
|
||||
<th class="col-md-2">Last Update</th>
|
||||
<th class="col-md-2 text-right">Actions</th>
|
||||
</tr>
|
||||
|
||||
<EventTableRow v-for="e in eventStore.events" :key="e.id" :id="e.id" />
|
||||
</thead>
|
||||
</table>
|
||||
`
|
||||
}
|
||||
29
hal-core/resources/web/js/vue/components/EventTableRowComponent.js
vendored
Normal file
29
hal-core/resources/web/js/vue/components/EventTableRowComponent.js
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import {eventStore} from 'EventStore'
|
||||
import EventAction from 'EventActionComponent'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
'id': {default: 0},
|
||||
},
|
||||
data() {
|
||||
let event = eventStore.getEvent(this.id)
|
||||
return {
|
||||
event: event,
|
||||
timestamp: getRelTimestamp(event.data.timestamp)
|
||||
}
|
||||
},
|
||||
components: {
|
||||
EventAction
|
||||
},
|
||||
template: `
|
||||
<tr :data-device-id="event.id">
|
||||
<td><a :href="'?id=' + event.id">{{ event.name }}</a></td>
|
||||
<td>{{ event.configType }}</td>
|
||||
<td>{{ event.data.valueStr }}</td>
|
||||
<td class="timestamp">{{ timestamp }}</td>
|
||||
<td>
|
||||
<EventAction :id="event.id"/>
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
46
hal-core/resources/web/js/vue/stores/AlertStore.js
vendored
Normal file
46
hal-core/resources/web/js/vue/stores/AlertStore.js
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { reactive } from 'vue'
|
||||
|
||||
export const alertStore = reactive({
|
||||
loading: false,
|
||||
alerts: [],
|
||||
pollTimer: null,
|
||||
|
||||
enableAutoLoad(enabled = true) {
|
||||
if (enabled && this.pollTimer !== null) {
|
||||
this.pollTimer = setInterval(function() {
|
||||
load();
|
||||
}, 3000);
|
||||
} else {
|
||||
clearInterval(this.timer);
|
||||
this.pollTimer == null;
|
||||
}
|
||||
},
|
||||
|
||||
load() {
|
||||
fetch('/api/alert?action=poll')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
data.forEach(alert => {
|
||||
alert.source = "server";
|
||||
alertStore.alerts.push(alert);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
setLoading(l = true) {
|
||||
this.loading = l;
|
||||
},
|
||||
|
||||
addAlertInfo(message) {
|
||||
this.alerts.push({source: "local", level: "info", message: message});
|
||||
},
|
||||
addAlertSuccess(message) {
|
||||
this.alerts.push({source: "local", level: "success", message: message});
|
||||
},
|
||||
addAlertWarning(message) {
|
||||
this.alerts.push({source: "local", level: "warning", message: message});
|
||||
},
|
||||
AddAlertError(message) {
|
||||
this.alerts.push({source: "local", level: "danger", message: message});
|
||||
},
|
||||
});
|
||||
49
hal-core/resources/web/js/vue/stores/EventStore.js
vendored
Normal file
49
hal-core/resources/web/js/vue/stores/EventStore.js
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { reactive } from 'vue'
|
||||
import { alertStore } from 'AlertStore'
|
||||
|
||||
export const eventStore = reactive({
|
||||
events: [],
|
||||
pollTimerId: null,
|
||||
pollInterval: 10000, // 10 sec
|
||||
|
||||
enableAutoLoad(enabled = true) {
|
||||
if (enabled) {
|
||||
if (this.pollTimerId !== null)
|
||||
return; // Timer already initialized
|
||||
eventStore.load();
|
||||
|
||||
this.pollTimerId = setInterval(function() {
|
||||
eventStore.load();
|
||||
}, this.pollInterval);
|
||||
} else {
|
||||
clearInterval(this.pollTimerId);
|
||||
this.pollTimerId == null;
|
||||
}
|
||||
},
|
||||
|
||||
load() {
|
||||
alertStore.setLoading(true);
|
||||
|
||||
fetch('/api/event', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
if (json['error'] != null) {
|
||||
alertStore.alertError(json['error']);
|
||||
return;
|
||||
}
|
||||
|
||||
eventStore.events = json;
|
||||
alertStore.setLoading(false);
|
||||
})
|
||||
},
|
||||
|
||||
getEvent(id) {
|
||||
let event = eventStore.events.find(event => id == event.id);
|
||||
return event;
|
||||
}
|
||||
});
|
||||
44
hal-core/resources/web/js/vue/stores/SensorStore.js
vendored
Normal file
44
hal-core/resources/web/js/vue/stores/SensorStore.js
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { reactive } from 'vue'
|
||||
import { alertStore } from 'AlertStore'
|
||||
|
||||
export const sensorStore = reactive({
|
||||
sensors: {},
|
||||
pollTimerId: null,
|
||||
pollInterval: 10000, // 10 sec
|
||||
|
||||
enableAutoLoad(enabled = true) {
|
||||
if (enabled) {
|
||||
if (this.pollTimerId !== null)
|
||||
return; // Timer already initialized
|
||||
sensorStore.load();
|
||||
|
||||
this.pollTimerId = setInterval(function() {
|
||||
sensorStore.load();
|
||||
}, this.pollInterval);
|
||||
} else {
|
||||
clearInterval(this.pollTimerId);
|
||||
this.pollTimerId == null;
|
||||
}
|
||||
},
|
||||
|
||||
load() {
|
||||
alertStore.setLoading(true);
|
||||
|
||||
fetch('/api/sensor', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
if (json['error'] != null) {
|
||||
alertStore.alertError(json['error']);
|
||||
return;
|
||||
}
|
||||
|
||||
sensorStore.sensors = json;
|
||||
alertStore.setLoading(false);
|
||||
})
|
||||
},
|
||||
});
|
||||
3615
hal-core/resources/web/js/vue/vue-router.esm-browser.js
vendored
Normal file
3615
hal-core/resources/web/js/vue/vue-router.esm-browser.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
3615
hal-core/resources/web/js/vue/vue-router.esm-browser.prod.js
vendored
Normal file
3615
hal-core/resources/web/js/vue/vue-router.esm-browser.prod.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
16633
hal-core/resources/web/js/vue/vue.esm-browser.js
vendored
Normal file
16633
hal-core/resources/web/js/vue/vue.esm-browser.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
11
hal-core/resources/web/js/vue/vue.esm-browser.prod.js
vendored
Normal file
11
hal-core/resources/web/js/vue/vue.esm-browser.prod.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -19,11 +19,12 @@
|
|||
<script src="js/lib/bootstrap-switch.min.js"></script>
|
||||
<script src="js/lib/moment.js"></script>
|
||||
<script src="js/hal.js"></script>
|
||||
<script src="js/hal_alert.js"></script>
|
||||
<!-- <script src="js/hal_alert.js"></script> -->
|
||||
|
||||
<!-- charts -->
|
||||
<script src="js/lib/d3.js"></script>
|
||||
<script src="js/lib/c3.js"></script>
|
||||
<script src="js/lib/d3.min.js"></script>
|
||||
<script src="js/lib/force-graph.min.js"></script>
|
||||
<script src="js/lib/c3.min.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
|
|
@ -39,10 +40,52 @@
|
|||
{{/side_navigation}}
|
||||
{{^side_navigation}}<div class="main">{{/side_navigation}}
|
||||
<div id="alert-container"></div>
|
||||
|
||||
<div id="app"></div>
|
||||
|
||||
{{content}}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
"vue": "./js/vue/vue.esm-browser.js",
|
||||
"vue-router": "./js/vue/vue-router.esm-browser.js",
|
||||
"@vue/devtools-api": "https://unpkg.com/@vue/devtools-api@6.4.5/lib/esm/index.js",
|
||||
|
||||
{{#javascriptModules}}"{{.getModuleName()}}": "{{.getScriptPath()}}",
|
||||
{{/javascriptModules}}
|
||||
|
||||
"App": "./js/vue/components/App.js"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
import { createApp } from 'vue'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
{{#javascriptPages}}import {{.getModuleName()}} from '{{.getModuleName()}}'
|
||||
{{/javascriptPages}}
|
||||
|
||||
import App from 'App'
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{{#javascriptPages}}{ name: "{{.getModuleName()}}", path: "{{.getPage()}}", component: {{.getModuleName()}} },
|
||||
{{/javascriptPages}}
|
||||
]
|
||||
});
|
||||
app.use(router);
|
||||
|
||||
app.mount('#app');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package se.hal;
|
|||
import se.hal.daemon.HalExternalWebDaemon;
|
||||
import se.hal.intf.*;
|
||||
import se.hal.intf.HalJavascriptModule.HalJsModule;
|
||||
import se.hal.page.EmptyWebPage;
|
||||
import se.hal.page.StartupWebPage;
|
||||
import se.hal.struct.PluginConfig;
|
||||
import zutil.db.DBConnection;
|
||||
|
|
@ -138,18 +139,22 @@ public class HalServer {
|
|||
http.setDefaultPage(filePage);
|
||||
http.setPage("/", new HttpRedirectPage("/map"));
|
||||
|
||||
for (Iterator<HalWebPage> it = pluginManager.getSingletonIterator(HalApiEndpoint.class); it.hasNext(); )
|
||||
registerPage(it.next());
|
||||
for (Iterator<HalWebPage> it = pluginManager.getSingletonIterator(HalWebPage.class); it.hasNext(); )
|
||||
registerPage(it.next());
|
||||
for (Iterator<HalJavascriptModule> it = pluginManager.getSingletonIterator(HalJavascriptModule.class); it.hasNext(); ) {
|
||||
HalJsModule[] jsModules = it.next().getJavascriptModules();
|
||||
|
||||
if (jsModules != null) {
|
||||
for (HalJsModule module : jsModules)
|
||||
for (HalJsModule module : jsModules) {
|
||||
HalWebPage.addJavascriptModule(module);
|
||||
|
||||
if (module instanceof HalJavascriptModule.HalJsModulePage)
|
||||
registerPage(((HalJavascriptModule.HalJsModulePage) module).getPage(), new EmptyWebPage());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Iterator<HalWebPage> it = pluginManager.getSingletonIterator(HalApiEndpoint.class); it.hasNext(); )
|
||||
registerPage(it.next());
|
||||
for (Iterator<HalWebPage> it = pluginManager.getSingletonIterator(HalWebPage.class); it.hasNext(); )
|
||||
registerPage(it.next());
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
|
|
@ -202,6 +207,10 @@ public class HalServer {
|
|||
}
|
||||
}
|
||||
|
||||
public static List<HalDaemon> getAllDaemons() {
|
||||
return daemons;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Registers the given page with the intranet Hal web server.
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
|
||||
public abstract class HalWebPage implements HttpPage{
|
||||
public abstract class HalWebPage implements HttpPage {
|
||||
private static final String TEMPLATE_MAIN = HalContext.RESOURCE_WEB_ROOT + "/main_index.tmpl";
|
||||
private static final String TEMPLATE_NAVIGATION = HalContext.RESOURCE_WEB_ROOT + "/main_nav.tmpl";
|
||||
private static final String TEMPLATE_SIDE_NAVIGATION = HalContext.RESOURCE_WEB_ROOT + "/main_nav_side.tmpl";
|
||||
|
|
@ -83,7 +83,12 @@ public abstract class HalWebPage implements HttpPage{
|
|||
main.set("side_navigation", subNavigationTemplate);
|
||||
main.set("javascriptModules", jsModules);
|
||||
main.set("javascriptPages", jsPages);
|
||||
main.set("content", httpRespond(session, cookie, request));
|
||||
|
||||
Templator body = httpRespond(session, cookie, request);
|
||||
if (body == null)
|
||||
main.set("content", "");
|
||||
else
|
||||
main.set("content", body);
|
||||
|
||||
out.print(main.compile());
|
||||
} catch (Exception e) {
|
||||
|
|
|
|||
44
hal-core/src/se/hal/page/EmptyWebPage.java
Normal file
44
hal-core/src/se/hal/page/EmptyWebPage.java
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package se.hal.page;
|
||||
|
||||
import se.hal.EventControllerManager;
|
||||
import se.hal.HalContext;
|
||||
import se.hal.intf.HalEventData;
|
||||
import se.hal.intf.HalWebPage;
|
||||
import se.hal.struct.Event;
|
||||
import se.hal.struct.devicedata.ColorEventData;
|
||||
import se.hal.struct.devicedata.LevelEventData;
|
||||
import se.hal.struct.devicedata.OnOffEventData;
|
||||
import se.hal.util.DeviceNameComparator;
|
||||
import se.hal.util.HistoryDataListSqlResult;
|
||||
import se.hal.util.HistoryDataListSqlResult.HistoryData;
|
||||
import zutil.ObjectUtil;
|
||||
import zutil.db.DBConnection;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.parser.Templator;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A empty page with an empty body content generated from server side.
|
||||
*/
|
||||
public class EmptyWebPage extends HalWebPage {
|
||||
|
||||
public EmptyWebPage() {
|
||||
super("empty");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Templator httpRespond(
|
||||
Map<String, Object> session,
|
||||
Map<String, String> cookie,
|
||||
Map<String, String> request)
|
||||
throws Exception{
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
package se.hal.page;
|
||||
|
||||
import se.hal.intf.HalJavascriptModule;
|
||||
import se.hal.intf.HalWebPage;
|
||||
|
||||
public class JavascriptModules implements HalJavascriptModule {
|
||||
|
||||
@Override
|
||||
public HalJsModule[] getJavascriptModules() {
|
||||
HalWebPage.getRootNav().createSubNav("Events").createSubNav("/event_overview", "Overview");
|
||||
|
||||
return new HalJsModule[] {
|
||||
new HalJsModule("AlertStore", "./js/vue/stores/AlertStore.js"),
|
||||
new HalJsModule("EventStore", "./js/vue/stores/EventStore.js"),
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@
|
|||
{"se.hal.intf.HalApiEndpoint": "se.hal.page.api.RoomApiEndpoint"},
|
||||
{"se.hal.intf.HalApiEndpoint": "se.hal.page.api.SensorApiEndpoint"},
|
||||
|
||||
{"se.hal.intf.HalWebPage": "se.hal.page.EventOverviewWebPage"},
|
||||
{"se.hal.intf.HalJavascriptModule": "se.hal.page.JavascriptModules"},
|
||||
{"DISABLEDse.hal.intf.HalWebPage": "se.hal.page.EventOverviewWebPage"},
|
||||
{"se.hal.intf.HalWebPage": "se.hal.page.EventConfigWebPage"},
|
||||
{"se.hal.intf.HalWebPage": "se.hal.page.PropertyConfigWebPage"},
|
||||
{"se.hal.intf.HalWebPage": "se.hal.page.PluginConfigWebPage"},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue