实现系统总览、统计分析的图标数据接口

This commit is contained in:
lixiaoyuan
2025-07-31 17:56:08 +08:00
parent 0958fcc224
commit 697193a7aa
19 changed files with 984 additions and 640 deletions

View File

@@ -453,3 +453,8 @@ tr.shown td.details-control {
.btn-default:hover { .btn-default:hover {
background-color: #858585; background-color: #858585;
} }
/* 隐藏所有输入框的清除按钮 */
input[type="date"]::-webkit-clear-button {
display: none;
}

View File

@@ -1,238 +1,261 @@
var G = { var G = {
user: { username: '' }, user: { username: '' },
loadPage: function (pagename) { loadPage: function (pagename) {
while (1) { while (1) {
var script = document.querySelector("script[id='mytempfile']") var script = document.querySelector("script[id='mytempfile']")
if (!script) break if (!script) break
script.remove() script.remove()
}
var file = 'assets/html/pages/' + pagename + '.html'
G.cppNative.readFile(file).then(function (text) {
// 加载 html 页面内容文件
document.getElementById('mypage').innerHTML = text
// 加载 js 脚本文件
var jsUrl = './pages/' + pagename + '.js'
var element = document.createElement('script')
element.src = jsUrl + '?t=' + new Date().getTime()
element.async = false // 脚本按照加载先后顺序处理
element.setAttribute('id', 'mytempfile')
document.body.appendChild(element)
})
},
switchSetStatus: function (id1, id2, checked, callback) {
document.getElementById(id2).checked = !(document.getElementById(id1).checked = checked)
if (callback) callback(checked)
},
switchGetStatus: function (id) { return document.getElementById(id).checked },
initTable: function (id, info) {
if (info.table) {
info.table.ajax.reload()
return;
}
var tableOption = G.tableGetOption()
tableOption.columns = info.columns
tableOption.ajax = function (req, callback, settings) {
G.cppNative.log('lxy=== table ajax start')
if (info.query) {
info.query(1, 10).then(res => {
var result = []
res.data.forEach(function (item, index) {
var rowData = []
info.header.forEach(function (key, index) { rowData.push(item[key]) })
result.push(rowData)
})
G.cppNative.log("table ajax: " + JSON.stringify(result))
callback({ draw: req.draw, recordsTotal: result.length, recordsFiltered: result.length, data: result })
})
} else {
callback({ draw: req.draw, recordsTotal: 0, recordsFiltered: 0, data: [] })
}
}
// 初始化表格
info.table = $('#' + id + 'Table').DataTable(tableOption)
G.cppNative.log('lxy=== table init end')
//$('div.gotopage').html('<b style="color:#7f8fa4">跳转至 </b><input id="searchNumber" style="width:80px"/><b style="color:#7f8fa4;"> 页</b>')
},
updateTableData: function (queryFunc, params) {
if (queryFunc) {
}
},
cppCall: function (id) {
var cppfunc = G.cppNative[id]
if (cppfunc) {
G.cppNative.log('call cpp function [' + id + ']')
cppfunc()
} else {
G.cppNative.log('call cpp function [' + id + '] error: not exist')
}
},
cppSignal: function (id, callback) {
var signal = G.cppNative[id]
if (signal) {
G.cppNative.log('cpp signal [' + id + '] ')
signal.connect(callback)
} else {
G.cppNative.log('cpp signal [' + id + '] error: not exist')
}
},
initForm: function (id, funcPopConfirm) {
var form = document.getElementById(id + 'Form')
var formBtnOk = document.getElementById(id + 'FormOk')
if (form && formBtnOk) {
// 监听表单的输入事件,检查表单的必填项是否完成输入,如果未完成,则确认按钮不可用
form.addEventListener('input', function () { formBtnOk.disabled = !form.checkValidity() })
// 编辑弹窗的确定按钮监听点击事件
formBtnOk.addEventListener('click', function () { if (funcPopConfirm) { funcPopConfirm(id) } })
}
},
showElement: function (id, visible) {
var elemt = document.getElementById(id)
if (elemt) { elemt.style.display = visible ? 'block' : 'none' }
},
popSetParams: function (id, keys, rowdata, callback) {
keys.forEach(function (key, index) {
var val = ''
if (rowdata && rowdata.length > index) { val = rowdata[index] }
if (key == 'is_open') {
G.switchSetStatus(id + 'Form_on', id + 'Form_off', parseInt(val))
} else {
var myval = (callback) ? callback(id, key, val) : val
// 回调函数返回 undefined 时, 不需要设置参数值
if (myval != undefined) {
val = myval
var element = $('#' + id + 'Form_' + key)
if (element) {
if (element.prop("tagName") == "SELECT" && val == '') {
element.get(0).selectedIndex = 0;
} else {
element.val(val)
}
}
}
}
})
},
popGetParams: function (id, keys, rowdata, callback) {
var params = {}
keys.forEach(function (key, index) {
var val
if (key == 'is_open') {
val = G.switchGetStatus(id + 'Form_on') ? '1' : '0'
} else {
var myval = callback ? callback(id, key) : undefined
val = myval != undefined ? myval : $('#' + id + 'Form_' + key).val()
}
if (rowdata) {
if (rowdata.length > index && val != rowdata[index]) { params[key] = val }
} else {
if (val != '') { params[key] = val }
}
})
G.cppNative.log('POP get params id=' + id + ', params=' + JSON.stringify(params))
return params
},
popSetParamReadonly: function (id, key, isReadonly) {
$('#' + id + 'Form_' + key).attr('readonly', isReadonly)
},
tableGetOption: function () {
return {
data: [],
columns: [],
bSort: false,
scollY: "600px",
//aLengthMenu: [10, 20, 50, 100], //设置每页显示数据条数的下拉选项
//displayLength: 5, //每页初始显示最大记录数量
// 设置表格t、分页数据条数l、搜索框f、表格信息i、分页p、加载信息r
dom: 'rt<"mydt-ext"p<"item"<"gotopage">><"item"i><"item"f>>',
language: {
emptyTable: '无数据',
lengthMenu: '每页显示 _MENU_ 条记录',
zeroRecords: '对不起,查询不到任何相关数据',
info: '共有 _TOTAL_ 条记录', // '当前显示 _START_ 到 _END_ 条,共 _TOTAL_ 条记录',
infoEmpty: '共 0 页',
infoFiltered: '数据表中共为 _MAX_ 条记录)',
processing: '正在加载中...',
search: '结果中搜索',
paginate: {
previous: '上一页', next: '下一页', first: '首页', last: '尾页'
},
},
columnDefs: [{ targets: '_all', className: 'dt-head-left' }, { targets: '_all', className: 'dt-body-left' }],
//serverSide: true,
processing: false,
ajax: function (req, callback, settings) {
// 查询服务器获取数据, 同时获取总数据条数
callback({ draw: req.draw, recordsTotal: 0, recordsFiltered: 0, data: [] })
},
createdRow: function (row, data, dataIndex) {
if (dataIndex % 2 === 0) {
//$(row).css('background-color', '#ff000010')
} else {
//$(row).css('background-color', '#00ff0010')
}
$(row).on('mouseenter', function () {
//if (curEditRow && myTableApi.row(curEditRow).index() == myTableApi.row($(this)).index()) return
$(this).css('background-color', '#ff808050') // 高亮颜色
})
$(row).on('mouseleave', function () {
//if (curEditRow && myTableApi.row(curEditRow).index() == myTableApi.row($(this)).index()) return
$(this).css('background-color', '') // 恢复默认
})
},
}
},
clickSubpageBtn: function (id) {
if (G.curSubpageId == id) return
if (G.curSubpageId) {
var oldBtn = document.getElementById(G.curSubpageId + 'Btn')
if (oldBtn) { oldBtn.className = 'btn btn-primary' }
var oldSubpage = $('#' + G.curSubpageId)
if (oldSubpage) { oldSubpage.hide() }
}
if (id) {
var curBtn = document.getElementById(id + 'Btn')
if (curBtn) { curBtn.className = 'btn btn-success btn-lg' }
var curSubpage = $('#' + id)
if (curSubpage) { curSubpage.show() }
}
G.curSubpageId = id
},
getRandDataDay: function (a, b, n) {
var data = []
var t0 = Date.parse('2025-03-01 00:00:00')
if (!n) { n = 24 }
var step = 86400 / n
var y = a;
for (var i = 0; i < n; ++i) {
// data[i] = { x: t0 + i * step * 1000, y: Math.sin(i * 0.1) * (n - m) }
y = Math.floor((y + RAND(0, 50) - 25) * 100) / 100
data[i] = { x: t0 + i * step * 1000, y: y }
}
return data
} }
} var file = 'assets/html/pages/' + pagename + '.html'
G.cppNative.readFile(file).then(function (text) {
// 加载 html 页面内容文件
document.getElementById('mypage').innerHTML = text
// 加载 js 脚本文件
var jsUrl = './pages/' + pagename + '.js'
var element = document.createElement('script')
element.src = jsUrl + '?t=' + new Date().getTime()
element.async = false // 脚本按照加载先后顺序处理
element.setAttribute('id', 'mytempfile')
document.body.appendChild(element)
})
},
switchSetStatus: function (id1, id2, checked, callback) {
document.getElementById(id2).checked = !(document.getElementById(id1).checked = checked)
if (callback) callback(checked)
},
switchGetStatus: function (id) { return document.getElementById(id).checked },
initTable: function (id, info) {
if (info.table) {
info.table.ajax.reload()
return;
}
var tableOption = G.tableGetOption()
tableOption.columns = info.columns
tableOption.ajax = function (req, callback, settings) {
G.cppNative.log('lxy=== table ajax start')
if (info.query) {
info.query(1, 10).then(res => {
var result = []
res.data.forEach(function (item, index) {
var rowData = []
info.header.forEach(function (key, index) { rowData.push(item[key]) })
result.push(rowData)
})
G.cppNative.log("table ajax: " + JSON.stringify(result))
callback({ draw: req.draw, recordsTotal: result.length, recordsFiltered: result.length, data: result })
})
} else {
callback({ draw: req.draw, recordsTotal: 0, recordsFiltered: 0, data: [] })
}
}
// 初始化表格
info.table = $('#' + id + 'Table').DataTable(tableOption)
G.cppNative.log('lxy=== table init end')
//$('div.gotopage').html('<b style="color:#7f8fa4">跳转至 </b><input id="searchNumber" style="width:80px"/><b style="color:#7f8fa4;"> 页</b>')
},
updateTableData: function (queryFunc, params) {
if (queryFunc) {
}
},
cppCall: function (id) {
var cppfunc = G.cppNative[id]
if (cppfunc) {
G.cppNative.log('call cpp function [' + id + ']')
cppfunc()
} else {
G.cppNative.log('call cpp function [' + id + '] error: not exist')
}
},
cppSignal: function (id, callback) {
var signal = G.cppNative[id]
if (signal) {
G.cppNative.log('cpp signal [' + id + '] ')
signal.connect(callback)
} else {
G.cppNative.log('cpp signal [' + id + '] error: not exist')
}
},
initForm: function (id, funcPopConfirm) {
var form = document.getElementById(id + 'Form')
var formBtnOk = document.getElementById(id + 'FormOk')
if (form && formBtnOk) {
// 监听表单的输入事件,检查表单的必填项是否完成输入,如果未完成,则确认按钮不可用
form.addEventListener('input', function () { formBtnOk.disabled = !form.checkValidity() })
// 编辑弹窗的确定按钮监听点击事件
formBtnOk.addEventListener('click', function () { if (funcPopConfirm) { funcPopConfirm(id) } })
}
},
showElement: function (id, visible) {
var elemt = document.getElementById(id)
if (elemt) { elemt.style.display = visible ? 'block' : 'none' }
},
popSetParams: function (id, keys, rowdata, callback) {
keys.forEach(function (key, index) {
var val = ''
if (rowdata && rowdata.length > index) { val = rowdata[index] }
if (key == 'is_open') {
G.switchSetStatus(id + 'Form_on', id + 'Form_off', parseInt(val))
} else {
var myval = (callback) ? callback(id, key, val) : val
// 回调函数返回 undefined 时, 不需要设置参数值
if (myval != undefined) {
val = myval
var element = $('#' + id + 'Form_' + key)
if (element) {
if (element.prop("tagName") == "SELECT" && val == '') {
element.get(0).selectedIndex = 0;
} else {
element.val(val)
}
}
}
}
})
},
popGetParams: function (id, keys, rowdata, callback) {
var params = {}
keys.forEach(function (key, index) {
var val
if (key == 'is_open') {
val = G.switchGetStatus(id + 'Form_on') ? '1' : '0'
} else {
var myval = callback ? callback(id, key) : undefined
val = myval != undefined ? myval : $('#' + id + 'Form_' + key).val()
}
if (rowdata) {
if (rowdata.length > index && val != rowdata[index]) { params[key] = val }
} else {
if (val != '') { params[key] = val }
}
})
G.cppNative.log('POP get params id=' + id + ', params=' + JSON.stringify(params))
return params
},
popSetParamReadonly: function (id, key, isReadonly) {
$('#' + id + 'Form_' + key).attr('readonly', isReadonly)
},
tableGetOption: function () {
return {
data: [],
columns: [],
bSort: false,
scollY: "600px",
//aLengthMenu: [10, 20, 50, 100], //设置每页显示数据条数的下拉选项
//displayLength: 5, //每页初始显示最大记录数量
// 设置表格t、分页数据条数l、搜索框f、表格信息i、分页p、加载信息r
dom: 'rt<"mydt-ext"p<"item"<"gotopage">><"item"i><"item"f>>',
language: {
emptyTable: '无数据',
lengthMenu: '每页显示 _MENU_ 条记录',
zeroRecords: '对不起,查询不到任何相关数据',
info: '共有 _TOTAL_ 条记录', // '当前显示 _START_ 到 _END_ 条,共 _TOTAL_ 条记录',
infoEmpty: '共 0 页',
infoFiltered: '数据表中共为 _MAX_ 条记录)',
processing: '正在加载中...',
search: '结果中搜索',
paginate: {
previous: '上一页', next: '下一页', first: '首页', last: '尾页'
},
},
columnDefs: [{ targets: '_all', className: 'dt-head-left' }, { targets: '_all', className: 'dt-body-left' }],
//serverSide: true,
processing: false,
ajax: function (req, callback, settings) {
// 查询服务器获取数据, 同时获取总数据条数
callback({ draw: req.draw, recordsTotal: 0, recordsFiltered: 0, data: [] })
},
createdRow: function (row, data, dataIndex) {
if (dataIndex % 2 === 0) {
//$(row).css('background-color', '#ff000010')
} else {
//$(row).css('background-color', '#00ff0010')
}
$(row).on('mouseenter', function () {
//if (curEditRow && myTableApi.row(curEditRow).index() == myTableApi.row($(this)).index()) return
$(this).css('background-color', '#ff808050') // 高亮颜色
})
$(row).on('mouseleave', function () {
//if (curEditRow && myTableApi.row(curEditRow).index() == myTableApi.row($(this)).index()) return
$(this).css('background-color', '') // 恢复默认
})
},
}
},
clickSubpageBtn: function (id) {
if (G.curSubpageId == id) return
if (G.curSubpageId) {
var oldBtn = document.getElementById(G.curSubpageId + 'Btn')
if (oldBtn) { oldBtn.className = 'btn btn-primary' }
var oldSubpage = $('#' + G.curSubpageId)
if (oldSubpage) { oldSubpage.hide() }
}
if (id) {
var curBtn = document.getElementById(id + 'Btn')
if (curBtn) { curBtn.className = 'btn btn-success btn-lg' }
var curSubpage = $('#' + id)
if (curSubpage) { curSubpage.show() }
}
G.curSubpageId = id
},
getRandDataDay: function (a, b, n) {
var data = []
var t0 = Date.parse('2025-07-01 00:00:00')
if (!n) { n = 24 }
var step = 86400 / n
var y = a;
for (var i = 0; i < n; ++i) {
// data[i] = { x: t0 + i * step * 1000, y: Math.sin(i * 0.1) * (n - m) }
var x = t0 + i * step * 1000
y = Math.floor((y + RAND(0, 50) - 25) * 100) / 100
data[i] = { name: x, value: [x, y] }
}
return data
},
// 格式化日期为YYYY-MM-DD
formatDateMD: function (date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
//return `${year}-${month}-${day}`;
return `${month}-${day}`;
},
alertMessage: function (type, message) {
const wrapper = document.createElement('div')
wrapper.innerHTML = [
`<div class="alert alert-${type} alert-dismissible" role="alert">`,
` <div>${message}</div>`,
' <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>',
'</div>'
].join('')
const elemtParent = document.getElementById('alertBox')
if (elemtParent) { elemtParent.append(wrapper); }
}
}
function RAND(a, b) { function RAND(a, b) {
return a + Math.random() * (b - a) return a + Math.random() * (b - a)
} }

View File

@@ -1,80 +1,96 @@
var optionBar = { var optionLegend = {
animation: false, show: true, //是否显示
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, orient: 'horizontal',
grid: { left: '2%', right: '2%', bottom: '2%', containLabel: true }, x: 'center', //可设定图例在左、右、居中
legend: { y: 'top', //可设定图例在上、下、居中
show: true, //是否显示 padding: [20, 10, 10, 50], //可设定图例[距上方距离,距右方距离,距下方距离,距左方距离]
orient: 'horizontal', // data: ['日发电电量', '日入网电量'],
x: 'center', //可设定图例在左、右、居中 textStyle: {
y: 'top', //可设定图例在上、下、居中 //图例的公用文本样式。
padding: [20, 10, 10, 50], //可设定图例[距上方距离,距右方距离,距下方距离,距左方距离] color: '#ffffff', // 文字的颜色。
// data: ['日发电电量', '日入网电量'], // fontStyle: "normal", // 文字字体的风格。'italic'
textStyle: { // fontWeight: "normal", // 文字字体的粗细。 'normal' 'bold' 'bolder' 'lighter' 100 | 200 | 300 | 400...
//图例的公用文本样式 // fontFamily: "sans-serif", // 文字的字体系列
color: '#ffffff', // 文字的颜色 // fontSize: 12, // 文字的字体大小
// fontStyle: "normal", // 文字字体的风格。'italic' // lineHeight: 20, // 行高。
// fontWeight: "normal", // 文字字体的粗细。 'normal' 'bold' 'bolder' 'lighter' 100 | 200 | 300 | 400... // backgroundColor: "transparent", // 文字块背景色。
// fontFamily: "sans-serif", // 文字的字体系列 // borderColor: "transparent", // 文字块边框颜色
// fontSize: 12, // 文字的字体大小 // borderWidth: 0, // 文字块边框宽度
// lineHeight: 20, // 行高 // borderRadius: 0, // 文字块的圆角
// backgroundColor: "transparent", // 文字块背景色。 // padding: 0, // 文字块的内边距
// borderColor: "transparent", // 文字块边框颜色 // shadowColor: "transparent", // 文字块的背景阴影颜色
// borderWidth: 0, // 文字块边框宽度。 // shadowBlur: 0, // 文字块的背景阴影长度。
// borderRadius: 0, // 文字块的圆角 // shadowOffsetX: 0, // 文字块的背景阴影 X 偏移
// padding: 0, // 文字块的内边距 // shadowOffsetY: 0, // 文字块的背景阴影 Y 偏移。
// shadowColor: "transparent", // 文字块的背景阴影颜色 // width: 50, // 文字块的宽度。 默认
// shadowBlur: 0, // 文字块的背景阴影长度。 // height: 40, // 文字块的高度 默认
// shadowOffsetX: 0, // 文字块的背景阴影 X 偏移 // textBorderColor: "transparent", // 文字本身的描边颜色
// shadowOffsetY: 0, // 文字块的背景阴影 Y 偏移 // textBorderWidth: 0, // 文字本身的描边宽度
// width: 50, // 文字块的宽度。 默认 // textShadowColor: "transparent", // 文字本身的阴影颜色。
// height: 40, // 文字块的高度 默认 // textShadowBlur: 0, // 文字本身的阴影长度。
// textBorderColor: "transparent", // 文字本身的描边颜色 // textShadowOffsetX: 0, // 文字本身的阴影 X 偏移
// textBorderWidth: 0, // 文字本身的描边宽度 // textShadowOffsetY: 0, // 文字本身的阴影 Y 偏移
// textShadowColor: "transparent", // 文字本身的阴影颜色。
// textShadowBlur: 0, // 文字本身的阴影长度。
// textShadowOffsetX: 0, // 文字本身的阴影 X 偏移。
// textShadowOffsetY: 0, // 文字本身的阴影 Y 偏移。
},
}, },
xAxis: {
type: 'category',
//data: ['2025/3/1', '2025/3/2', '2025/3/3', '2025/3/4', '2025/3/5', '2025/3/6', '2025/3/7'],
data: ['1', '2', '3', '4', '5', '6', '7'],
axisTick: { alignWithLabel: true },
axisLabel: { color: 'white', fontSize: 12, margin: 8, interval: 'auto' },
},
yAxis: [
{
type: 'value',
axisTick: { show: true },
axisLine: { show: true },
axisLabel: { color: 'white', fontSize: 12, margin: 8, interval: 'auto' },
//网格线
splitLine: { show: true, interval: 2, lineStyle: { type: 'dashed', color: 'gray' } },
},
],
series: [
// { name: '日发电电量', type: 'bar', data: [30, 28, 35, 18, 36, 27, 19], color: '#2a82e4' },
],
} }
function echartGetOptionBar() {
return JSON.parse(JSON.stringify(optionBar)) var axisLabel = { color: 'white', fontSize: 12, margin: 8, interval: 'auto' };
var splitLine = { show: true, interval: 2, lineStyle: { type: 'dashed', color: 'gray' } }; //网格线
function echartGetOptionBar(yAxisLeft, yAxisRight) {
var option = {
animation: false,
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
grid: { left: '2%', right: '2%', bottom: '2%', containLabel: true },
legend: optionLegend,
xAxis: { type: 'category', axisTick: { alignWithLabel: true }, axisLabel: axisLabel, data: [] },
yAxis: [
{
type: 'value',
show: true,
name: yAxisLeft,
nameTextStyle: { color: 'white', fontSize: 15, width: 300 },
axisTick: { show: true },
axisLine: { show: true },
axisLabel: axisLabel,
splitLine: splitLine,
//boundaryGap: [0, '100%'],
},
{
position: 'right',
type: 'value',
show: true,
name: yAxisRight,
nameTextStyle: { color: 'white', fontSize: 15, width: '160px' },
axisTick: { show: true },
axisLine: { show: true },
axisLabel: axisLabel,
splitLine: splitLine,
//boundaryGap: [0, '100%'],
}
],
series: [
// { name: '日发电电量', type: 'bar', data: [30, 28, 35, 18, 36, 27, 19], color: '#2a82e4' },
],
}
//if (yAxisLeft) { option.yAxis[0].name = yAxisLeft; }
//if (yAxisRight) { option.yAxis[1].name = yAxisRight; }
// yaxis.forEach(item => { })
//return JSON.parse(JSON.stringify(optionBar))
return option;
} }
var optionAxisX_category = { var optionAxisX_category = {
type: 'category', type: 'category',
//data: ['2025/3/1', '2025/3/2', '2025/3/3', '2025/3/4', '2025/3/5', '2025/3/6', '2025/3/7'],
data: ['1'],
axisTick: { alignWithLabel: true }, axisTick: { alignWithLabel: true },
axisLabel: { color: 'white', fontSize: 12, margin: 8, interval: 'auto' }, axisLabel: { color: 'white', fontSize: 12, margin: 8, interval: 'auto' },
data: [],
} }
var optionAxisX_time = { var optionAxisX_time = {
type: 'time', type: 'time',
splitLine: { show: false },
splitNumber: 8, splitNumber: 8,
axisTick: { alignWithLabel: true }, axisTick: { alignWithLabel: true },
axisLine: { lineStyle: { color: 'white' } }, axisLine: { lineStyle: { color: 'white' } },
@@ -84,88 +100,67 @@ var optionAxisX_time = {
var date = new Date(value) var date = new Date(value)
var hour = date.getHours() var hour = date.getHours()
var minutes = date.getMinutes() var minutes = date.getMinutes()
if (hour < 10) { if (hour < 10) { hour = '0' + hour }
hour = '0' + hour if (minutes < 10) { minutes = '0' + minutes }
}
if (minutes < 10) {
minutes = '0' + minutes
}
return hour + ':' + minutes return hour + ':' + minutes
}, },
}, },
} }
var optionCurve = {
animation: false,
title: [], //
grid: { left: '2%', right: '2%', bottom: '2%', containLabel: true },
legend: { show: true, orient: 'horizontal', x: 'center', y: 'top', padding: [20, 10, 10, 50], textStyle: { color: '#ffffff' }, data: [], },
tooltip: {
trigger: 'axis',
// formatter: function (params) {
// params = params[0]
// var date = new Date(params.name)
// var hour = date.getHours()
// var minutes = date.getMinutes()
// if (hour < 10) { hour = '0' + hour }
// if (minutes < 10) { minutes = '0' + minutes }
// var dateStr = hour + ':' + minutes
// return dateStr + ' ' + params.value[1]
// },
axisPointer: { animation: false },
},
xAxis: {
type: 'time',
splitLine: { show: false },
splitNumber: 8,
axisTick: { alignWithLabel: true },
axisLine: { lineStyle: { color: 'white' } },
//interval: 3600, // 设置x轴时间间隔
axisLabel: {
color: 'white',
// formatter: function (value, index) {
// var date = new Date(value)
// var hour = date.getHours()
// var minutes = date.getMinutes()
// if (hour < 10) { hour = '0' + hour }
// if (minutes < 10) { minutes = '0' + minutes }
// //return hour + ':' + minutes
// return "value"
// },
// 格式化x轴显示
formatter: function (value, index) {
var date = new Date(value)
var hour = date.getHours()
var minutes = date.getMinutes()
if (hour < 10) { hour = '0' + hour }
if (minutes < 10) { minutes = '0' + minutes }
return hour + ':' + minutes
// alert(new Date().toLocaleDateString())
// // 如果时间是 23:59:59 , 格式化为 24:00
// if (value === new Date(moment().endOf('day').format('YYYY-MM-DD HH:mm:ss')).getTime()) {
// return moment(value).format("24:00");
// } else {
// // 其他的时间返回格式化 00:00
// return moment(value).format("HH:mm");
// }
}
},
},
yAxis: {
type: 'value',
axisTick: { show: true },
axisLine: { show: true, lineStyle: { color: 'white' } },
axisLabel: { color: 'white' },
splitLine: { show: true, lineStyle: { type: 'dashed', color: 'gray' } },
nameTextStyle: { color: 'white', fontSize: 15 },
boundaryGap: [0, '100%'],
},
// series: [{ name: '数据', type: 'line', hoverAnimation: false, smooth: false, symbolSize: 4, data: [] }],
}
function echartGetOptionCurve() { function echartGetOptionCurve(yAxisLeft, yAxisRight) {
optionCurve.legend.data = [] var optionCurve = {
optionCurve.series = [] animation: false,
title: [], //
grid: { left: '2%', right: '2%', bottom: '2%', containLabel: true },
legend: optionLegend,
tooltip: { trigger: 'axis', axisPointer: { animation: false } },
xAxis: {
type: 'time',
axisTick: { alignWithLabel: true },
//axisLine: { lineStyle: { color: 'white' } },
//interval: 3600, // 设置x轴时间间隔
axisLabel: {
color: 'white',
fontSize: 12,
// 格式化x轴显示
formatter: function (value, index) {
var date = new Date(value)
var hour = date.getHours()
var minutes = date.getMinutes()
if (hour < 10) { hour = '0' + hour }
if (minutes < 10) { minutes = '0' + minutes }
return hour + ':' + minutes
}
},
},
yAxis: [
{
type: 'value',
show: true,
name: yAxisLeft,
nameTextStyle: { color: 'white', fontSize: 15 },
axisTick: { show: true },
axisLine: { show: true },
axisLabel: axisLabel,
splitLine: splitLine,
//boundaryGap: [0, '100%'],
},
{
position: 'right',
type: 'value',
show: true,
name: yAxisRight,
nameTextStyle: { color: 'white', fontSize: 15 },
axisTick: { show: true },
axisLine: { show: true },
axisLabel: axisLabel,
splitLine: splitLine,
//boundaryGap: [0, '100%'],
}
],
// series: [{ name: '数据', type: 'line', hoverAnimation: false, smooth: false, symbolSize: 4, data: [] }],
}
return optionCurve return optionCurve
} }
@@ -177,11 +172,17 @@ function echartUpdateData(chart, index, data) {
} }
} }
function initEchartBar(id, seriesItems) { function initEchartBar(id, seriesItems, xdata, yAxisLeft, yAxisRight) {
var echart = echarts.init(document.getElementById(id)) var echart = echarts.init(document.getElementById(id))
var option = echartGetOptionBar() var option = echartGetOptionBar(yAxisLeft, yAxisRight)
option.legend.data = seriesItems option.legend.data = seriesItems
option.series = [] option.series = []
option.xAxis = {
type: 'category',
axisTick: { alignWithLabel: true },
axisLabel: { color: 'white', fontSize: 12, margin: 8, interval: 'auto' },
data: xdata
}
seriesItems.forEach(item => { seriesItems.forEach(item => {
option.series.push({ name: item, type: 'bar', data: [] }) option.series.push({ name: item, type: 'bar', data: [] })
}); });
@@ -189,18 +190,18 @@ function initEchartBar(id, seriesItems) {
return echart return echart
} }
function updateEchartBar(chart, index, xdata, ydata) { function updateEchartBar(chart, xdata, series) {
var option = chart.getOption() var option = chart.getOption()
option.xAxis.data = xdata option.xAxis = { data: xdata }
if (index < option.series.length) { //var series = [];
option.series[index].data = ydata //ydataArray.forEach((d => { series.push({ data: d }) }))
chart.setOption(option) option.series = series
} chart.setOption(option)
} }
function initEchartCurve(id, seriesItems) { function initEchartCurve(id, seriesItems, yAxisLeft, yAxisRight) {
var echart = echarts.init(document.getElementById(id)) var echart = echarts.init(document.getElementById(id))
var option = echartGetOptionCurve() var option = echartGetOptionCurve(yAxisLeft, yAxisRight)
option.legend.data = seriesItems option.legend.data = seriesItems
option.series = [] option.series = []
seriesItems.forEach(item => { seriesItems.forEach(item => {
@@ -213,9 +214,10 @@ function initEchartCurve(id, seriesItems) {
function updateEchartCurve(chart, index, data) { function updateEchartCurve(chart, index, data) {
var option = chart.getOption() var option = chart.getOption()
if (index < option.series.length) { if (index < option.series.length) {
for (var i = 0; i < data.length; ++i) { option.series[index].data = data
option.series[index].data[i] = { name: data[i].x, value: [data[i].x, data[i].y] } // for (var i = 0; i < data.length; ++i) {
} // option.series[index].data[i] = { name: data[i].x, value: [data[i].x, data[i].y] }
// }
chart.setOption(option) chart.setOption(option)
} }
} }

View File

@@ -82,35 +82,45 @@
</head> </head>
<body> <body>
<div <div style="width:100%; height:100%;">
style="width:100%; height: calc(8vh); display: flex; justify-content: space-between; padding-top: 1.4%; font-family: 优设标题黑;"> <div
<div class="myrow" style="height: 30px; color:white; font-size: 20px; float: left"> style="width:100%; height: calc(8vh); display: flex; justify-content: space-between; padding-top: 1.4%; font-family: 优设标题黑;">
<div id="currentTime" style="width:250px; margin-left: 20px; "></div>
<div id="currentWeekday"></div> <div class="myrow" style="height: 30px; color:white; font-size: 20px; float: left">
</div> <button type="button" class="btn btn-danger btn-sm" id="liveAlertBtn" style="font-size: 20px;">显示警告框</button>
<div id="currentTime" style="width:250px; margin-left: 20px; "></div>
<div id="currentWeekday"></div>
<div style=" width:500px; height: 30px; display: flex;justify-content: flex-end;">
<div style=" width: 28px; height: 28px; background-color: #01b7d1; border-radius: 14px;"></div>
<div id='username' style="line-height:28px;color: white; font-size: 20px; font-weight: 500; margin-left: 10px; ">
</div> </div>
<button
style="width:30px; height: 30px; background-color: transparent; border: none; background-image: url('../ui/exit.png'); background-size: cover; background-repeat: no-repeat;margin-left: 10px; margin-right: 10px;"
onclick="loginOut()"></button>
</div>
</div>
<div id="mypage" style="height: calc(92vh - 60px); overflow: hidden;">
</div>
<div style="background-color: #00496e; display: flex; justify-content: center;">
<button id="homePageBtn" class="btn btn-success" style="margin: 10px"
onclick="onClickMenuBtn(this, '系统总览')">系统总览</button>
<button class="btn btn-primary" style="margin: 10px" onclick="onClickMenuBtn(this, '运行监控')">运行监控</button>
<button class="btn btn-primary" style="margin: 10px" onclick="onClickMenuBtn(this, '预测管理')">预测管理</button>
<button class="btn btn-primary" style="margin: 10px" onclick="onClickMenuBtn(this, '统计分析')">统计分析</button>
<button class="btn btn-primary" style="margin: 10px" onclick="onClickMenuBtn(this, '系统管理')">系统管理</button>
<button class="btn btn-primary" style="margin: 10px" onclick="onClickMenuBtn(this, '安全管理')">安全管理</button>
<button class="btn btn-primary" style="margin: 10px" onclick="onClickMenuBtn(this, 'test')">测试页面</button>
</div>
<div style=" width:500px; height: 30px; display: flex;justify-content: flex-end;">
<div style=" width: 28px; height: 28px; background-color: #01b7d1; border-radius: 14px;"></div>
<div id='username'
style="line-height:28px;color: white; font-size: 20px; font-weight: 500; margin-left: 10px; ">
</div>
<button
style="width:30px; height: 30px; background-color: transparent; border: none; background-image: url('../ui/exit.png'); background-size: cover; background-repeat: no-repeat;margin-left: 10px; margin-right: 10px;"
onclick="loginOut()"></button>
</div>
</div>
<div id="mypage" style="height: calc(92vh - 60px); overflow: hidden;">
</div>
<div style="background-color: #00496e; display: flex; justify-content: center;">
<button id="homePageBtn" class="btn btn-success" style="margin: 10px"
onclick="onClickMenuBtn(this, '系统总览')">系统总览</button>
<button class="btn btn-primary" style="margin: 10px" onclick="onClickMenuBtn(this, '运行监控')">运行监控</button>
<button class="btn btn-primary" style="margin: 10px" onclick="onClickMenuBtn(this, '预测管理')">预测管理</button>
<button class="btn btn-primary" style="margin: 10px" onclick="onClickMenuBtn(this, '统计分析')">统计分析</button>
<button class="btn btn-primary" style="margin: 10px" onclick="onClickMenuBtn(this, '系统管理')">系统管理</button>
<button class="btn btn-primary" style="margin: 10px" onclick="onClickMenuBtn(this, '安全管理')">安全管理</button>
<button class="btn btn-primary" style="margin: 10px" onclick="onClickMenuBtn(this, 'test')">测试页面</button>
</div>
<!-- 浮动在右下角的子元素 -->
<div id="alertBox"
style="position: absolute; right: 0; top: 120px; width: 600px; height: 0px; background: red; z-index: 10;">
</div>
</div>
<div id="loginPage" style="position: fixed; top:0px; left:0px; width: 100%; height: 100%; background-color: #07486f; background-image: url('../ui/bkgLogin.png'); background-size: 100% 100%; <div id="loginPage" style="position: fixed; top:0px; left:0px; width: 100%; height: 100%; background-color: #07486f; background-image: url('../ui/bkgLogin.png'); background-size: 100% 100%;
background-attachment: fixed;"> background-attachment: fixed;">
<form id="loginForm" class="was-validated" <form id="loginForm" class="was-validated"
@@ -229,6 +239,14 @@
$("#loginPage").hide() $("#loginPage").hide()
onClickMenuBtn(document.getElementById('homePageBtn'), '系统总览') onClickMenuBtn(document.getElementById('homePageBtn'), '系统总览')
}) })
const alertTrigger = document.getElementById('liveAlertBtn')
if (alertTrigger) {
alertTrigger.addEventListener('click', () => {
G.alertMessage('danger', '这是一个错误提示信息!')
})
}
</script> </script>
</html> </html>

View File

@@ -33,7 +33,7 @@
<option value="3">储能设备</option> <option value="3">储能设备</option>
<option value="4">充电设备</option> <option value="4">充电设备</option>
<option value="5">负荷设备</option> <option value="5">负荷设备</option>
<option value="0">其它</option>0 <option value="0">其它</option>
</select> </select>
</div> </div>
<div class="input-group mb-3"> <div class="input-group mb-3">

View File

@@ -38,27 +38,6 @@ $(document).ready(function () {
$('div.gotopage').html('<b style="color:#7f8fa4">跳转至 </b><input id="searchNumber" style="width:80px"/><b style="color:#7f8fa4;"> 页</b>') $('div.gotopage').html('<b style="color:#7f8fa4">跳转至 </b><input id="searchNumber" style="width:80px"/><b style="color:#7f8fa4;"> 页</b>')
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['5214875222', 'C018', 'XH150', '', '', '2025-03-03', 'TCP', '', '', '', '', ''])
// myTable.row.add(['1', '2', '3', '', '', '', '', '', '', '', '', '']) // myTable.row.add(['1', '2', '3', '', '', '', '', '', '', '', '', ''])
// myTable.row.add(['1', '2', '3', '', '', '', '', '', '', '', '', '']) // myTable.row.add(['1', '2', '3', '', '', '', '', '', '', '', '', ''])
// myTable.row.add(['1', '2', '3', '', '', '', '', '', '', '', '', '']) // myTable.row.add(['1', '2', '3', '', '', '', '', '', '', '', '', ''])

View File

@@ -1,67 +1,65 @@
optionBar.xAxis.data = ['03/01', '03/02', '03/03', '03/04', '03/05', '03/06', '03/07'] var optionBar = echartGetOptionBar()
optionBar.xAxis.data = []
optionBar.legend.data = ['发电电量', '入网电量'] optionBar.legend.data = ['发电电量', '入网电量']
optionBar.series = [ optionBar.series = [
{ name: '发电电量', type: 'bar', data: [50, 28, 35, 18, 36, 27, 19] }, { name: '发电电量', type: 'bar', data: [] },
{ name: '入网电量', type: 'bar', data: [10, 32, 20, 33, 39, 13, 22] }, { name: '入网电量', type: 'bar', data: [] },
] ]
optionBar.yAxis[0].name = '电量'
optionBar.yAxis[1].name = ''
var mychartSolar = echarts.init(document.getElementById('mychartSolar')) var mychartSolar = echarts.init(document.getElementById('mychartSolar'))
mychartSolar.setOption(optionBar) mychartSolar.setOption(optionBar)
optionBar.legend.data = ['充电电量', '放电电量'] optionBar.legend.data = ['充电电量', '放电电量']
optionBar.series = [ optionBar.series = [
{ name: '充电电量', type: 'bar', data: [50, 28, 35, 18, 36, 27, 19] }, { name: '充电电量', type: 'bar', data: [] },
{ name: '放电电量', type: 'bar', data: [10, 32, 20, 33, 39, 13, 22] }, { name: '放电电量', type: 'bar', data: [] },
] ]
optionBar.yAxis[0].name = '电量'
optionBar.yAxis[1].name = ''
var mychartStorage = echarts.init(document.getElementById('mychartStorage')) var mychartStorage = echarts.init(document.getElementById('mychartStorage'))
mychartStorage.setOption(optionBar) mychartStorage.setOption(optionBar)
optionBar.legend.data = ['用电电量', '最大功率'] optionBar.legend.data = ['用电电量', '最大功率']
optionBar.series = [ optionBar.series = [
{ name: '用电电量', type: 'bar', data: [50, 28, 35, 18, 36, 27, 19] }, { name: '用电电量', type: 'bar', data: [] },
{ name: '最大功率', type: 'bar', data: [10, 32, 20, 33, 39, 13, 22] }, { name: '最大功率', type: 'bar', data: [] },
] ]
optionBar.yAxis[0].name = '电量'
optionBar.yAxis[1].name = '功率'
var mychartLoad = echarts.init(document.getElementById('mychartLoad')) var mychartLoad = echarts.init(document.getElementById('mychartLoad'))
mychartLoad.setOption(optionBar) mychartLoad.setOption(optionBar)
optionBar.legend.data = ['充电电量', '充电次数', '充电收益'] optionBar.legend.data = ['充电电量', '充电次数', '充电收益']
optionBar.series = [ optionBar.series = [
{ name: '充电电量', type: 'bar', data: [50, 28, 35, 18, 36, 27, 19] }, { name: '充电电量', type: 'bar', data: [] },
{ name: '充电次数', type: 'bar', data: [10, 32, 20, 33, 39, 13, 22] }, { name: '充电次数', type: 'bar', data: [] },
{ name: '充电收益', type: 'bar', data: [10, 32, 20, 33, 39, 13, 22] }, { name: '充电收益', type: 'bar', data: [] },
] ]
optionBar.yAxis[0].name = '电量'
optionBar.yAxis[1].name = '收益'
var mychartCharge = echarts.init(document.getElementById('mychartCharge')) var mychartCharge = echarts.init(document.getElementById('mychartCharge'))
mychartCharge.setOption(optionBar) mychartCharge.setOption(optionBar)
optionBar.legend.data = ['光伏设备', '储能设备', '负荷设备'] optionBar.legend.data = ['光伏设备', '储能设备', '负荷设备']
optionBar.series = [ optionBar.series = [
{ name: '光伏设备', type: 'bar', data: [50, 28, 35, 18, 36, 27, 19] }, { name: '光伏设备', type: 'bar', data: [] },
{ name: '储能设备', type: 'bar', data: [10, 32, 20, 33, 39, 13, 22] }, { name: '储能设备', type: 'bar', data: [] },
{ name: '负荷设备', type: 'bar', data: [10, 32, 20, 33, 39, 13, 22] }, { name: '负荷设备', type: 'bar', data: [] },
] ]
optionBar.yAxis[0].name = '次数'
optionBar.yAxis[1].name = ''
var mychartAlert = echarts.init(document.getElementById('mychartAlert')) var mychartAlert = echarts.init(document.getElementById('mychartAlert'))
mychartAlert.setOption(optionBar) mychartAlert.setOption(optionBar)
var optionCurve = echartGetOptionCurve()
function getRandomCurveData(m, n) {
var data = []
var t0 = Date.parse('2025-03-01 00:00:00')
var step = 600
var N = 86400 / step
var y = 500
for (var i = 0; i <= N; ++i) {
var t = t0 + i * step * 1000
//var y = Math.sin(i * 0.1) * (n - m) + m
y = Math.floor((y + RAND(0, 50) - 25) * 100) / 100
data[i] = { name: t, value: [t, y] }
}
return data
}
optionCurve.legend.data = ['发电功率', '辐照度'] optionCurve.legend.data = ['发电功率', '辐照度']
optionCurve.series = [ optionCurve.series = [
{ name: '发电功率', type: 'line', hoverAnimation: false, smooth: false, symbolSize: 0, data: getRandomCurveData(100, 200) }, { name: '发电功率', type: 'line', hoverAnimation: false, smooth: false, symbolSize: 0, data: [] },
{ name: '辐照度', type: 'line', hoverAnimation: false, smooth: false, symbolSize: 0, data: getRandomCurveData(100, 200) }, { name: '辐照度', type: 'line', hoverAnimation: false, smooth: false, symbolSize: 0, data: [] },
] ]
optionCurve.yAxis[0].name = '功率kw'
optionCurve.yAxis[1].name = '光照Lux'
var mychartRunning = echarts.init(document.getElementById('mychartRunning')) var mychartRunning = echarts.init(document.getElementById('mychartRunning'))
mychartRunning.setOption(optionCurve) mychartRunning.setOption(optionCurve)
@@ -95,8 +93,8 @@ function updatePageData() {
var charge = { d1: [], d2: [], d3: [] } var charge = { d1: [], d2: [], d3: [] }
res.forEach(item => { res.forEach(item => {
solar.d1.push(item['elect_gen_solar']) solar.d1.push(item['elect_gen'])
solar.d2.push(item['elect_in_solar']) solar.d2.push(item['elect_in'])
storage.d1.push(item['elect_store']) storage.d1.push(item['elect_store'])
storage.d2.push(item['elect_discharge']) storage.d2.push(item['elect_discharge'])
@@ -113,21 +111,32 @@ function updatePageData() {
// var data2 = [1, 2, 3, 4, 3, 2, 1] // var data2 = [1, 2, 3, 4, 3, 2, 1]
// var data3 = [1, 2, 3, 4, 3, 2, 1] // var data3 = [1, 2, 3, 4, 3, 2, 1]
mychartSolar.setOption({ series: [{ data: solar.d1 }, { data: solar.d2 }, { data: solar.d3 }] }); var xdata = []
mychartStorage.setOption({ series: [{ data: storage.d1 }, { data: storage.d2 }, { data: storage.d3 }] }); for (let i = 6; i >= 0; i--) {
mychartLoad.setOption({ series: [{ data: load.d1 }, { data: load.d2 }, { data: load.d3 }] }); const date = new Date();
mychartCharge.setOption({ series: [{ data: charge.d1 }, { data: charge.d2 }, { data: charge.d3 }] }); date.setDate(date.getDate() - i);
mychartAlert.setOption({ series: [{ data: solar.d1 }, { data: solar.d2 }, { data: solar.d3 }] }); xdata.push(G.formatDateMD(date));
}
updateEchartBar(mychartSolar, xdata, [{ data: solar.d1 }, { data: solar.d2 }, { data: solar.d3 }])
updateEchartBar(mychartStorage, xdata, [{ data: storage.d1 }, { data: storage.d2 }, { data: storage.d3 }])
updateEchartBar(mychartLoad, xdata, [{ data: load.d1 }, { data: load.d2 }, { data: load.d3 }])
updateEchartBar(mychartCharge, xdata, [{ data: charge.d1 }, { data: charge.d2 }, { data: charge.d3 }])
updateEchartBar(mychartAlert, xdata, [{ data: solar.d1 }, { data: solar.d2 }, { data: solar.d3 }])
})) }))
G.cppNative.getStatisticPowerDay().then(res => {
mychartRunning.setOption({ series: [{ data: res }] })
})
G.cppNative.getStatisticIrradianceDay().then(res => {
mychartRunning.setOption({ series: [{}, { data: res, yAxisIndex: 1 }] })
})
// 查询数据获取环境信息:光照、风速、环境温度、湿度 // 查询数据获取环境信息:光照、风速、环境温度、湿度
G.cppNative.getEnvironmentInfo().then(res => { G.cppNative.getEnvironmentInfo().then(res => {
$('#envIllumination').text(res['illumination'] + ' Lux') $('#envIllumination').text(res['illumination'] + ' Lux')
$('#envWindspeed').text(res['windspeed'] + ' m/s') $('#envWindspeed').text(res['windspeed'] + ' m/s')
$('#envTemperture').text(res['temperature'] + ' ℃') $('#envTemperture').text(res['temperature'] + ' ℃')
$('#envHumidity').text(res['humidity'] + ' %') $('#envHumidity').text(res['humidity'] + ' %')
}) })
} }
@@ -139,6 +148,8 @@ document.currentScript.addEventListener('DOMNodeRemoved', () => {
}); });
$(document).ready(function () { $(document).ready(function () {
updatePageData() updatePageData()
// 定时器更新页面数据 // 定时器更新页面数据

View File

@@ -253,15 +253,14 @@
</div> </div>
<div id="deviceAttrPop" class="mask"> <div id="deviceAttrPop" class="mask">
<div class="pop" style="height: 500px"> <div id="deviceAttrPopPanel" class="pop" style="height: 500px">
<p id="devicePopTitle" class="pop_title">设备属性设置</p> <p id="devicePopTitle" class="pop_title">设备属性设置</p>
<form id="deviceAttrForm" class="was-validated" style="margin: 0 80px 0 80px; padding-top: 30px"> <form id="deviceAttrForm" class="was-validated" style="margin: 0 80px 0 80px; padding-top: 30px">
<div class="input-group mb-3"></div> <div class="input-group mb-3"></div>
<div class="input-group mb-3"></div> <div class="input-group mb-3"></div>
</form> </form>
<div style="display: flex; justify-content: center; padding: 20px 20px 20px 20px"> <div style="display: flex; justify-content: center; padding: 20px 20px 20px 20px">
<button id="deviceAttrFormOk" class="btn btn-success" style="width: 90px; margin-right: 20px" <button id="deviceAttrFormOk" class="btn btn-success" style="width: 90px; margin-right: 20px">确定</button>
disabled>确定</button>
<button class="btn btn-danger" style="width: 90px; margin-left: 20px" <button class="btn btn-danger" style="width: 90px; margin-left: 20px"
onclick="G.showElement('deviceAttrPop', false)">取消</button> onclick="G.showElement('deviceAttrPop', false)">取消</button>
</div> </div>
@@ -298,7 +297,7 @@
</div> </div>
</form> </form>
<div style="display: flex; justify-content: center; padding: 20px 20px 20px 20px"> <div style="display: flex; justify-content: center; padding: 20px 20px 20px 20px">
<button id="priceFormOk" class="btn btn-success" style="width: 90px; margin-right: 20px" disabled>确定</button> <button id="priceFormOk" class="btn btn-success" style="width: 90px; margin-right: 20px">确定</button>
<button class="btn btn-danger" style="width: 90px; margin-left: 20px" <button class="btn btn-danger" style="width: 90px; margin-left: 20px"
onclick="G.showElement('pricePop', false)">取消</button> onclick="G.showElement('pricePop', false)">取消</button>
</div> </div>

View File

@@ -254,23 +254,18 @@ async function initSubpage(id) {
}) })
}) })
} }
else if (id == 'role') { else if (id == 'role') { }
}
else if (id == 'price') { else if (id == 'price') {
var elementSelectPriceType = document.getElementById('priceForm_type') var elementSelectPriceType = document.getElementById('priceForm_type')
elementSelectPriceType.innerHTML = '' elementSelectPriceType.innerHTML = ''
await G.cppNative.queryPriceTypeList().then(res => { G.cppNative.queryPriceTypeList().then(res => {
res.forEach(item => { res.forEach(item => { elementSelectPriceType.options.add(new Option(item.name, item.id)) })
elementSelectPriceType.options.add(new Option(item.name, item.id))
})
}) })
} }
else if (id == 'policy') { else if (id == 'policy') {
var elementSelectPolicyType = document.getElementById('policyForm_type') var elementSelectPolicyType = document.getElementById('policyForm_type')
elementSelectPolicyType.innerHTML = '' elementSelectPolicyType.innerHTML = ''
for (var k in policyTypeDef) { for (var k in policyTypeDef) { elementSelectPolicyType.options.add(new Option(policyTypeDef[k], k)) }
elementSelectPolicyType.options.add(new Option(policyTypeDef[k], k))
}
} }
var tableInfo = tableDef[id] var tableInfo = tableDef[id]
@@ -280,8 +275,11 @@ async function initSubpage(id) {
return return
} }
// 初始化表格
G.initTable(id, tableInfo) G.initTable(id, tableInfo)
// 初始化弹窗Form
G.initForm(id, popConfirm) G.initForm(id, popConfirm)
if (id == 'device') { G.initForm("deviceAttr", popConfirm) }
// 绑定行编辑 // 绑定行编辑
tableInfo.table.on('click', '#btnRowEdit', function () { tableInfo.table.on('click', '#btnRowEdit', function () {
@@ -310,50 +308,67 @@ function showPop(id, rowData) {
// 设置弹框的参数信息 // 设置弹框的参数信息
G.popSetParams(id, tableInfo.popkeys, popRowdata, callbackPopSetParams) G.popSetParams(id, tableInfo.popkeys, popRowdata, callbackPopSetParams)
if (id == 'user') { if (id == 'user') { G.popSetParamReadonly('user', 'account', isEdit) }
G.popSetParamReadonly('user', 'account', isEdit)
}
} }
var deviceAttrKeyDef = { var deviceAttrKeyDef = { commType: "通讯方式", ip: "通讯地址", port: "通讯端口", isclient: "客户端" }
commType: "通讯方式",
ip: "通讯地址",
port: "通讯端口",
isclient: "客户端",
}
// 显示设备的属性编辑弹窗属性字段格式为JSON: attrs="{}" // 显示设备的属性编辑弹窗属性字段格式为JSON: attrs="{}"
function showPopDeviceAttr(rowData) { function showPopDeviceAttr(rowData) {
G.showElement('deviceAttrPop', true) G.showElement('deviceAttrPop', true)
popRowdata = (rowData && rowData.length > 0) ? rowData : null
var device_id = rowData[0] var device_id = rowData[0]
var deviceType = rowData[1] var deviceType = rowData[1]
var deviceTypeStr = deviceTypeDef[deviceType] var deviceTypeStr = deviceTypeDef[deviceType]
var attrsStr = rowData[9] var attrsStr = rowData[9]
var attrs = { commType: "", ip: "", port: 0, isclient: 1 } var attrs = {}
try { try { attrs = JSON.parse(attrsStr); } catch (error) { attrs = {} }
attrs = JSON.parse(attrsStr); if (Object.keys(attrs).length == 0) { attrs = { commType: "", ip: "", port: 0, isclient: 1 } }
} catch (error) {
}
var elemtForm = document.getElementById('deviceAttrForm') var elemtForm = document.getElementById('deviceAttrForm')
elemtForm.innerHTML = elemtForm.innerHTML =
`<div class="input-group mb-3"> `<div class="input-group mb-3">
<span class="input-group-text" style="width: 90px; background-color: #a6c0da">编号</span> <span class="input-group-text" style="width: 90px; background-color: #a6c0da">ID</span>
<input type="text" class="form-control" value='${device_id}' disabled /> <input type="text" class="form-control" value='${device_id}' disabled />
</div> </div>
<div class="input-group mb-3"> <div class="input-group mb-3">
<span class="input-group-text" style="width: 90px; background-color: #a6c0da">设备类型</span> <span class="input-group-text" style="width: 90px; background-color: #a6c0da">设备类型</span>
<input type="text" class="form-control" value='${deviceTypeStr}' disabled /> <input type="text" class="form-control" value='${deviceTypeStr}' disabled />
</div>` </div>
`
for (k in attrs) { for (k in attrs) {
var title = deviceAttrKeyDef[k] var title = deviceAttrKeyDef[k]
elemtForm.innerHTML += `<div class="input-group mb-3"> var v = attrs[k]
<span class="input-group-text" style="width: 90px; background-color: #a6c0da">${title}</span> if (k == "commType") {
<input type="text" class="form-control" value='${attrs[k]}' />
</div>`
}
}
var optionStr = ('<option ' + (v == "TCP" ? 'selected ' : '') + 'value="TCP">TCP</option>')
+ ('<option ' + (v == "MODBUS" ? 'selected ' : '') + 'value="MODBUS">MODBUS</option>')
+ ('<option ' + (v == "SDK" ? 'selected ' : '') + 'value="SDK">SDK</option>')
+ ('<option ' + (v == "ACTIVEX" ? 'selected ' : '') + 'value="ACTIVEX">ACTIVEX</option>')
elemtForm.innerHTML +=
`<div class="input-group mb-3">
<span class="input-group-text" style="width: 90px; background-color: #a6c0da">${title}</span>
<select id="${k}" class="form-select" required>
${optionStr}
</select>
</div>
`
} else {
elemtForm.innerHTML +=
`<div class="input-group mb-3">
<span class="input-group-text" style="width: 90px; background-color: #a6c0da">${title}</span>
<input id="${k}" type="text" class="form-control" value='${v}' />
</div>
`
}
}
G.cppNative.log(elemtForm.innerHTML);
}
function showPrompt(text, status) { function showPrompt(text, status) {
G.showElement('promptPop', true) G.showElement('promptPop', true)
@@ -404,9 +419,7 @@ function callbackPopGetParams(id, key) {
val = [] val = []
var elemtFormPermission = document.getElementById('roleForm_permission') var elemtFormPermission = document.getElementById('roleForm_permission')
var elemtPermissionList = elemtFormPermission.querySelectorAll('input') var elemtPermissionList = elemtFormPermission.querySelectorAll('input')
elemtPermissionList.forEach((elemt => { elemtPermissionList.forEach((elemt => { val.push({ permission_id: elemt.value, is_open: (elemt.checked ? 1 : 0) }) }))
val.push({ permission_id: elemt.value, is_open: (elemt.checked ? 1 : 0) })
}))
} }
return val return val
} }
@@ -417,13 +430,36 @@ function updateTableData(id) {
if (!queryFunc) return if (!queryFunc) return
queryFunc(1, 10).then((res) => { queryFunc(1, 10).then((res) => {
}) })
} }
// 用户弹窗确认 // 用户弹窗确认
function popConfirm(id) { function popConfirm(id) {
// 设备属性弹窗: 读取属性配置
if (id == "deviceAttr") {
/// 选择所有表单元素(input, select, textarea) $('#parentElement :input')
/// :input 是jQuery特有的选择器可以匹配所有input、select、textarea和button元素。
/// 同时选择input和select元素 $('#parentElement input, #parentElement select')
var param = {}
$("#deviceAttrForm :input").each(function () {
var id = $(this).attr('id'); // 获取id属性
var val = $(this).val(); // 获取value值
if ((id == "port" || id == "isclient")) { param[id] = parseInt(val) }
else if (id == "commType") { }
else if (id) { param[id] = val }
});
G.cppNative.log(JSON.stringify(param))
// 存储更新数据
G.cppNative.log("rowdata: " + JSON.stringify(popRowdata))
var dataId = popRowdata[0]
G.cppNative.updateDevice(dataId, { attrs: JSON.stringify(param) }).then(ret => {
G.showElement('deviceAttrPop', false)
showPrompt((ret == 0 ? '操作成功!' : '操作失败!'), ret)
})
return;
}
// 获取数据表格操作对象 // 获取数据表格操作对象
var mytable = $('#' + id + 'Table').DataTable() var mytable = $('#' + id + 'Table').DataTable()
// 获取弹框的参数信息 // 获取弹框的参数信息
@@ -471,6 +507,5 @@ for (k in deviceTypeDef) {
$(document).ready(function () { $(document).ready(function () {
initSubpage('user') initSubpage('user')
//G.initTable('user', tableDef['user']) //G.initTable('user', tableDef['user'])
}) })

View File

@@ -6,6 +6,8 @@
onclick="initSubPage('storage')">储能设备运行分析</button> onclick="initSubPage('storage')">储能设备运行分析</button>
<button id="chargeBtn" class="btn btn-primary" style="width: 200px;margin-left: 10px" <button id="chargeBtn" class="btn btn-primary" style="width: 200px;margin-left: 10px"
onclick="initSubPage('charge')">充电设备运行分析</button> onclick="initSubPage('charge')">充电设备运行分析</button>
<input id="statisticDate" type="date" class="form-control" style="width: 240px; margin-left: 30px;">
</div> </div>
<hr style="margin: 10px 0 10px 0;" /> <hr style="margin: 10px 0 10px 0;" />
<!-- <div <!-- <div

View File

@@ -2,80 +2,48 @@
var pageDef = { var pageDef = {
solar: { solar: {
charts: [ charts: [
{ id: 'solarEchart1', echart: null, type: 'bar', series: ['发电电量', '入网电量'] }, { id: 'solarEchart1', echart: null, type: 'bar', series: ['发电电量', '入网电量'], yAxisLeft: "电量kWh", yAxisRight: "" },
{ id: 'solarEchart2', echart: null, type: 'bar', series: ['发电时长', '故障次数'] }, { id: 'solarEchart2', echart: null, type: 'bar', series: ['发电时长', '故障次数'], yAxisLeft: "时长s", yAxisRight: "次数" },
{ id: 'solarEchart3', echart: null, type: 'line', series: ['日电压', '日电流'] }, { id: 'solarEchart3', echart: null, type: 'line', series: ['日电压', '日电流'], yAxisLeft: "电压V", yAxisRight: "电流A" },
{ id: 'solarEchart4', echart: null, type: 'line', series: ['日功率'] } { id: 'solarEchart4', echart: null, type: 'line', series: ['日功率'], yAxisLeft: "功率kw" },
], ],
}, },
storage: { storage: {
charts: [ charts: [
{ id: 'storageEchart1', echart: null, type: 'bar', series: ['储能电量', '放电电量'] }, { id: 'storageEchart1', echart: null, type: 'bar', series: ['储能电量', '放电电量'], yAxisLeft: "电量kWh" },
{ id: 'storageEchart2', echart: null, type: 'bar', series: ['储能时长', '放电时长', '故障次数'] }, { id: 'storageEchart2', echart: null, type: 'bar', series: ['储能时长', '放电时长', '故障次数'], yAxisLeft: "时长s" },
{ id: 'storageEchart3', echart: null, type: 'line', series: ['日电压', '日电流'] }, { id: 'storageEchart3', echart: null, type: 'line', series: ['日电压', '日电流'], yAxisLeft: "电压V" },
{ id: 'storageEchart4', echart: null, type: 'line', series: ['日功率'] } { id: 'storageEchart4', echart: null, type: 'line', series: ['日功率'], yAxisLeft: "功率kw" }
], ],
}, },
charge: { charge: {
charts: [ charts: [
{ id: 'chargeEchart1', echart: null, type: 'bar', series: ['充电电量', '充电收益'] }, { id: 'chargeEchart1', echart: null, type: 'bar', series: ['充电电量', '充电收益'], yAxisLeft: "电量kWh" },
{ id: 'chargeEchart2', echart: null, type: 'bar', series: ['充电次数', '充电时长', '故障次数'] }, { id: 'chargeEchart2', echart: null, type: 'bar', series: ['充电次数', '充电时长', '故障次数'], yAxisLeft: '次数' },
{ id: 'chargeEchart3', echart: null, type: 'line', series: ['日电压', '日电流'] }, { id: 'chargeEchart3', echart: null, type: 'line', series: ['日电压', '日电流'], yAxisLeft: "电压V" },
{ id: 'chargeEchart4', echart: null, type: 'line', series: ['日功率'] } { id: 'chargeEchart4', echart: null, type: 'line', series: ['日功率'], yAxisLeft: "功率kw" }
], ],
} }
} }
var xdataBar = ['2025/3/1', '2025/3/2', '2025/3/3', '2025/3/4', '2025/3/5', '2025/3/6', '2025/3/7']
var ydataBar = [
[30, 28, 35, 18, 36, 27, 19],
[10, 32, 20, 33, 39, 13, 22],
]
var curActiveId = 'solar'
function getRandomCurveData(m, n) { var activeModuleId = ''
var data = []
var t0 = Date.parse('2025-03-01 00:00:00')
var step = 600
var N = 86400 / step
var y = 500;
for (var i = 0; i < N; ++i) {
// data[i] = { x: t0 + i * step * 1000, y: Math.sin(i * 0.1) * (n - m) }
y = Math.floor((y + RAND(0, 50) - 25) * 100) / 100
data[i] = { x: t0 + i * step * 1000, y: y }
}
return data
}
function getCurveData(a, b, n) {
var d = [[], []]
for (var i = 0; i < n; ++i) {
var v = RAND(a, b)
d[0].push(v)
d[1].push(v - RAND(50, 200))
}
return d
}
function RAND(a, b) {
return a + Math.random() * (b - a)
}
// 点击子页面菜单按钮,切换子页面 // 点击子页面菜单按钮,切换子页面
function initSubPage(id) { function initSubPage(id) {
//if (curActiveId == id) return //if (activeModuleId == id) return
// 切换子菜单按钮样式,旧选择按钮恢复样式 // 切换子菜单按钮样式,旧选择按钮恢复样式
if (curActiveId && curActiveId != '') { if (activeModuleId && activeModuleId != '') {
document.getElementById(curActiveId + 'Btn').className = 'btn btn-primary' document.getElementById(activeModuleId + 'Btn').className = 'btn btn-primary'
// 隐藏旧菜单对应的表格子页面 // 隐藏旧菜单对应的表格子页面
$('#' + curActiveId).hide() $('#' + activeModuleId).hide()
} }
curActiveId = id activeModuleId = id
// 切换子菜单按钮样式,新选择按钮设置样式 // 切换子菜单按钮样式,新选择按钮设置样式
document.getElementById(curActiveId + 'Btn').className = 'btn btn-success btn-lg' document.getElementById(activeModuleId + 'Btn').className = 'btn btn-success btn-lg'
// 显示新菜单对应的表格子页面 // 显示新菜单对应的表格子页面
$('#' + curActiveId).show() $('#' + activeModuleId).show()
var page = pageDef[id] var page = pageDef[id]
if (!page) { if (!page) {
@@ -83,46 +51,131 @@ function initSubPage(id) {
return return
} }
// 更新曲线数据
G.cppNative.getCurveDataDay(200, 300).then(res => {
updateEchartCurve(pageDef[activeModuleId].charts[2].echart, 0, res)
})
G.cppNative.getCurveDataDay(100, 300).then(res => {
updateEchartCurve(pageDef[activeModuleId].charts[2].echart, 1, res)
})
G.cppNative.getCurveDataDay(300, 400).then(res => {
updateEchartCurve(pageDef[activeModuleId].charts[3].echart, 0, res)
})
// 柱状图的 x 轴, 7天的时间的日期
var dateStr = $('#statisticDate').val()
var xdata = []
for (let i = 6; i >= 0; i--) {
const date = new Date(dateStr);
date.setDate(date.getDate() - i);
xdata.push(G.formatDateMD(date));
}
page.charts.forEach(item => { page.charts.forEach(item => {
if (item.type == 'bar') { if (item.type == 'bar') {
item.echart = initEchartBar(item.id, item.series) item.echart = initEchartBar(item.id, item.series, xdata, item.yAxisLeft, item.yAxisRight)
for (var i = 0; i < item.series.length; ++i) { for (var i = 0; i < item.series.length; ++i) {
//updateEchartBar(item.echart, i, xdataBar, ydataBar[i]) //updateEchartBar(item.echart, i, xdataBar, ydataBar[i])
} }
} else if (item.type == 'line') { } else if (item.type == 'line') {
item.echart = initEchartCurve(item.id, item.series) item.echart = initEchartCurve(item.id, item.series, item.yAxisLeft, item.yAxisRight)
for (var i = 0; i < item.series.length; ++i) { for (var i = 0; i < item.series.length; ++i) {
updateEchartCurve(item.echart, i, G.getRandDataDay(100 * (i + 1), 200 * (i + 1))) updateEchartCurve(item.echart, i, G.getRandDataDay(100 * (i + 1), 200 * (i + 1)))
} }
} }
if (id == 'solar') {
if (item.id == 'solarEchart1') {
var d = getCurveData(400, 500, 7)
updateEchartBar(item.echart, 0, xdataBar, d[0])
updateEchartBar(item.echart, 1, xdataBar, d[1])
} else if (item.id == 'solarEchart2') {
var d = getCurveData(480, 780, 7)
updateEchartBar(item.echart, 0, xdataBar, d[0])
updateEchartBar(item.echart, 1, xdataBar, [0, 2, 0, 0, 3, 0, 1])
}
} else if (id == 'storage') {
} else if (id == 'charge') {
}
}); });
} }
window.onresize = () => { // 更新页面数据
var page = pageDef[curActiveId] function updatePageData() {
charts.charts.forEach(item => { // 柱状图的 x 轴, 7天的时间的日期
if (item.echart) { item.echart.resize() } var dateStr = $('#statisticDate').val()
}) var xdata = []
for (let i = 6; i >= 0; i--) {
const date = new Date(dateStr);
date.setDate(date.getDate() - i);
xdata.push(G.formatDateMD(date));
}
// 光伏统计
if (activeModuleId == "solar") {
G.cppNative.getStatisticDay(7).then(res => {
var d1 = [], d2 = [], d3 = [], d4 = [], d5 = []
res.forEach(item => {
d1.push(item['elect_gen'])
d2.push(item['elect_in'])
d3.push(item['elect_gen_t'])
d4.push(item['num_fault_solar'])
})
// 发电电量、入网电量
updateEchartBar(pageDef.solar.charts[0].echart, xdata, [{ data: d1 }, { data: d2 }]);
// 发电时长、故障次数
updateEchartBar(pageDef.solar.charts[1].echart, xdata, [{ data: d3 }, { data: d4 }]);
})
}
// 储能统计
else if (activeModuleId == "storage") {
G.cppNative.getStatisticDay(7).then(res => {
var d1 = [], d2 = [], d3 = [], d4 = [], d5 = []
res.forEach(item => {
d1.push(item['elect_store'])
d2.push(item['elect_discharge'])
d3.push(item['elect_store_t'])
d4.push(item['elect_discharge_t'])
d5.push(item['num_fault_store'])
})
// 储能电量、放电电量
updateEchartBar(pageDef.storage.charts[0].echart, xdata, [{ data: d1 }, { data: d2 }]);
// 储能时长、放电时长、故障次数
updateEchartBar(pageDef.storage.charts[1].echart, xdata, [{ data: d3 }, { data: d4 }, { data: d5 }]);
})
}
// 充电统计
else if (activeModuleId == "charge") {
G.cppNative.getStatisticDay(7).then(res => {
var d1 = [], d2 = [], d3 = [], d4 = [], d5 = []
res.forEach(item => {
d1.push(item['elect_charge'])
d2.push(item['income_charge'])
d3.push(item['num_charge'])
d4.push(item['elect_charge_t'])
d5.push(item['num_fault_charge'])
})
// 充电电量、充电收益
updateEchartBar(pageDef.charge.charts[0].echart, xdata, [{ data: d1 }, { data: d2 }]);
// 充电次数、充电时长、故障次数
updateEchartBar(pageDef.charge.charts[1].echart, xdata, [{ data: d3 }, { data: d4 }, { data: d5 }]);
})
}
} }
window.onresize = () => {
var page = pageDef[activeModuleId]
page.charts.forEach(item => { if (item.echart) { item.echart.resize() } })
}
/// 清理资源
var timerId = null
document.currentScript.addEventListener('DOMNodeRemoved', () => {
G.cppNative.log('DOMNodeRemoved: 统计分析')
clearInterval(timerId);
});
$(document).ready(function () { $(document).ready(function () {
var dateStr = new Date().toLocaleString('zh', { year: 'numeric', month: '2-digit', day: '2-digit' }).replace(/\//g, '-')
$('#statisticDate').val(dateStr)
document.getElementById('statisticDate').addEventListener('input', function (e) {
// updatePageData()
});
initSubPage("solar") initSubPage("solar")
updatePageData()
// 定时器更新页面数据
timerId = setInterval(updatePageData, 1000)
}) })

View File

@@ -86,8 +86,8 @@
<div class="myrow" <div class="myrow"
style="font-size: 14px; font-weight: 600; justify-content: space-between; border: solid #00fffb; border-radius: 0 0 8px 8px; border-width: 0 1px 1px 1px; height: 40px; line-height: 40px;"> style="font-size: 14px; font-weight: 600; justify-content: space-between; border: solid #00fffb; border-radius: 0 0 8px 8px; border-width: 0 1px 1px 1px; height: 40px; line-height: 40px;">
<div style="width: 33%; text-align: center;">#1</div> <div style="width: 33%; text-align: center;">#1</div>
<div style="width: 33%; text-align: center;">20℃</div> <div id="envTemperture" style="width: 33%; text-align: center;">20℃</div>
<div style="width: 33%; text-align: center;">20%</div> <div id="envHumidity" style="width: 33%; text-align: center;">20%</div>
</div> </div>
</div> </div>
@@ -142,12 +142,69 @@
<div id="deviceSettingPop" class="mask" style="display: none;"> <div id="deviceSettingPop" class="mask" style="display: none;">
<div <div
style="margin: 200px auto;width:800px; padding: 20px; height: 500px; background-color: #012036f0; border:4px solid #14dcfb; "> style="margin: 200px auto; padding: 20px; width:550px; height: 380px; background-color: #012036f0; border:4px solid #14dcfb; ">
<div id="popDeviceInfo" style="height: 30px;"></div>
<div class="myline-b" style="color:white;">控制功能</div> <div class="myline-b" style="color:white;">控制功能</div>
<div style="display: flex; justify-content: center; width: 100%; margin-top: 100px;">
<div class="myrow" style="height: 26px; margin-top:30px;">
<div style="width: 120px;">开关机控制</div>
<div class="btn-group" style="width:80px;">
<input id="deviceSwitch_on" type="checkbox" class="btn-check" autocomplete="off"
onclick="G.switchSetStatus('deviceSwitch_on', 'deviceSwitch_off', this.checked)" />
<label class="btn btn-outline-primary" for="deviceSwitch_on" style="padding: 0;"></label>
<input id="deviceSwitch_off" type="checkbox" class="btn-check" autocomplete="off"
onclick="G.switchSetStatus('deviceSwitch_on', 'deviceSwitch_off', !this.checked)" checked />
<label class="btn btn-outline-danger" for="deviceSwitch_off" style="padding: 0;"></label>
</div>
</div>
<div class="myrow" style="height: 26px; margin-top:16px;">
<div style="width: 120px;">功率调控</div>
<div class="btn-group" style="width:80px;">
<input id="powerCtrl_on" type="checkbox" class="btn-check" autocomplete="off"
onclick="G.switchSetStatus('powerCtrl_on', 'powerCtrl_off', this.checked)" />
<label class="btn btn-outline-primary" for="powerCtrl_on" style="padding: 0;"></label>
<input id="powerCtrl_off" type="checkbox" class="btn-check" autocomplete="off"
onclick="G.switchSetStatus('powerCtrl_on', 'powerCtrl_off', !this.checked)" checked />
<label class="btn btn-outline-danger" for="powerCtrl_off" style="padding: 0;"></label>
</div>
<div style="margin-left:60px; width: 90px;">调控策略</div>
<select class="form-select" aria-label="选择策略"
style="width: 100px; padding: 0 0 0 10px; background-color: #0c283b; color: white;">
<option value="0">策略1</option>
<option value="1">策略2</option>
<option value="2">策略3</option>
<option value="3">策略4</option>
</select>
</div>
<div class="myrow" style="height: 26px; margin-top:16px;">
<div style="width: 120px;">充放电模式控制</div>
<div class="btn-group" style="width:80px;">
<input id="chargeMode_on" type="checkbox" class="btn-check" autocomplete="off"
onclick="G.switchSetStatus('chargeMode_on', 'chargeMode_off', this.checked)" />
<label class="btn btn-outline-primary" for="chargeMode_on" style="padding: 0;"></label>
<input id="chargeMode_off" type="checkbox" class="btn-check" autocomplete="off"
onclick="G.switchSetStatus('chargeMode_on', 'chargeMode_off', !this.checked)" checked />
<label class="btn btn-outline-danger" for="chargeMode_off" style="padding: 0;"></label>
</div>
</div>
<div class="myrow" style="height: 26px; margin-top:16px;">
<div style="width: 120px;">并离网模式控制</div>
<div class="btn-group" style="width:80px;">
<input id="workMode_on" type="checkbox" class="btn-check" autocomplete="off"
onclick="G.switchSetStatus('workMode_on', 'workMode_off', this.checked)" />
<label class="btn btn-outline-primary" for="workMode_on" style="padding: 0;"></label>
<input id="workMode_off" type="checkbox" class="btn-check" autocomplete="off"
onclick="G.switchSetStatus('workMode_on', 'workMode_off', !this.checked)" checked />
<label class="btn btn-outline-danger" for="workMode_off" style="padding: 0;"></label>
</div>
</div>
<div style="display: flex; justify-content: flex-end; width: 100%; margin-top: 50px; ">
<button class="btn btn-success" style="width:80px; color: white;" <button class="btn btn-success" style="width:80px; color: white;"
onclick="G.showElement('deviceSettingPop', false)">确定</button> onclick="G.showElement('deviceSettingPop', false)">确定</button>
<button class="btn btn-default" style="width:80px; color: white; margin-left: 50px;" <button class="btn btn-default" style="width:80px; color: white; margin-left: 20px;"
onclick="G.showElement('deviceSettingPop', false)">取消</button> onclick="G.showElement('deviceSettingPop', false)">取消</button>
</div> </div>
</div> </div>

View File

@@ -113,14 +113,14 @@ function setDeviceCardParam(deviceid, subid, key, text) {
} }
var activeCardBtn = null var activeCardBtn = null
var activeModuleId = null
function onClickCardBtn(btn, id) { function onClickCardBtn(btn, id) {
if (activeCardBtn) activeCardBtn.className = 'mycardbtn' if (activeCardBtn) activeCardBtn.className = 'mycardbtn'
activeCardBtn = btn activeCardBtn = btn
activeCardBtn.className = 'mycardbtn-active' activeCardBtn.className = 'mycardbtn-active'
activeModuleId = id
var deviceType = id initDeviceList(activeModuleId)
initDeviceList(deviceType)
} }
function creatElementSwitch(id, x, y) { function creatElementSwitch(id, x, y) {
@@ -223,19 +223,46 @@ function initDeviceList(deviceType) {
}) })
} }
var elemtPanel = document.getElementById(item.device_id) var elemtPanel = document.getElementById(item.device_id)
//JSON.stringify(item)
var paramStr = JSON.stringify(item).replace(/"/g, '"')
elemtPanel.innerHTML += `<button elemtPanel.innerHTML += `<button
class="btn btn-outline-primary btn-sm" class="btn btn-outline-primary btn-sm"
style="height:30px; margin: 5px 0 0 10px" style="height:30px; margin: 5px 0 0 10px"
onclick="onDeviceSetting(${item.device_id})"> onclick='onDeviceSetting(${paramStr})'>
参数设置 参数设置
</button>` </button>`
}) })
}) })
} }
function onDeviceSetting(deviceid) { function onDeviceSetting(param) {
var deviceType = getDeviceTypeName(param.type)
$('#popDeviceInfo').text(param.device_id + ":" + deviceType + ":" + param.name)
G.showElement('deviceSettingPop', true) G.showElement('deviceSettingPop', true)
if (deviceType == "逆变器") {
}
}
function updatePageData() {
if (activeModuleId == "security") {
// 查询数据获取环境信息:光照、风速、环境温度、湿度
G.cppNative.getEnvironmentInfo().then(res => {
// $('#envIllumination').text(res['illumination'] + ' Lux')
// $('#envWindspeed').text(res['windspeed'] + ' m/s')
$('#envTemperture').text(res['temperature'] + '℃')
$('#envHumidity').text(res['humidity'] + '%')
})
// 消防信息
G.cppNative.getFireInfo().then(res => {
//res.data.forEach(item => { })
})
}
} }
// ==================================================================================================== // ====================================================================================================
@@ -252,9 +279,6 @@ document.currentScript.addEventListener('DOMNodeRemoved', () => {
$(document).ready(function () { $(document).ready(function () {
onClickCardBtn(document.getElementById('solarCardBtn'), 'solar') onClickCardBtn(document.getElementById('solarCardBtn'), 'solar')
// timerId = setInterval(() => {
// updateParam('1', '电压', Math.floor(Math.random() * 220) + ' V')
// }, 2000);
G.cppNative.getDeviceInfo([0]).then((res) => { G.cppNative.getDeviceInfo([0]).then((res) => {
var nums = { var nums = {
@@ -271,6 +295,7 @@ $(document).ready(function () {
if (item.err != 0) nums[deviceType].numFault += 1 if (item.err != 0) nums[deviceType].numFault += 1
} }
}) })
$('#solarDeviceNum').text('光伏板数量: ' + nums['103'].num + ' 个') $('#solarDeviceNum').text('光伏板数量: ' + nums['103'].num + ' 个')
$('#solarIdleNum').text('空闲: ' + nums['103'].numIdle + ' 个') $('#solarIdleNum').text('空闲: ' + nums['103'].numIdle + ' 个')
$('#solarOfflineNum').text('离线: ' + nums['103'].numOffline + ' 个') $('#solarOfflineNum').text('离线: ' + nums['103'].numOffline + ' 个')
@@ -285,7 +310,8 @@ $(document).ready(function () {
$('#chargeIdleNum').text('空闲: ' + nums['108'].numIdle + ' 个') $('#chargeIdleNum').text('空闲: ' + nums['108'].numIdle + ' 个')
$('#chargeOfflineNum').text('离线: ' + nums['108'].numOffline + ' 个') $('#chargeOfflineNum').text('离线: ' + nums['108'].numOffline + ' 个')
$('#chargeFaultNum').text('故障: ' + nums['108'].numFault + ' 个') $('#chargeFaultNum').text('故障: ' + nums['108'].numFault + ' 个')
}) })
// 定时更新页面数据
timerId = setInterval(updatePageData, 1000);
}) })

View File

@@ -4,42 +4,25 @@ var chartDef = [
{ id: 'loadEchart', echart: null, type: 'line', series: ['预测功率', '实时功率'] }, { id: 'loadEchart', echart: null, type: 'line', series: ['预测功率', '实时功率'] },
] ]
function RAND(a, b) {
return (a + Math.random() * (b - a))
}
function getRandomCurveData(m, n) { function getRandomCurveData(m, n) {
var data = [] var data = []
var t0 = Date.parse('2025-03-01 00:00:00') var t0 = Date.parse('2025-07-01 00:00:00')
var step = 600
var N = 86400 / step
for (var i = 0; i < N; ++i) {
//data[i] = { x: t0 + i * step * 1000, y: Math.sin(i * 0.1) * (n - m) }
data[i] = { x: t0 + i * step * 1000, y: RAND(m, n) }
}
return data
}
function getRandomData1(m, n) {
var data = []
var t0 = Date.parse('2025-03-01 00:00:00')
var step = 600 var step = 600
var N = 86400 / step var N = 86400 / step
var y = 500; var y = 500;
for (var i = 0; i < N; ++i) { for (var i = 0; i < N; ++i) {
//data[i] = { x: t0 + i * step * 1000, y: Math.sin(i * 0.1) * (n - m) } var x = t0 + i * step * 1000
y = Math.floor((y + RAND(0, 50) - 25) * 100) / 100 y = Math.floor((y + RAND(0, 20) - 10) * 100) / 100
data[i] = { x: t0 + i * step * 1000, y: y } data[i] = { name: x, value: [x, y] }
} }
return data return data
} }
chartDef.forEach((item) => { chartDef.forEach((item) => {
item.echart = initEchartCurve(item.id, item.series) item.echart = initEchartCurve(item.id, item.series, item.yAxisLeft, item.yAxisRight)
updateEchartCurve(item.echart, 1, getRandomCurveData(100, 200))
updateEchartCurve(item.echart, 1, getRandomData1(100, 200))
for (var i = 0; i < item.series.length; ++i) { for (var i = 0; i < item.series.length; ++i) {
updateEchartCurve(item.echart, i, getRandomData1(300 * (i + 1), 400 * (i + 1))) updateEchartCurve(item.echart, i, getRandomCurveData(300 * (i + 1), 400 * (i + 1)))
} }
}) })

View File

@@ -5,6 +5,82 @@
#include "Operator.h" #include "Operator.h"
struct AppData
{
/////////////////////////////////////////////
/// === 系统 ===
int64_t sysActivationTime {};
/////////////////////////////////////////////
/// === 数据库 ===
struct {
std::string host;
int port;
std::string user;
std::string passwd;
} db;
/////////////////////////////////////////////
/// === 系统统计 ===
// 累计发电量单位kWh
double electGenTatal {};
// 累计入网电量单位kWh
double electInTotal {};
// 累计收益,单位:元
double incomeTotal {};
// 碳减排量, 单位:吨
double ccers {};
/////////////////////////////////////////////
/// === 环境 ===
// 光照度
double illuminance {};
// 辐照度
double irradiance {};
// 风速
double windspeed {};
// 温度
double temperature {};
// 湿度
double humidity {};
/////////////////////////////////////////////
/// === 日统计 ===
struct {
// 发电量
double electGen {};
// 入网电量
double electIn {};
// 发电收益金额
double incomeElect {};
// 储能电量
double electStorage {};
// 储能次数
int numStore {};
// 放电电量
double electDischarge {};
// 放电次数
int numDischarge {};
// 用电电量
double electLoad {};
// 充电电量
double electCharge {};
// 充电次数
int numCharge {};
// 充电收益
double incomeCharge {};
// 故障次数
int numFault {};
// 故障次数:光伏设备
int numFaultSolar {};
// 故障次数:储能设备
int numFaultStorage {};
// 故障次数:负荷设备
int numFaultLoad {};
} statDay;
};
class Application class Application
{ {
public: public:
@@ -22,7 +98,6 @@ public:
void runThreadMain(); void runThreadMain();
void runThreadDevice(); void runThreadDevice();
private: private:

View File

@@ -205,6 +205,23 @@ int64_t Utils::timeNowMS()
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
//return std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now()).time_since_epoch().count(); //return std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now()).time_since_epoch().count();
} }
int64_t Utils::timeNowDate()
{
// 获取当前时间
auto now = std::chrono::system_clock::now();
time_t t = std::chrono::system_clock::to_time_t(now);
// 转换为本地时间结构体
struct tm* tmlocal = localtime(&t);
// 设置时分秒为0
tmlocal->tm_hour = 0;
tmlocal->tm_min = 0;
tmlocal->tm_sec = 0;
// 转换回time_t
return mktime(tmlocal);
}
string Utils::timeNowStr(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/) string Utils::timeNowStr(std::string fmt /*= "%Y-%m-%dT%H:%M:%S"*/)
{ {
auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());

View File

@@ -41,6 +41,7 @@ public:
static int64_t timeNow(); static int64_t timeNow();
static int64_t timeNowMS(); static int64_t timeNowMS();
static int64_t timeNowDate();
static string timeNowStr(std::string fmt = "%Y-%m-%d %H:%M:%S"); static string timeNowStr(std::string fmt = "%Y-%m-%d %H:%M:%S");
static string timeNowStrMS(std::string fmt = "%Y-%m-%d %H:%M:%S"); static string timeNowStrMS(std::string fmt = "%Y-%m-%d %H:%M:%S");
static string timeStr(int64_t ts, std::string fmt = "%Y-%m-%d %H:%M:%S"); static string timeStr(int64_t ts, std::string fmt = "%Y-%m-%d %H:%M:%S");

View File

@@ -438,6 +438,7 @@ int MyWebHandler::updateDevice(const QString& deviceId, QVariantMap params)
JSgetReqParam("model", params, fields); JSgetReqParam("model", params, fields);
JSgetReqParam("factory", params, fields); JSgetReqParam("factory", params, fields);
JSgetReqParam("is_open", params, fields); JSgetReqParam("is_open", params, fields);
JSgetReqParam("attrs", params, fields);
if (fields.size() == 0) if (fields.size() == 0)
{ {
return 0; return 0;
@@ -783,16 +784,70 @@ QVariantList MyWebHandler::getStatisticDay(const QString& startDate, int nday)
{ {
QVariantMap row; QVariantMap row;
row["dt"] = ""; row["dt"] = "";
row["elect_gen_solar"] = float(i);
row["elect_in_solar"] = float(i); // 光伏:发电量、入网电量、发电时长、故障次数
row["income_solar"] = float(i); row["elect_gen"] = float(i);
row["elect_in"] = float(i);
row["elect_gen_t"] = float(i);
row["income_elect"] = float(i); // 发电收益
row["num_fault_solar"] = float(i);
// 储能:储能电量、放电电量、储能时长、放电时长、故障次数
row["elect_store"] = float(i); row["elect_store"] = float(i);
row["elect_discharge"] = float(i); row["elect_discharge"] = float(i);
row["elect_load"] = float(i); row["elect_store_t"] = float(i);
row["elect_discharge_t"] = float(i);
row["num_fault_store"] = float(i);
// 充电:充电电量、充电收益、充电次数、充电时长、故障次数
row["elect_charge"] = float(i); row["elect_charge"] = float(i);
row["num_charge"] = float(i); row["elect_charge_t"] = float(i);
row["income_charge"] = float(i); row["income_charge"] = float(i);
row["num_charge"] = float(i);
row["num_fault_charge"] = float(i);
// 负载:
row["elect_load"] = float(i);
row["num_fault_load"] = float(i);
result << row; result << row;
} }
return result; return result;
} }
static QVariantList RandCurveDataDay(int a, int b)
{
QVariantList d;
int64_t t0 = Utils::timeNowDate();
int step = 600;
int N = 86400 / step;
int y = a;
for (int i = 0; i <= N; ++i) {
int t = t0 + i * step * 1000;
y = std::floor((y + Utils::random(0, 20) - 10) * 100) / 100;
QVariantMap item;
item["name"] = t;
item["value"] = QVariantList {t, y};
d << item;
}
return d;
}
// 获取一天的发电功率
QVariantList MyWebHandler::getStatisticPowerDay()
{
static auto tempData = RandCurveDataDay(500, 600);
return tempData;
}
// 获取一天的辐照度
QVariantList MyWebHandler::getStatisticIrradianceDay()
{
static auto tempData = RandCurveDataDay(500, 600);
return tempData;
}
QVariantList MyWebHandler::getCurveDataDay(int a, int b)
{
return RandCurveDataDay(a, b);
}

View File

@@ -129,9 +129,12 @@ public slots:
QVariantMap getStatisticTotal(); QVariantMap getStatisticTotal();
// 获取按天统计数据 // 获取按天统计数据
QVariantList getStatisticDay(const QString& startDate, int nday=7); QVariantList getStatisticDay(const QString& startDate, int nday=7);
// 获取一天的 // 获取一天的发电功率
QVariantList getStatisticPowerDay();
// 获取一天的辐照度
QVariantList getStatisticIrradianceDay();
QVariantList getCurveDataDay(int a, int b);
public: public:
QString nativeText_; QString nativeText_;