物管理前端
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

  1. window.isJSON = function (str) {
  2. if (typeof str != 'string') { // 1、传入值必须是 字符串
  3. return false;
  4. }
  5. try {
  6. var obj = JSON.parse(str); // 2、仅仅通过 JSON.parse(str),不能完全检验一个字符串是JSON格式的字符串
  7. if (typeof obj == 'object' && obj) { //3、还必须是 object 类型
  8. return true;
  9. } else {
  10. return false;
  11. }
  12. } catch (e) {
  13. return false;
  14. }
  15. return false;
  16. }
  17. // 解析最新值
  18. window.resolveScadaNewValue = (defaultValue) => {
  19. if (defaultValue) {
  20. let valueParsed = [{
  21. val: ''
  22. }];
  23. let realValue = ''
  24. try {
  25. valueParsed = JSON.parse(defaultValue) || [{ val: '' }];
  26. if (valueParsed[0]) {
  27. realValue = valueParsed[0].val;
  28. } else {
  29. realValue = valueParsed[0] === undefined ? '' : valueParsed[0]
  30. }
  31. } catch (err) {
  32. console.log('err', err)
  33. }
  34. return realValue;
  35. } else {
  36. return ''
  37. }
  38. }
  39. // 字体颜色动态改变
  40. window.changeFontColor = (style, properties) => {
  41. const { uiData } = properties.dynamic || {};
  42. if (uiData) {
  43. const realValue = window.resolveScadaNewValue(uiData.defaultValue)
  44. if (realValue !== '') {
  45. uiData.conditionVariables.forEach((item) => {
  46. if (item.type === 'rangeColor') {
  47. let from = item.from;
  48. let to = item.to;
  49. if (item.from >= item.to) {
  50. from = item.to;
  51. to = item.from;
  52. }
  53. if (item.fontColor && Number(realValue) >= from && Number(realValue) <= to) {
  54. style.color = item.fontColor
  55. }
  56. } else if (item.type === 'equal') {
  57. if (Number(realValue) === Number(item.value)) {
  58. item.fontColor && (style.color = item.fontColor);
  59. }
  60. } else if (item.type === 'boolean') {
  61. if (realValue === 'true' || realValue === true) {
  62. item.fontColor && (style.color = item.backColor);
  63. }
  64. }
  65. })
  66. }
  67. }
  68. }
  69. // 背景颜色改变
  70. window.changeBackgroundColor = (style, properties) => {
  71. const { uiData } = properties.dynamic || {};
  72. if (uiData) {
  73. const realValue = window.resolveScadaNewValue(uiData.defaultValue)
  74. if (realValue !== '') {
  75. uiData.conditionVariables.forEach((item) => {
  76. if (item.type === 'rangeColor') {
  77. let from = item.from;
  78. let to = item.to;
  79. if (item.from >= item.to) {
  80. from = item.to;
  81. to = item.from;
  82. }
  83. if (item.backColor && Number(realValue) >= from && Number(realValue) <= to) {
  84. style.fill = item.backColor
  85. }
  86. } else if (item.type === 'equal') {
  87. if (Number(realValue) === Number(item.value)) {
  88. item.backColor && (style.fill = item.backColor);
  89. }
  90. } else if (item.type === 'boolean') {
  91. if (realValue === 'true' || realValue === true) {
  92. item.backColor && (style.fill = item.backColor);
  93. }
  94. }
  95. })
  96. }
  97. }
  98. }
  99. // 创建 svg 图案
  100. window.createSvgPattern = (id, href, width, height) => {
  101. const defsId = 'defs-' + id;
  102. const patternId = 'pattern-' + id;
  103. const oldDefs = document.getElementById(defsId);
  104. const oldPattern = document.getElementById(patternId);
  105. if (oldDefs) {
  106. oldDefs.remove();
  107. }
  108. if (oldPattern) {
  109. oldPattern.remove();
  110. }
  111. const defs = document.createElement('defs');
  112. defs.setAttribute('id', 'defs-' + id);
  113. const pattern = document.createElement('pattern');
  114. pattern.setAttribute('id', 'pattern-' + id);
  115. pattern.setAttribute('width', '1');
  116. pattern.setAttribute('height', '1');
  117. pattern.setAttribute('patternUnits', 'objectBoundingBox');
  118. const img = document.createElement('image');
  119. img.setAttribute('width', width);
  120. img.setAttribute('height', height);
  121. img.setAttribute("xlink:href", href);
  122. pattern.appendChild(img);
  123. defs.append(pattern);
  124. const canvasOverlay = document.querySelector('.lf-canvas-overlay')
  125. canvasOverlay.prepend(defs);
  126. }
  127. var messageFn = (msg) => {
  128. layui.use('layer', function () {
  129. const layer = layui.layer;
  130. layer.msg(msg);
  131. });
  132. }
  133. window.totalListeners = {
  134. 'click': [],
  135. 'dbclick': [],
  136. 'mouseenter': [],
  137. 'mouseleave': []
  138. }
  139. // 没有明确点击下发指令的部件,像普通的图形或图片也有可能会下发指令,因此也需要绑定好事件监听。
  140. window.nodeEventsListeners = (self) => {
  141. const clickHandler = function ({ data }) {
  142. if (data.id !== this.id) return
  143. this.graphModel.eventCenter.emit('myNode:click', { data: this, e: this.realValue })
  144. };
  145. const clickHandlerBound = clickHandler.bind(self)
  146. window.totalListeners.click.push(clickHandlerBound)
  147. const dbclickHandler = function ({ data }) {
  148. if (data.id !== this.id) return
  149. this.graphModel.eventCenter.emit('myNode:dbclick', { data: this, e: this.realValue })
  150. }
  151. const dbclickHandlerBound = dbclickHandler.bind(self)
  152. window.totalListeners.dbclick.push(dbclickHandlerBound)
  153. const mouseEnterHandler = function ({ data }) {
  154. if (data.id !== this.id) return
  155. this.graphModel.eventCenter.emit('myNode:mouseenter', { data: this, e: this.realValue })
  156. }
  157. const mouseEnterHandlerBound = mouseEnterHandler.bind(self)
  158. window.totalListeners.mouseenter.push(mouseEnterHandlerBound)
  159. const mouseLeaveHandler = function ({ data }) {
  160. if (data.id !== this.id) return
  161. this.graphModel.eventCenter.emit('myNode:mouseleave', { data: this, e: this.realValue })
  162. }
  163. const mouseLeaveHandlerBound = mouseLeaveHandler.bind(self)
  164. window.totalListeners.mouseleave.push(mouseLeaveHandlerBound)
  165. }
  166. // 判断矩形是否相交
  167. function calcBoundingBox(boundingBox) {
  168. const boundingBoxLeft = boundingBox.x
  169. const boundingBoxRight = boundingBox.x + boundingBox.width || boundingBox.properties.width
  170. const boundingBoxTop = boundingBox.y
  171. const boundingBoxBottom = boundingBox.y + boundingBox.height || boundingBox.properties.height
  172. return {
  173. boundingBoxLeft,
  174. boundingBoxRight,
  175. boundingBoxTop,
  176. boundingBoxBottom,
  177. }
  178. }
  179. function getEdgeWidthHeight(edge) {
  180. if (!edge)
  181. return
  182. let edgeStartPoint = {
  183. x: 0,
  184. y: 0,
  185. }
  186. let edgeEndPoint = {
  187. x: 0,
  188. y: 0,
  189. }
  190. if (edge.modelType === 'line-edge') {
  191. edgeStartPoint = edge.startPoint
  192. edgeEndPoint = edge.endPoint
  193. }
  194. else {
  195. edgeStartPoint = edge.pointsList[0]
  196. edgeEndPoint = edge.pointsList[edge.pointsList.length - 1]
  197. }
  198. const width = Math.abs(edgeEndPoint.x - edgeStartPoint.x)
  199. const height = Math.abs(edgeEndPoint.y - edgeStartPoint.y)
  200. return {
  201. x: edgeStartPoint.x < edgeEndPoint.x ? edgeStartPoint.x : edgeEndPoint.x,
  202. y: edgeStartPoint.y < edgeEndPoint.y ? edgeStartPoint.y : edgeEndPoint.y,
  203. width,
  204. height,
  205. }
  206. }
  207. // 应该取名为是否相交(图层上移下移用到),为了避免影响原来的代码,就叫这个吧。
  208. const isInsideBoundingBox = function (elementBox, boundingBox) {
  209. const elementLeft = elementBox.x
  210. const elementRight = elementBox.x + elementBox.width
  211. const elementTop = elementBox.y
  212. const elementBottom = elementBox.y + elementBox.height
  213. const { boundingBoxLeft, boundingBoxRight, boundingBoxBottom, boundingBoxTop } = calcBoundingBox(boundingBox)
  214. if (
  215. elementRight < boundingBoxLeft
  216. || elementLeft > boundingBoxRight
  217. || elementTop > boundingBoxBottom
  218. || elementBottom < boundingBoxTop
  219. )
  220. return false // 两个矩形不相交
  221. return true // 两个矩形相交
  222. }
  223. const isRealInsideBoundingBox = function (elementBox, boundingBox) {
  224. const elementLeft = elementBox.x
  225. const elementRight = elementBox.x + elementBox.width
  226. const elementTop = elementBox.y
  227. const elementBottom = elementBox.y + elementBox.height
  228. const { boundingBoxLeft, boundingBoxRight, boundingBoxBottom, boundingBoxTop } = calcBoundingBox(boundingBox)
  229. if (
  230. (elementLeft >= boundingBoxLeft && elementLeft < boundingBoxRight)
  231. && (elementRight <= boundingBoxRight && elementRight > boundingBoxLeft)
  232. && (elementTop >= boundingBoxTop && elementTop < boundingBoxBottom)
  233. && (elementBottom <= boundingBoxBottom && elementBottom > boundingBoxTop)
  234. )
  235. return true // 一个矩形在另一个矩形之内
  236. return false
  237. }
  238. const getMaxBoundingBox = function (selects) {
  239. const { nodes, edges } = selects
  240. let maxBoundingNode = nodes[0]
  241. let maxBoundingEdge = getEdgeWidthHeight(edges[0])
  242. if (nodes.length > 0) {
  243. nodes.forEach((node, index) => {
  244. if (node.type === 'helper-circle-point')
  245. return
  246. const area = node.properties.width * node.properties.height
  247. const oldArea = maxBoundingNode.properties.width * maxBoundingNode.properties.height
  248. if (area > oldArea)
  249. maxBoundingNode = node
  250. })
  251. }
  252. if (maxBoundingNode) {
  253. const w = maxBoundingNode.width || maxBoundingNode.properties.width
  254. const h = maxBoundingNode.height || maxBoundingNode.properties.height
  255. maxBoundingNode = {
  256. x: maxBoundingNode.x - w / 2,
  257. y: maxBoundingNode.y - h / 2,
  258. width: w,
  259. height: h,
  260. }
  261. }
  262. if (edges.length > 0) {
  263. edges.forEach((edge, index) => {
  264. const edgeWidthHeight = getEdgeWidthHeight(edge)
  265. if (edgeWidthHeight) {
  266. const { width, height } = edgeWidthHeight
  267. const area = width * height
  268. if (maxBoundingEdge) {
  269. const { width: oldWidth, height: oldHeight } = maxBoundingEdge
  270. const oldArea = oldWidth * oldHeight
  271. if (area > oldArea)
  272. maxBoundingEdge = edge
  273. }
  274. }
  275. })
  276. }
  277. if (maxBoundingNode && !maxBoundingEdge) {
  278. return maxBoundingNode
  279. }
  280. else if (!maxBoundingNode && maxBoundingEdge) {
  281. return maxBoundingEdge
  282. }
  283. else if (maxBoundingNode && maxBoundingEdge) {
  284. if ((maxBoundingNode.width * maxBoundingNode.height) > (maxBoundingEdge.width * maxBoundingEdge.height))
  285. return maxBoundingNode
  286. else
  287. return maxBoundingEdge
  288. }
  289. }
  290. // 计算某个元素内部的所有节点元素(不包括连线)
  291. window.calcInsideAreaElements = (lf, selects) => {
  292. // 矩形相交的元素
  293. let areaElements = []
  294. // 获取最大的 boundingBox
  295. const maxBounding = getMaxBoundingBox(selects)
  296. if (!maxBounding)
  297. return
  298. lf.graphModel.nodes.forEach((node) => {
  299. if (node.type === 'helper-circle-point')
  300. return
  301. const ABox = {
  302. x: node.x - node.width / 2,
  303. y: node.y - node.height / 2,
  304. width: node.width,
  305. height: node.height,
  306. }
  307. const isIn = isRealInsideBoundingBox(ABox, maxBounding)
  308. if (isIn) {
  309. areaElements.push(node)
  310. }
  311. })
  312. return areaElements
  313. }
  314. // 数据点绑定时候的 数据处理函数, 全局的。
  315. window.dataProcessFn = function (event) {
  316. const { info, apiid, defaultValueIndex, values, deviceIdCodeMap } = event;
  317. const {
  318. context, service, nodeId, dataPointStr, dynamicFlag,
  319. dataSource, deviceCode, devices, enableDataHandle, deviceAttrs, calcRules, uniquePoint, dataPoint, attrs, callBacks,
  320. } = info
  321. // console.log('nodeId', nodeId);
  322. // console.log('开始数据处理了', deviceIdCodeMap)
  323. const totalValues = []
  324. if (calcRules && calcRules.length > 0) {
  325. if (deviceAttrs && deviceAttrs.table && deviceAttrs.table.length > 0) {
  326. const newDeviceTable = []
  327. const valuesGroup = {}
  328. const codeAttrToNumMap = {}
  329. const nums = deviceAttrs.table.map(i => {
  330. const code = deviceIdCodeMap[i.devices]
  331. valuesGroup[i.num] = []
  332. codeAttrToNumMap[code + '-' + i.dataPoint] = i.num
  333. newDeviceTable.push({
  334. ...i,
  335. deviceCode: code
  336. })
  337. return i.num
  338. })
  339. // 根据 物+属性 聚合分类
  340. values.forEach((val) => {
  341. const findNum = codeAttrToNumMap[val.thingCode + '-' + val.attrKey]
  342. if (findNum) {
  343. valuesGroup[findNum].push({ ...val })
  344. }
  345. })
  346. calcRules.forEach((rule) => {
  347. const findNum = newDeviceTable.find(d => d.num === rule.resultAttr)
  348. if (findNum) {
  349. // 计算结果属性为已经存在的序号
  350. valuesGroup[findNum.num].forEach((val, index) => {
  351. let newFormular = rule.formular
  352. nums.forEach(n => {
  353. if (newFormular.includes(n)) {
  354. const otherValue = valuesGroup[n][index].val
  355. newFormular = newFormular.replace(n, otherValue)
  356. }
  357. })
  358. const fn = new Function('', `return ${newFormular}`)
  359. let newValue = fn()
  360. val.val = newValue
  361. })
  362. } else {
  363. // 创建新的属性
  364. if (nums.length > 0) {
  365. // 默认使用第一组的数据长度做为新的数据长度
  366. const newThingAndAttr = valuesGroup[nums[0]].map((val, index) => {
  367. let newFormular = rule.formular
  368. nums.forEach(n => {
  369. if (newFormular.includes(n)) {
  370. const otherValue = valuesGroup[n][index].val
  371. newFormular = newFormular.replace(n, otherValue)
  372. }
  373. })
  374. let newValue = eval(newFormular)
  375. return {
  376. thingCode: rule.resultAttr,
  377. attrKey: rule.resultAttr,
  378. ts: val.ts,
  379. val: newValue
  380. }
  381. })
  382. valuesGroup[rule.resultAttr] = newThingAndAttr
  383. }
  384. }
  385. })
  386. // console.log('valuesGroup', valuesGroup);
  387. if (uniquePoint && nums.includes(uniquePoint)) {
  388. totalValues.push(...valuesGroup[uniquePoint])
  389. } else {
  390. Object.values(valuesGroup).forEach(valGroup => {
  391. totalValues.push(...valGroup)
  392. })
  393. }
  394. // console.log('totalValues', totalValues)
  395. let dataPointValue = JSON.stringify(totalValues)
  396. return dataPointValue
  397. }
  398. }
  399. }
  400. // 寻找最低采集频率
  401. window.filterMinimumFrequency = (info, historyDatas) => {
  402. let totals = [];
  403. const thingGrouped = window._.groupBy(historyDatas, 'thingCode');
  404. for (const thingKey in thingGrouped) {
  405. const attrGrouped = window._.groupBy(thingGrouped[thingKey], 'attrKey')
  406. Object.keys(attrGrouped).forEach((attrkey) => {
  407. totals.push(info[thingKey].attrs[attrkey].rate);
  408. })
  409. }
  410. const filteredTotals = totals.filter(Boolean)
  411. if (filteredTotals.length === 0) {
  412. return 0
  413. } else {
  414. return Math.min(...filteredTotals)
  415. }
  416. }
  417. // 柱状图和折线图的完整时间(数据补全用到)
  418. window.completeTimesForChart = function (apiDetails, info, historyDatas, apiid) {
  419. if (!apiDetails) return;
  420. // 首先确定开始和结束时间
  421. let startTime = null
  422. let endTime = null
  423. if (apiDetails.type === 'range') {
  424. startTime = apiDetails.startTime
  425. endTime = apiDetails.endTime
  426. } else if (apiDetails.type === 'nearest') {
  427. const { day, hour, minute } = apiDetails.nearest
  428. endTime = window.dayjs()
  429. startTime = endTime.subtract(day, 'days').subtract(hour, 'hours').subtract(minute, 'minutes')
  430. } else if (apiDetails.type === 'interval') {
  431. const { duration, type } = apiDetails.interval
  432. if (type === 'day') {
  433. endTime = window.dayjs().endOf('day')
  434. startTime = endTime.subtract(duration, 'day').add(1, 'second')
  435. } else if (type === 'week') {
  436. endTime = window.dayjs().endOf('week')
  437. startTime = endTime.subtract(duration, 'week').add(1, 'second')
  438. } else if (type === 'month') {
  439. endTime = window.dayjs().endOf('month');
  440. const lastDayofMonth = endTime.subtract(duration, 'month');
  441. startTime = lastDayofMonth.endOf('month').add(1, 'second');
  442. } else if (type === 'year') {
  443. endTime = window.dayjs().endOf('year')
  444. startTime = endTime.subtract(duration, 'year').add(1, 'second')
  445. }
  446. }
  447. if (startTime && endTime) {
  448. const totalTimes = [dayjs(startTime).millisecond(0).valueOf()];
  449. const { day, hour, minute } = apiDetails.agg;
  450. let newEndTime = null;
  451. if (day) {
  452. newEndTime = dayjs(startTime).add(day, 'days').millisecond(0).valueOf();
  453. totalTimes.push(newEndTime);
  454. while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  455. newEndTime = dayjs(newEndTime).add(day, 'days').millisecond(0).valueOf();
  456. if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  457. totalTimes.push(newEndTime);
  458. }
  459. }
  460. return totalTimes;
  461. } else if (hour) {
  462. newEndTime = dayjs(startTime).add(hour, 'hours').millisecond(0).valueOf();
  463. totalTimes.push(newEndTime);
  464. while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  465. newEndTime = dayjs(newEndTime).add(hour, 'hours').millisecond(0).valueOf();
  466. if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  467. totalTimes.push(newEndTime);
  468. }
  469. }
  470. return totalTimes;
  471. } else if (minute) {
  472. newEndTime = dayjs(startTime).add(minute, 'minutes').millisecond(0).valueOf();
  473. totalTimes.push(newEndTime);
  474. while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  475. newEndTime = dayjs(newEndTime).add(minute, 'minutes').millisecond(0).valueOf();
  476. if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  477. totalTimes.push(newEndTime);
  478. }
  479. }
  480. return totalTimes;
  481. } else if (apiDetails.type === 'interval' && !apiDetails.func && !day && !hour && !minute) {
  482. const { duration, type } = apiDetails.interval
  483. const min = window.filterMinimumFrequency(info, historyDatas);
  484. if (!min) {
  485. if (type === 'day') {
  486. newEndTime = dayjs(startTime).add(1, 'hours').millisecond(0).valueOf();
  487. totalTimes.push(newEndTime);
  488. while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  489. newEndTime = dayjs(newEndTime).add(1, 'hours').millisecond(0).valueOf();
  490. if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  491. totalTimes.push(newEndTime);
  492. }
  493. }
  494. } else if (type === 'week') {
  495. newEndTime = dayjs(startTime).add(1, 'days').millisecond(0).valueOf();
  496. totalTimes.push(newEndTime);
  497. while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  498. newEndTime = dayjs(newEndTime).add(1, 'days').millisecond(0).valueOf();
  499. if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  500. totalTimes.push(newEndTime);
  501. }
  502. }
  503. } else if (type === 'month') {
  504. newEndTime = dayjs(startTime).add(1, 'days').millisecond(0).valueOf();
  505. totalTimes.push(newEndTime);
  506. while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  507. newEndTime = dayjs(newEndTime).add(1, 'days').millisecond(0).valueOf();
  508. if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  509. totalTimes.push(newEndTime);
  510. }
  511. }
  512. } else if (type === 'year') {
  513. newEndTime = dayjs(startTime).add(1, 'months').millisecond(0).valueOf();
  514. totalTimes.push(newEndTime);
  515. while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  516. newEndTime = dayjs(newEndTime).add(1, 'months').millisecond(0).valueOf();
  517. if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  518. totalTimes.push(newEndTime);
  519. }
  520. }
  521. }
  522. } else {
  523. newEndTime = dayjs(startTime).add(min, 'seconds').millisecond(0).valueOf();
  524. totalTimes.push(newEndTime);
  525. while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  526. newEndTime = dayjs(newEndTime).add(min, 'seconds').millisecond(0).valueOf();
  527. if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  528. totalTimes.push(newEndTime);
  529. }
  530. }
  531. }
  532. return totalTimes;
  533. } else if (apiDetails.type !== 'interval' && !apiDetails.func && !day && !hour && !minute) {
  534. const days = startTime.diff(endTime, 'day');
  535. const months = startTime.diff(endTime, 'month');
  536. const min = window.filterMinimumFrequency(info, historyDatas);
  537. if (!min) {
  538. if (months === 0 && days === 0) {
  539. newEndTime = dayjs(startTime).add(1, 'hours').millisecond(0).valueOf();
  540. totalTimes.push(newEndTime);
  541. while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  542. newEndTime = dayjs(newEndTime).add(1, 'hours').millisecond(0).valueOf();
  543. if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  544. totalTimes.push(newEndTime);
  545. }
  546. }
  547. } else if (months === 0 && days > 0) {
  548. newEndTime = dayjs(startTime).add(1, 'days').millisecond(0).valueOf();
  549. totalTimes.push(newEndTime);
  550. while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  551. newEndTime = dayjs(newEndTime).add(1, 'days').millisecond(0).valueOf();
  552. if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  553. totalTimes.push(newEndTime);
  554. }
  555. }
  556. } else if (months > 0 && days >= 0) {
  557. newEndTime = dayjs(startTime).add(1, 'months').millisecond(0).valueOf();
  558. totalTimes.push(newEndTime);
  559. while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  560. newEndTime = dayjs(newEndTime).add(1, 'months').millisecond(0).valueOf();
  561. if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  562. totalTimes.push(newEndTime);
  563. }
  564. }
  565. }
  566. } else {
  567. newEndTime = dayjs(startTime).add(min, 'seconds').millisecond(0).valueOf();
  568. totalTimes.push(newEndTime);
  569. while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  570. newEndTime = dayjs(newEndTime).add(min, 'seconds').millisecond(0).valueOf();
  571. if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  572. totalTimes.push(newEndTime);
  573. }
  574. }
  575. }
  576. return totalTimes;
  577. } else if (day && hour && minute) {
  578. newEndTime = dayjs(startTime).add(day, 'days').add(hour, 'hours').add(minute, 'minutes').millisecond(0).valueOf();
  579. totalTimes.push(newEndTime);
  580. while (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  581. newEndTime = dayjs(newEndTime).add(day, 'days').add(hour, 'hours').add(minute, 'minutes').millisecond(0).valueOf();
  582. if (dayjs(newEndTime).isBefore(dayjs(endTime))) {
  583. totalTimes.push(newEndTime);
  584. }
  585. }
  586. return totalTimes;
  587. }
  588. }
  589. }
  590. // 柱状图/折线图时间分割,今日-昨日,当月-上月,今年-去年
  591. window.splitTimes = function (totalTimes, timeCompare) {
  592. const prev = []
  593. const curr = []
  594. if (timeCompare === 'day') {
  595. totalTimes.forEach((t) => {
  596. if (dayjs(t).isBefore(dayjs().startOf('day'))) {
  597. prev.push(t)
  598. } else {
  599. curr.push(t)
  600. }
  601. })
  602. } else if (timeCompare === 'month') {
  603. totalTimes.forEach((t) => {
  604. if (dayjs(t).month() < dayjs().month()) {
  605. prev.push(t)
  606. } else {
  607. curr.push(t)
  608. }
  609. })
  610. } else if (timeCompare === 'year') {
  611. totalTimes.forEach((t) => {
  612. if (dayjs(t).year() < dayjs().year()) {
  613. prev.push(t)
  614. } else {
  615. curr.push(t)
  616. }
  617. })
  618. }
  619. return {
  620. prev, curr
  621. }
  622. }
  623. // 把 css 渐变颜色转换成 svg 渐变标签
  624. window.generateSVGGradient = function (gradientString, gradientId) {
  625. // 解析渐变颜色字符串
  626. // 解析渐变颜色字符串
  627. const regex = /linear-gradient\((.+?),(.+?)\s([0-9]+%)\s?,\s(.+?)\s([0-9]+%)\)/;
  628. const matches = gradientString.match(regex);
  629. if (!matches || matches.length < 6) {
  630. return null;
  631. }
  632. const direction = matches[1];
  633. const startColor = matches[2];
  634. const startPercentage = matches[3];
  635. const endColor = matches[4];
  636. const endPercentage = matches[5];
  637. // 确定渐变方向和坐标范围
  638. let x1, y1, x2, y2;
  639. if (direction.includes("deg")) {
  640. const degrees = parseFloat(direction);
  641. const radians = (degrees * Math.PI) / 180;
  642. x1 = Math.cos(radians).toFixed(2);
  643. y1 = Math.sin(radians).toFixed(2);
  644. x2 = Math.cos(radians + Math.PI).toFixed(2);
  645. y2 = Math.sin(radians + Math.PI).toFixed(2);
  646. } else {
  647. // 处理其他方向信息(例如 to top, to bottom, to left, to right 等)
  648. // 根据具体需求进行处理
  649. return null;
  650. }
  651. // 渐变元素
  652. const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
  653. const linearGradient = document.createElementNS("http://www.w3.org/2000/svg", "linearGradient");
  654. // 设置渐变元素属性值
  655. linearGradient.setAttribute("id", gradientId);
  656. linearGradient.setAttribute("x1", x1);
  657. linearGradient.setAttribute("y1", y1);
  658. linearGradient.setAttribute("x2", x2);
  659. linearGradient.setAttribute("y2", y2);
  660. // 创建 stop 元素并设置属性值
  661. const startStop = document.createElementNS("http://www.w3.org/2000/svg", "stop");
  662. startStop.setAttribute("offset", startPercentage);
  663. startStop.style.stopColor = startColor;
  664. const endStop = document.createElementNS("http://www.w3.org/2000/svg", "stop");
  665. endStop.setAttribute("offset", endPercentage);
  666. endStop.style.stopColor = endColor;
  667. // 将 stop 元素添加到渐变元素中
  668. linearGradient.appendChild(startStop);
  669. linearGradient.appendChild(endStop);
  670. // 将渐变元素添加到 SVG 元素中
  671. defs.appendChild(linearGradient);
  672. // 返回生成的 渐变标签
  673. return defs;
  674. }
  675. // 登录获取唯一标识
  676. const getUuid = () => {
  677. return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
  678. const r = (Math.random() * 16) | 0,
  679. v = c == "x" ? r : (r & 0x3) | 0x8;
  680. return v.toString(16);
  681. });
  682. };
  683. // 创建登录弹框
  684. window.createLoginDialog = function () {
  685. const { createApp, createVNode, render } = Vue;
  686. const app = createApp({})
  687. const Login = {
  688. template: `<div style="position: absolute;top: 50%;left: 50%;margin-top: -178px;margin-left: -170px;z-index: 1000;background-color: #ffffff;">
  689. <div class="layui-field-box">
  690. <div class="layui-form-item logo-title">
  691. <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>
  692. </div>
  693. <form class="layui-form layui-form-pane">
  694. <div class="layui-form-item">
  695. <label class="layui-form-label">用户名</label>
  696. <div class="layui-input-inline">
  697. <input v-model="username" type="text" name="username" lay-verify="required" placeholder="请输入用户名" autocomplete="off" class="layui-input">
  698. </div>
  699. </div>
  700. <div class="layui-form-item">
  701. <label class="layui-form-label">密码</label>
  702. <div class="layui-input-inline">
  703. <input v-model="password" type="password" name="password" lay-verify="required" placeholder="请输入密码" autocomplete="off" class="layui-input">
  704. </div>
  705. </div>
  706. <div class="layui-form-item">
  707. <button class="layui-btn layui-btn-normal layui-btn-fluid" @click="loginHandler">立即登录</button>
  708. </div>
  709. </form>
  710. </div>
  711. </div>`,
  712. props: {
  713. },
  714. emits: ["loginFinished"],
  715. setup(props, { emit }) {
  716. const { ref, watch } = Vue
  717. const username = ref('')
  718. const password = ref('')
  719. const loginHandler = (e) => {
  720. e.preventDefault();
  721. e.stopPropagation();
  722. if (!username || !password) {
  723. return;
  724. }
  725. const loadIdx = window.layer.load(1, {
  726. shade: [0.2, '#000'],
  727. })
  728. window.service.post("/login/noCaptcha", {
  729. username: username.value,
  730. password: password.value,
  731. uuid: getUuid(),
  732. captcha: "11"
  733. })
  734. .then((res) => {
  735. if (res.code !== 0) {
  736. window.layer.close(loadIdx);
  737. window.layui.use('layer', function(){
  738. const layer = layui.layer;
  739. layer.msg(res.msg);
  740. });
  741. return;
  742. }
  743. sessionStorage.setItem('v1@CacheToken', JSON.stringify(res.data), true);
  744. emit("loginFinished");
  745. window.layer.close(loadIdx);
  746. setTimeout(() => {
  747. window.location.reload();
  748. }, 500)
  749. })
  750. .catch(() => {
  751. });
  752. }
  753. function remove () {
  754. const loginDom = document.getElementById('loginModal');
  755. loginDom && document.body.removeChild(loginDom);
  756. }
  757. return {
  758. username,
  759. password,
  760. loginHandler,
  761. remove,
  762. }
  763. }
  764. }
  765. const el = document.createElement('div');
  766. el.id = 'loginModal';
  767. el.style = `position: absolute; top:0; bottom: 0; width: 100%; height: 100%; z-index: 990;background-color: rgba(0,0,0,0.8);`
  768. const loginHandler = () => {
  769. document.body.removeChild(el);
  770. }
  771. const loginDom = document.getElementById('loginModal');
  772. if (!loginDom) {
  773. const instance = createVNode(Login, {
  774. onLoginFinished: loginHandler
  775. })
  776. instance.appContext = app._context
  777. render(instance, el);
  778. document.body.appendChild(el);
  779. }
  780. }