You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
848 lines
28 KiB
848 lines
28 KiB
window.isJSON = function (str) {
|
|
if (typeof str != 'string') { // 1、传入值必须是 字符串
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
var obj = JSON.parse(str); // 2、仅仅通过 JSON.parse(str),不能完全检验一个字符串是JSON格式的字符串
|
|
if (typeof obj == 'object' && obj) { //3、还必须是 object 类型
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// 解析最新值
|
|
window.resolveScadaNewValue = (defaultValue) => {
|
|
if (defaultValue) {
|
|
let valueParsed = [{
|
|
val: ''
|
|
}];
|
|
let realValue = ''
|
|
try {
|
|
valueParsed = JSON.parse(defaultValue) || [{ val: '' }];
|
|
if (valueParsed[0]) {
|
|
realValue = valueParsed[0].val;
|
|
} else {
|
|
realValue = valueParsed[0] === undefined ? '' : valueParsed[0]
|
|
}
|
|
} catch (err) {
|
|
console.log('err', err)
|
|
}
|
|
return realValue;
|
|
} else {
|
|
return ''
|
|
}
|
|
}
|
|
|
|
// 字体颜色动态改变
|
|
window.changeFontColor = (style, properties) => {
|
|
const { uiData } = properties.dynamic || {};
|
|
if (uiData) {
|
|
const realValue = window.resolveScadaNewValue(uiData.defaultValue)
|
|
if (realValue !== '') {
|
|
uiData.conditionVariables.forEach((item) => {
|
|
if (item.type === 'rangeColor') {
|
|
let from = item.from;
|
|
let to = item.to;
|
|
if (item.from >= item.to) {
|
|
from = item.to;
|
|
to = item.from;
|
|
}
|
|
if (item.fontColor && Number(realValue) >= from && Number(realValue) <= to) {
|
|
style.color = item.fontColor
|
|
}
|
|
} else if (item.type === 'equal') {
|
|
if (Number(realValue) === Number(item.value)) {
|
|
item.fontColor && (style.color = item.fontColor);
|
|
}
|
|
} else if (item.type === 'boolean') {
|
|
if (realValue === 'true' || realValue === true) {
|
|
item.fontColor && (style.color = item.backColor);
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// 背景颜色改变
|
|
window.changeBackgroundColor = (style, properties) => {
|
|
const { uiData } = properties.dynamic || {};
|
|
if (uiData) {
|
|
const realValue = window.resolveScadaNewValue(uiData.defaultValue)
|
|
if (realValue !== '') {
|
|
uiData.conditionVariables.forEach((item) => {
|
|
if (item.type === 'rangeColor') {
|
|
let from = item.from;
|
|
let to = item.to;
|
|
if (item.from >= item.to) {
|
|
from = item.to;
|
|
to = item.from;
|
|
}
|
|
if (item.backColor && Number(realValue) >= from && Number(realValue) <= to) {
|
|
style.fill = item.backColor
|
|
}
|
|
} else if (item.type === 'equal') {
|
|
if (Number(realValue) === Number(item.value)) {
|
|
item.backColor && (style.fill = item.backColor);
|
|
}
|
|
} else if (item.type === 'boolean') {
|
|
if (realValue === 'true' || realValue === true) {
|
|
item.backColor && (style.fill = item.backColor);
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// 创建 svg 图案
|
|
window.createSvgPattern = (id, href, width, height) => {
|
|
const defsId = 'defs-' + id;
|
|
const patternId = 'pattern-' + id;
|
|
const oldDefs = document.getElementById(defsId);
|
|
const oldPattern = document.getElementById(patternId);
|
|
if (oldDefs) {
|
|
oldDefs.remove();
|
|
}
|
|
if (oldPattern) {
|
|
oldPattern.remove();
|
|
}
|
|
const defs = document.createElement('defs');
|
|
defs.setAttribute('id', 'defs-' + id);
|
|
const pattern = document.createElement('pattern');
|
|
pattern.setAttribute('id', 'pattern-' + id);
|
|
pattern.setAttribute('width', '1');
|
|
pattern.setAttribute('height', '1');
|
|
pattern.setAttribute('patternUnits', 'objectBoundingBox');
|
|
const img = document.createElement('image');
|
|
img.setAttribute('width', width);
|
|
img.setAttribute('height', height);
|
|
img.setAttribute("xlink:href", href);
|
|
pattern.appendChild(img);
|
|
defs.append(pattern);
|
|
const canvasOverlay = document.querySelector('.lf-canvas-overlay')
|
|
canvasOverlay.prepend(defs);
|
|
}
|
|
|
|
var messageFn = (msg) => {
|
|
layui.use('layer', function () {
|
|
const layer = layui.layer;
|
|
layer.msg(msg);
|
|
});
|
|
}
|
|
|
|
window.totalListeners = {
|
|
'click': [],
|
|
'dbclick': [],
|
|
'mouseenter': [],
|
|
'mouseleave': []
|
|
}
|
|
// 没有明确点击下发指令的部件,像普通的图形或图片也有可能会下发指令,因此也需要绑定好事件监听。
|
|
window.nodeEventsListeners = (self) => {
|
|
const clickHandler = function ({ data }) {
|
|
if (data.id !== this.id) return
|
|
this.graphModel.eventCenter.emit('myNode:click', { data: this, e: this.realValue })
|
|
};
|
|
const clickHandlerBound = clickHandler.bind(self)
|
|
window.totalListeners.click.push(clickHandlerBound)
|
|
|
|
const dbclickHandler = function ({ data }) {
|
|
if (data.id !== this.id) return
|
|
this.graphModel.eventCenter.emit('myNode:dbclick', { data: this, e: this.realValue })
|
|
}
|
|
const dbclickHandlerBound = dbclickHandler.bind(self)
|
|
window.totalListeners.dbclick.push(dbclickHandlerBound)
|
|
|
|
const mouseEnterHandler = function ({ data }) {
|
|
if (data.id !== this.id) return
|
|
this.graphModel.eventCenter.emit('myNode:mouseenter', { data: this, e: this.realValue })
|
|
}
|
|
const mouseEnterHandlerBound = mouseEnterHandler.bind(self)
|
|
window.totalListeners.mouseenter.push(mouseEnterHandlerBound)
|
|
|
|
const mouseLeaveHandler = function ({ data }) {
|
|
if (data.id !== this.id) return
|
|
this.graphModel.eventCenter.emit('myNode:mouseleave', { data: this, e: this.realValue })
|
|
}
|
|
const mouseLeaveHandlerBound = mouseLeaveHandler.bind(self)
|
|
window.totalListeners.mouseleave.push(mouseLeaveHandlerBound)
|
|
}
|
|
|
|
|
|
// 判断矩形是否相交
|
|
function calcBoundingBox(boundingBox) {
|
|
const boundingBoxLeft = boundingBox.x
|
|
const boundingBoxRight = boundingBox.x + boundingBox.width || boundingBox.properties.width
|
|
const boundingBoxTop = boundingBox.y
|
|
const boundingBoxBottom = boundingBox.y + boundingBox.height || boundingBox.properties.height
|
|
|
|
return {
|
|
boundingBoxLeft,
|
|
boundingBoxRight,
|
|
boundingBoxTop,
|
|
boundingBoxBottom,
|
|
}
|
|
}
|
|
|
|
function getEdgeWidthHeight(edge) {
|
|
if (!edge)
|
|
return
|
|
let edgeStartPoint = {
|
|
x: 0,
|
|
y: 0,
|
|
}
|
|
let edgeEndPoint = {
|
|
x: 0,
|
|
y: 0,
|
|
}
|
|
if (edge.modelType === 'line-edge') {
|
|
edgeStartPoint = edge.startPoint
|
|
edgeEndPoint = edge.endPoint
|
|
}
|
|
else {
|
|
edgeStartPoint = edge.pointsList[0]
|
|
edgeEndPoint = edge.pointsList[edge.pointsList.length - 1]
|
|
}
|
|
const width = Math.abs(edgeEndPoint.x - edgeStartPoint.x)
|
|
const height = Math.abs(edgeEndPoint.y - edgeStartPoint.y)
|
|
return {
|
|
x: edgeStartPoint.x < edgeEndPoint.x ? edgeStartPoint.x : edgeEndPoint.x,
|
|
y: edgeStartPoint.y < edgeEndPoint.y ? edgeStartPoint.y : edgeEndPoint.y,
|
|
width,
|
|
height,
|
|
}
|
|
}
|
|
// 应该取名为是否相交(图层上移下移用到),为了避免影响原来的代码,就叫这个吧。
|
|
const isInsideBoundingBox = function (elementBox, boundingBox) {
|
|
const elementLeft = elementBox.x
|
|
const elementRight = elementBox.x + elementBox.width
|
|
const elementTop = elementBox.y
|
|
const elementBottom = elementBox.y + elementBox.height
|
|
|
|
const { boundingBoxLeft, boundingBoxRight, boundingBoxBottom, boundingBoxTop } = calcBoundingBox(boundingBox)
|
|
|
|
if (
|
|
elementRight < boundingBoxLeft
|
|
|| elementLeft > boundingBoxRight
|
|
|| elementTop > boundingBoxBottom
|
|
|| elementBottom < boundingBoxTop
|
|
)
|
|
return false // 两个矩形不相交
|
|
|
|
return true // 两个矩形相交
|
|
}
|
|
const isRealInsideBoundingBox = function (elementBox, boundingBox) {
|
|
const elementLeft = elementBox.x
|
|
const elementRight = elementBox.x + elementBox.width
|
|
const elementTop = elementBox.y
|
|
const elementBottom = elementBox.y + elementBox.height
|
|
|
|
const { boundingBoxLeft, boundingBoxRight, boundingBoxBottom, boundingBoxTop } = calcBoundingBox(boundingBox)
|
|
|
|
if (
|
|
(elementLeft >= boundingBoxLeft && elementLeft < boundingBoxRight)
|
|
&& (elementRight <= boundingBoxRight && elementRight > boundingBoxLeft)
|
|
&& (elementTop >= boundingBoxTop && elementTop < boundingBoxBottom)
|
|
&& (elementBottom <= boundingBoxBottom && elementBottom > boundingBoxTop)
|
|
)
|
|
return true // 一个矩形在另一个矩形之内
|
|
|
|
return false
|
|
}
|
|
|
|
const getMaxBoundingBox = function (selects) {
|
|
const { nodes, edges } = selects
|
|
let maxBoundingNode = nodes[0]
|
|
let maxBoundingEdge = getEdgeWidthHeight(edges[0])
|
|
|
|
if (nodes.length > 0) {
|
|
nodes.forEach((node, index) => {
|
|
if (node.type === 'helper-circle-point')
|
|
return
|
|
const area = node.properties.width * node.properties.height
|
|
const oldArea = maxBoundingNode.properties.width * maxBoundingNode.properties.height
|
|
if (area > oldArea)
|
|
maxBoundingNode = node
|
|
})
|
|
}
|
|
|
|
if (maxBoundingNode) {
|
|
const w = maxBoundingNode.width || maxBoundingNode.properties.width
|
|
const h = maxBoundingNode.height || maxBoundingNode.properties.height
|
|
maxBoundingNode = {
|
|
x: maxBoundingNode.x - w / 2,
|
|
y: maxBoundingNode.y - h / 2,
|
|
width: w,
|
|
height: h,
|
|
}
|
|
}
|
|
|
|
if (edges.length > 0) {
|
|
edges.forEach((edge, index) => {
|
|
const edgeWidthHeight = getEdgeWidthHeight(edge)
|
|
if (edgeWidthHeight) {
|
|
const { width, height } = edgeWidthHeight
|
|
const area = width * height
|
|
if (maxBoundingEdge) {
|
|
const { width: oldWidth, height: oldHeight } = maxBoundingEdge
|
|
const oldArea = oldWidth * oldHeight
|
|
if (area > oldArea)
|
|
maxBoundingEdge = edge
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
if (maxBoundingNode && !maxBoundingEdge) {
|
|
return maxBoundingNode
|
|
}
|
|
else if (!maxBoundingNode && maxBoundingEdge) {
|
|
return maxBoundingEdge
|
|
}
|
|
else if (maxBoundingNode && maxBoundingEdge) {
|
|
if ((maxBoundingNode.width * maxBoundingNode.height) > (maxBoundingEdge.width * maxBoundingEdge.height))
|
|
return maxBoundingNode
|
|
|
|
else
|
|
return maxBoundingEdge
|
|
}
|
|
}
|
|
|
|
|
|
// 计算某个元素内部的所有节点元素(不包括连线)
|
|
window.calcInsideAreaElements = (lf, selects) => {
|
|
// 矩形相交的元素
|
|
let areaElements = []
|
|
// 获取最大的 boundingBox
|
|
const maxBounding = getMaxBoundingBox(selects)
|
|
if (!maxBounding)
|
|
return
|
|
|
|
lf.graphModel.nodes.forEach((node) => {
|
|
if (node.type === 'helper-circle-point')
|
|
return
|
|
const ABox = {
|
|
x: node.x - node.width / 2,
|
|
y: node.y - node.height / 2,
|
|
width: node.width,
|
|
height: node.height,
|
|
}
|
|
const isIn = isRealInsideBoundingBox(ABox, maxBounding)
|
|
if (isIn) {
|
|
areaElements.push(node)
|
|
}
|
|
})
|
|
return areaElements
|
|
}
|
|
|
|
// 数据点绑定时候的 数据处理函数, 全局的。
|
|
window.dataProcessFn = function (event) {
|
|
const { info, apiid, defaultValueIndex, values, deviceIdCodeMap } = event;
|
|
const {
|
|
context, service, nodeId, dataPointStr, dynamicFlag,
|
|
dataSource, deviceCode, devices, enableDataHandle, deviceAttrs, calcRules, uniquePoint, dataPoint, attrs, callBacks,
|
|
} = info
|
|
|
|
// console.log('nodeId', nodeId);
|
|
|
|
// console.log('开始数据处理了', deviceIdCodeMap)
|
|
|
|
const totalValues = []
|
|
if (calcRules && calcRules.length > 0) {
|
|
if (deviceAttrs && deviceAttrs.table && deviceAttrs.table.length > 0) {
|
|
const newDeviceTable = []
|
|
const valuesGroup = {}
|
|
const codeAttrToNumMap = {}
|
|
const nums = deviceAttrs.table.map(i => {
|
|
const code = deviceIdCodeMap[i.devices]
|
|
valuesGroup[i.num] = []
|
|
codeAttrToNumMap[code + '-' + i.dataPoint] = i.num
|
|
newDeviceTable.push({
|
|
...i,
|
|
deviceCode: code
|
|
})
|
|
return i.num
|
|
})
|
|
|
|
// 根据 物+属性 聚合分类
|
|
values.forEach((val) => {
|
|
const findNum = codeAttrToNumMap[val.thingCode + '-' + val.attrKey]
|
|
if (findNum) {
|
|
valuesGroup[findNum].push({ ...val })
|
|
}
|
|
})
|
|
calcRules.forEach((rule) => {
|
|
const findNum = newDeviceTable.find(d => d.num === rule.resultAttr)
|
|
if (findNum) {
|
|
// 计算结果属性为已经存在的序号
|
|
valuesGroup[findNum.num].forEach((val, index) => {
|
|
let newFormular = rule.formular
|
|
nums.forEach(n => {
|
|
if (newFormular.includes(n)) {
|
|
const otherValue = valuesGroup[n][index].val
|
|
newFormular = newFormular.replace(n, otherValue)
|
|
}
|
|
})
|
|
const fn = new Function('', `return ${newFormular}`)
|
|
let newValue = fn()
|
|
val.val = newValue
|
|
})
|
|
} else {
|
|
// 创建新的属性
|
|
if (nums.length > 0) {
|
|
// 默认使用第一组的数据长度做为新的数据长度
|
|
const newThingAndAttr = valuesGroup[nums[0]].map((val, index) => {
|
|
let newFormular = rule.formular
|
|
nums.forEach(n => {
|
|
if (newFormular.includes(n)) {
|
|
const otherValue = valuesGroup[n][index].val
|
|
newFormular = newFormular.replace(n, otherValue)
|
|
}
|
|
})
|
|
let newValue = eval(newFormular)
|
|
return {
|
|
thingCode: rule.resultAttr,
|
|
attrKey: rule.resultAttr,
|
|
ts: val.ts,
|
|
val: newValue
|
|
}
|
|
})
|
|
valuesGroup[rule.resultAttr] = newThingAndAttr
|
|
}
|
|
}
|
|
})
|
|
|
|
// console.log('valuesGroup', valuesGroup);
|
|
if (uniquePoint && nums.includes(uniquePoint)) {
|
|
totalValues.push(...valuesGroup[uniquePoint])
|
|
} else {
|
|
Object.values(valuesGroup).forEach(valGroup => {
|
|
totalValues.push(...valGroup)
|
|
})
|
|
}
|
|
// console.log('totalValues', totalValues)
|
|
|
|
let dataPointValue = JSON.stringify(totalValues)
|
|
|
|
return dataPointValue
|
|
}
|
|
}
|
|
}
|
|
|
|
// 寻找最低采集频率
|
|
window.filterMinimumFrequency = (info, historyDatas) => {
|
|
let totals = [];
|
|
const thingGrouped = window._.groupBy(historyDatas, 'thingCode');
|
|
for (const thingKey in thingGrouped) {
|
|
const attrGrouped = window._.groupBy(thingGrouped[thingKey], 'attrKey')
|
|
Object.keys(attrGrouped).forEach((attrkey) => {
|
|
totals.push(info[thingKey].attrs[attrkey].rate);
|
|
})
|
|
}
|
|
const filteredTotals = totals.filter(Boolean)
|
|
if (filteredTotals.length === 0) {
|
|
return 0
|
|
} else {
|
|
return Math.min(...filteredTotals)
|
|
}
|
|
}
|
|
|
|
// 柱状图和折线图的完整时间(数据补全用到)
|
|
window.completeTimesForChart = function (apiDetails, info, historyDatas, apiid) {
|
|
if (!apiDetails) return;
|
|
// 首先确定开始和结束时间
|
|
let startTime = null
|
|
let endTime = null
|
|
if (apiDetails.type === 'range') {
|
|
startTime = apiDetails.startTime
|
|
endTime = apiDetails.endTime
|
|
} else if (apiDetails.type === 'nearest') {
|
|
const { day, hour, minute } = apiDetails.nearest
|
|
endTime = window.dayjs()
|
|
startTime = endTime.subtract(day, 'days').subtract(hour, 'hours').subtract(minute, 'minutes')
|
|
} else if (apiDetails.type === 'interval') {
|
|
const { duration, type } = apiDetails.interval
|
|
if (type === 'day') {
|
|
endTime = window.dayjs().endOf('day')
|
|
startTime = endTime.subtract(duration, 'day').add(1, 'second')
|
|
} else if (type === 'week') {
|
|
endTime = window.dayjs().endOf('week')
|
|
startTime = endTime.subtract(duration, 'week').add(1, 'second')
|
|
} else if (type === 'month') {
|
|
endTime = window.dayjs().endOf('month');
|
|
const lastDayofMonth = endTime.subtract(duration, 'month');
|
|
startTime = lastDayofMonth.endOf('month').add(1, 'second');
|
|
} else if (type === 'year') {
|
|
endTime = window.dayjs().endOf('year')
|
|
startTime = endTime.subtract(duration, 'year').add(1, 'second')
|
|
}
|
|
}
|
|
|
|
if (startTime && endTime) {
|
|
const totalTimes = [dayjs(startTime).millisecond(0).valueOf()];
|
|
const { day, hour, minute } = apiDetails.agg;
|
|
let newEndTime = null;
|
|
|
|
if (day) {
|
|
newEndTime = dayjs(startTime).add(day, 'days').millisecond(0).valueOf();
|
|
totalTimes.push(newEndTime);
|
|
while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
newEndTime = dayjs(newEndTime).add(day, 'days').millisecond(0).valueOf();
|
|
if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
totalTimes.push(newEndTime);
|
|
}
|
|
}
|
|
return totalTimes;
|
|
} else if (hour) {
|
|
newEndTime = dayjs(startTime).add(hour, 'hours').millisecond(0).valueOf();
|
|
totalTimes.push(newEndTime);
|
|
while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
newEndTime = dayjs(newEndTime).add(hour, 'hours').millisecond(0).valueOf();
|
|
if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
totalTimes.push(newEndTime);
|
|
}
|
|
}
|
|
return totalTimes;
|
|
} else if (minute) {
|
|
newEndTime = dayjs(startTime).add(minute, 'minutes').millisecond(0).valueOf();
|
|
totalTimes.push(newEndTime);
|
|
while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
newEndTime = dayjs(newEndTime).add(minute, 'minutes').millisecond(0).valueOf();
|
|
if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
totalTimes.push(newEndTime);
|
|
}
|
|
}
|
|
return totalTimes;
|
|
} else if (apiDetails.type === 'interval' && !apiDetails.func && !day && !hour && !minute) {
|
|
const { duration, type } = apiDetails.interval
|
|
|
|
const min = window.filterMinimumFrequency(info, historyDatas);
|
|
|
|
if (!min) {
|
|
if (type === 'day') {
|
|
newEndTime = dayjs(startTime).add(1, 'hours').millisecond(0).valueOf();
|
|
totalTimes.push(newEndTime);
|
|
while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
newEndTime = dayjs(newEndTime).add(1, 'hours').millisecond(0).valueOf();
|
|
if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
totalTimes.push(newEndTime);
|
|
}
|
|
}
|
|
} else if (type === 'week') {
|
|
newEndTime = dayjs(startTime).add(1, 'days').millisecond(0).valueOf();
|
|
totalTimes.push(newEndTime);
|
|
while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
newEndTime = dayjs(newEndTime).add(1, 'days').millisecond(0).valueOf();
|
|
if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
totalTimes.push(newEndTime);
|
|
}
|
|
}
|
|
} else if (type === 'month') {
|
|
newEndTime = dayjs(startTime).add(1, 'days').millisecond(0).valueOf();
|
|
totalTimes.push(newEndTime);
|
|
while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
newEndTime = dayjs(newEndTime).add(1, 'days').millisecond(0).valueOf();
|
|
if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
totalTimes.push(newEndTime);
|
|
}
|
|
}
|
|
} else if (type === 'year') {
|
|
newEndTime = dayjs(startTime).add(1, 'months').millisecond(0).valueOf();
|
|
totalTimes.push(newEndTime);
|
|
while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
newEndTime = dayjs(newEndTime).add(1, 'months').millisecond(0).valueOf();
|
|
if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
totalTimes.push(newEndTime);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
newEndTime = dayjs(startTime).add(min, 'seconds').millisecond(0).valueOf();
|
|
totalTimes.push(newEndTime);
|
|
while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
newEndTime = dayjs(newEndTime).add(min, 'seconds').millisecond(0).valueOf();
|
|
if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
totalTimes.push(newEndTime);
|
|
}
|
|
}
|
|
}
|
|
return totalTimes;
|
|
} else if (apiDetails.type !== 'interval' && !apiDetails.func && !day && !hour && !minute) {
|
|
const days = startTime.diff(endTime, 'day');
|
|
const months = startTime.diff(endTime, 'month');
|
|
const min = window.filterMinimumFrequency(info, historyDatas);
|
|
|
|
if (!min) {
|
|
if (months === 0 && days === 0) {
|
|
newEndTime = dayjs(startTime).add(1, 'hours').millisecond(0).valueOf();
|
|
totalTimes.push(newEndTime);
|
|
while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
newEndTime = dayjs(newEndTime).add(1, 'hours').millisecond(0).valueOf();
|
|
if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
totalTimes.push(newEndTime);
|
|
}
|
|
}
|
|
} else if (months === 0 && days > 0) {
|
|
newEndTime = dayjs(startTime).add(1, 'days').millisecond(0).valueOf();
|
|
totalTimes.push(newEndTime);
|
|
while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
newEndTime = dayjs(newEndTime).add(1, 'days').millisecond(0).valueOf();
|
|
if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
totalTimes.push(newEndTime);
|
|
}
|
|
}
|
|
} else if (months > 0 && days >= 0) {
|
|
newEndTime = dayjs(startTime).add(1, 'months').millisecond(0).valueOf();
|
|
totalTimes.push(newEndTime);
|
|
while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
newEndTime = dayjs(newEndTime).add(1, 'months').millisecond(0).valueOf();
|
|
if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
totalTimes.push(newEndTime);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
newEndTime = dayjs(startTime).add(min, 'seconds').millisecond(0).valueOf();
|
|
totalTimes.push(newEndTime);
|
|
while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
newEndTime = dayjs(newEndTime).add(min, 'seconds').millisecond(0).valueOf();
|
|
if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
totalTimes.push(newEndTime);
|
|
}
|
|
}
|
|
}
|
|
return totalTimes;
|
|
} else if (day && hour && minute) {
|
|
newEndTime = dayjs(startTime).add(day, 'days').add(hour, 'hours').add(minute, 'minutes').millisecond(0).valueOf();
|
|
totalTimes.push(newEndTime);
|
|
while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
newEndTime = dayjs(newEndTime).add(day, 'days').add(hour, 'hours').add(minute, 'minutes').millisecond(0).valueOf();
|
|
if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
|
|
totalTimes.push(newEndTime);
|
|
}
|
|
}
|
|
return totalTimes;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 柱状图/折线图时间分割,今日-昨日,当月-上月,今年-去年
|
|
window.splitTimes = function (totalTimes, timeCompare) {
|
|
const prev = []
|
|
const curr = []
|
|
if (timeCompare === 'day') {
|
|
totalTimes.forEach((t) => {
|
|
if (dayjs(t).isBefore(dayjs().startOf('day'))) {
|
|
prev.push(t)
|
|
} else {
|
|
curr.push(t)
|
|
}
|
|
})
|
|
} else if (timeCompare === 'month') {
|
|
totalTimes.forEach((t) => {
|
|
if (dayjs(t).month() < dayjs().month()) {
|
|
prev.push(t)
|
|
} else {
|
|
curr.push(t)
|
|
}
|
|
})
|
|
} else if (timeCompare === 'year') {
|
|
totalTimes.forEach((t) => {
|
|
if (dayjs(t).year() < dayjs().year()) {
|
|
prev.push(t)
|
|
} else {
|
|
curr.push(t)
|
|
}
|
|
})
|
|
}
|
|
return {
|
|
prev, curr
|
|
}
|
|
}
|
|
|
|
|
|
// 把 css 渐变颜色转换成 svg 渐变标签
|
|
window.generateSVGGradient = function (gradientString, gradientId) {
|
|
// 解析渐变颜色字符串
|
|
// 解析渐变颜色字符串
|
|
const regex = /linear-gradient\((.+?),(.+?)\s([0-9]+%)\s?,\s(.+?)\s([0-9]+%)\)/;
|
|
const matches = gradientString.match(regex);
|
|
|
|
if (!matches || matches.length < 6) {
|
|
return null;
|
|
}
|
|
|
|
const direction = matches[1];
|
|
const startColor = matches[2];
|
|
const startPercentage = matches[3];
|
|
const endColor = matches[4];
|
|
const endPercentage = matches[5];
|
|
|
|
// 确定渐变方向和坐标范围
|
|
let x1, y1, x2, y2;
|
|
|
|
if (direction.includes("deg")) {
|
|
const degrees = parseFloat(direction);
|
|
const radians = (degrees * Math.PI) / 180;
|
|
|
|
x1 = Math.cos(radians).toFixed(2);
|
|
y1 = Math.sin(radians).toFixed(2);
|
|
x2 = Math.cos(radians + Math.PI).toFixed(2);
|
|
y2 = Math.sin(radians + Math.PI).toFixed(2);
|
|
} else {
|
|
// 处理其他方向信息(例如 to top, to bottom, to left, to right 等)
|
|
// 根据具体需求进行处理
|
|
return null;
|
|
}
|
|
|
|
// 渐变元素
|
|
const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
|
|
const linearGradient = document.createElementNS("http://www.w3.org/2000/svg", "linearGradient");
|
|
|
|
// 设置渐变元素属性值
|
|
linearGradient.setAttribute("id", gradientId);
|
|
linearGradient.setAttribute("x1", x1);
|
|
linearGradient.setAttribute("y1", y1);
|
|
linearGradient.setAttribute("x2", x2);
|
|
linearGradient.setAttribute("y2", y2);
|
|
|
|
// 创建 stop 元素并设置属性值
|
|
const startStop = document.createElementNS("http://www.w3.org/2000/svg", "stop");
|
|
startStop.setAttribute("offset", startPercentage);
|
|
startStop.style.stopColor = startColor;
|
|
|
|
const endStop = document.createElementNS("http://www.w3.org/2000/svg", "stop");
|
|
endStop.setAttribute("offset", endPercentage);
|
|
endStop.style.stopColor = endColor;
|
|
|
|
// 将 stop 元素添加到渐变元素中
|
|
linearGradient.appendChild(startStop);
|
|
linearGradient.appendChild(endStop);
|
|
|
|
// 将渐变元素添加到 SVG 元素中
|
|
defs.appendChild(linearGradient);
|
|
|
|
// 返回生成的 渐变标签
|
|
return defs;
|
|
}
|
|
|
|
// 登录获取唯一标识
|
|
const getUuid = () => {
|
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
const r = (Math.random() * 16) | 0,
|
|
v = c == "x" ? r : (r & 0x3) | 0x8;
|
|
return v.toString(16);
|
|
});
|
|
};
|
|
|
|
// 创建登录弹框
|
|
window.createLoginDialog = function () {
|
|
const { createApp, createVNode, render } = Vue;
|
|
const app = createApp({})
|
|
const Login = {
|
|
template: `<div style="position: absolute;top: 50%;left: 50%;margin-top: -178px;margin-left: -170px;z-index: 1000;background-color: #ffffff;">
|
|
<div class="layui-field-box">
|
|
<div class="layui-form-item logo-title">
|
|
<h1 style="color: #1E9FFF; font-size: 20px;text-align: center">登录系统<span style="float: right; cursor: pointer" @click="remove"><i class="layui-icon layui-icon-close"></i></span></h1>
|
|
</div>
|
|
<form class="layui-form layui-form-pane">
|
|
<div class="layui-form-item">
|
|
<label class="layui-form-label">用户名</label>
|
|
<div class="layui-input-inline">
|
|
<input v-model="username" type="text" name="username" lay-verify="required" placeholder="请输入用户名" autocomplete="off" class="layui-input">
|
|
</div>
|
|
</div>
|
|
<div class="layui-form-item">
|
|
<label class="layui-form-label">密码</label>
|
|
<div class="layui-input-inline">
|
|
<input v-model="password" type="password" name="password" lay-verify="required" placeholder="请输入密码" autocomplete="off" class="layui-input">
|
|
</div>
|
|
</div>
|
|
<div class="layui-form-item">
|
|
<button class="layui-btn layui-btn-normal layui-btn-fluid" @click="loginHandler">立即登录</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>`,
|
|
props: {
|
|
|
|
},
|
|
emits: ["loginFinished"],
|
|
setup(props, { emit }) {
|
|
const { ref, watch } = Vue
|
|
const username = ref('')
|
|
const password = ref('')
|
|
|
|
const loginHandler = (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
if (!username || !password) {
|
|
return;
|
|
}
|
|
const loadIdx = window.layer.load(1, {
|
|
shade: [0.2, '#000'],
|
|
})
|
|
window.service.post("/login/noCaptcha", {
|
|
username: username.value,
|
|
password: password.value,
|
|
uuid: getUuid(),
|
|
captcha: "11"
|
|
})
|
|
.then((res) => {
|
|
if (res.code !== 0) {
|
|
window.layer.close(loadIdx);
|
|
window.layui.use('layer', function(){
|
|
const layer = layui.layer;
|
|
layer.msg(res.msg);
|
|
});
|
|
return;
|
|
}
|
|
sessionStorage.setItem('v1@CacheToken', JSON.stringify(res.data), true);
|
|
emit("loginFinished");
|
|
window.layer.close(loadIdx);
|
|
setTimeout(() => {
|
|
window.location.reload();
|
|
}, 500)
|
|
})
|
|
.catch(() => {
|
|
|
|
});
|
|
}
|
|
|
|
function remove () {
|
|
const loginDom = document.getElementById('loginModal');
|
|
loginDom && document.body.removeChild(loginDom);
|
|
}
|
|
|
|
return {
|
|
username,
|
|
password,
|
|
loginHandler,
|
|
remove,
|
|
}
|
|
}
|
|
}
|
|
|
|
const el = document.createElement('div');
|
|
el.id = 'loginModal';
|
|
el.style = `position: absolute; top:0; bottom: 0; width: 100%; height: 100%; z-index: 990;background-color: rgba(0,0,0,0.8);`
|
|
const loginHandler = () => {
|
|
document.body.removeChild(el);
|
|
}
|
|
const loginDom = document.getElementById('loginModal');
|
|
if (!loginDom) {
|
|
const instance = createVNode(Login, {
|
|
onLoginFinished: loginHandler
|
|
})
|
|
instance.appContext = app._context
|
|
render(instance, el);
|
|
document.body.appendChild(el);
|
|
}
|
|
}
|