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

381 lines
16 KiB

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <title></title>
  8. <style>
  9. html,
  10. body {
  11. height: 100%;
  12. margin: 0;
  13. padding: 0;
  14. }
  15. body.overflow-hidden {
  16. overflow: hidden;
  17. }
  18. #diagram {
  19. height: 100%;
  20. overflow: hidden;
  21. }
  22. @keyframes blink {
  23. 0% {
  24. transform: scale(0.2);
  25. opacity: 1;
  26. }
  27. to {
  28. transform: scale(1.5);
  29. opacity: 0.1;
  30. }
  31. }
  32. .blinkAnim {
  33. animation: blink 1.5s ease-in-out infinite;
  34. }
  35. </style>
  36. <link rel="stylesheet" href="./logicflow/core.css" />
  37. <link rel="stylesheet" href="./logicflow/extension.css" />
  38. <link rel="stylesheet" href="./logicflow/extensionLib/style/index.css" />
  39. <link rel="stylesheet" href="./iconfonts/iconfont.css">
  40. <link rel="stylesheet" href="./plugins/layui/css/layui.css" />
  41. <link id="layui_theme_css" rel="stylesheet" />
  42. </head>
  43. <body>
  44. <div id="diagram">
  45. </div>
  46. <script src="./plugins/vue.min.js"></script>
  47. <script src="./plugins/axios.min.js"></script>
  48. <script src="./plugins/service.js"></script>
  49. <script src="./plugins/registerRemoteComponents.js"></script>
  50. <script src="./logicflow/core.umd.js"></script>
  51. <script src="./logicflow/extensionLib/NodeResize.js"></script>
  52. <script src="./logicflow/extensionLib/CurvedEdge.js"></script>
  53. <script src="./logicflow/extensionLib/Group.js"></script>
  54. <script src="./plugins/layui/layui.js"></script>
  55. <script src="./plugins/lodash.js"></script>
  56. <script src="./plugins/myDebounce.js"></script>
  57. <script src="./plugins/utils.js"></script>
  58. <script src="./plugins/dynamicEventsHandler.js"></script>
  59. <script src="./plugins/dynamicAnimationHandler.js"></script>
  60. <script src="./plugins/dynamicShowHideHandler.js"></script>
  61. <script src="./plugins/dataPointsHandlers.js"></script>
  62. <script src="./plugins/dayjs.min.js"></script>
  63. <script src="./plugins/locale-dayjs/zh-cn.js"></script>
  64. <script src="./plugins/svg.min.js" async defer></script>
  65. <script type="module">
  66. import { registerCustomElement } from './previewScripts/registerCustomElement.js'
  67. window.isPreviewEnv = true;
  68. dayjs.locale('zh-cn');
  69. window.loadIdx = window.layer.load(1, {
  70. shade: [0.2, '#000'],
  71. });
  72. window.lf = null;
  73. const lf = new LogicFlow({
  74. container: document.getElementById('diagram'),
  75. overlapMode: 1,
  76. isSilentMode: true,
  77. hideAnchors: true,
  78. stopMoveGraph: true,
  79. stopZoomGraph: true,
  80. stopScrollGraph: true,
  81. autoWrap: true,
  82. autoExpand: true,
  83. snapline: true,
  84. history: true,
  85. animation: true,
  86. metaKeyMultipleSelected: true,
  87. hoverOutline: false,
  88. nodeSelectedOutline: false,
  89. edgeSelectedOutline: false,
  90. keyboard: {
  91. enabled: true,
  92. shortcuts: [
  93. {
  94. keys: ['backspace'],
  95. callback() {
  96. // 退格键不做任何处理
  97. },
  98. },
  99. ],
  100. },
  101. grid: {
  102. visible: false,
  103. size: 2,
  104. },
  105. background: {
  106. backgroundImage: 'url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImdyaWQiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTSAwIDEwIEwgNDAgMTAgTSAxMCAwIEwgMTAgNDAgTSAwIDIwIEwgNDAgMjAgTSAyMCAwIEwgMjAgNDAgTSAwIDMwIEwgNDAgMzAgTSAzMCAwIEwgMzAgNDAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QwZDBkMCIgb3BhY2l0eT0iMC4yIiBzdHJva2Utd2lkdGg9IjEiLz48cGF0aCBkPSJNIDQwIDAgTCAwIDAgMCA0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZDBkMGQwIiBzdHJva2Utd2lkdGg9IjEiLz48L3BhdHRlcm4+PC9kZWZzPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JpZCkiLz48L3N2Zz4=")',
  107. backgroundColor: '#ffffff',
  108. backgroundRepeat: 'repeat',
  109. backgroundSize: '',
  110. },
  111. })
  112. lf.setTheme(
  113. {
  114. baseEdge: { strokeWidth: 1 },
  115. baseNode: { strokeWidth: 1 },
  116. nodeText: { overflowMode: 'autoWrap', lineHeight: 1.5 },
  117. edgeText: { overflowMode: 'autoWrap', lineHeight: 1.5 },
  118. outline: {
  119. fill: "transparent",
  120. stroke: "transparent",
  121. strokeDasharray: "3,3",
  122. hover: {
  123. stroke: "transparent",
  124. },
  125. },
  126. },
  127. );
  128. window.lf = lf
  129. lf.setDefaultEdgeType('pro-polyline')
  130. registerCustomElement(lf)
  131. lf.render()
  132. lf.setZoomMiniSize(0.1)
  133. const url = new URL(window.location.href);
  134. const params = new URLSearchParams(url.search);
  135. const id = params.get('id');
  136. var scriptCallbacks = []
  137. // console.log('id', id)
  138. if (id) {
  139. service.get('/iotconfigurationdesig/getDetailByBoardManageId', { boardManageId: id }).then((ret) => {
  140. if (ret.code !== 0) {
  141. // return message.warning(ret.msg)
  142. }
  143. if (!ret.data) return;
  144. const { boardType, width: pageWidth, height: pageHeight, isDefaultBackImg, backgroundImage, backgroundColor, backgroundRepeat, pageName, iotThingApiDTOList, deviceRatio, backgroundSize, pictureData } = ret.data
  145. if (iotThingApiDTOList) {
  146. // 给历史数据需要的相关数据提前存起来
  147. iotThingApiDTOList.forEach((i) => {
  148. const time = JSON.parse(i.timeCondition || '{}')
  149. const isHistoryData = time.type !== 'last'
  150. if (isHistoryData) {
  151. getHistoryDatas(i.id, service)
  152. }
  153. })
  154. }
  155. const temp = {
  156. width: pageWidth || 1920,
  157. height: pageHeight || 1080,
  158. backgroundImage: backgroundImage || 'url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImdyaWQiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTSAwIDEwIEwgNDAgMTAgTSAxMCAwIEwgMTAgNDAgTSAwIDIwIEwgNDAgMjAgTSAyMCAwIEwgMjAgNDAgTSAwIDMwIEwgNDAgMzAgTSAzMCAwIEwgMzAgNDAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QwZDBkMCIgb3BhY2l0eT0iMC4yIiBzdHJva2Utd2lkdGg9IjEiLz48cGF0aCBkPSJNIDQwIDAgTCAwIDAgMCA0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZDBkMGQwIiBzdHJva2Utd2lkdGg9IjEiLz48L3BhdHRlcm4+PC9kZWZzPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JpZCkiLz48L3N2Zz4=")',
  159. backgroundColor: backgroundColor || '#ffffff',
  160. backgroundRepeat: backgroundRepeat || 'repeat',
  161. pageName,
  162. deviceRatio,
  163. backgroundSize,
  164. isDefaultBackImg,
  165. }
  166. // 渲染背景
  167. const backHandler = (val) => {
  168. if (val.isDefaultBackImg) {
  169. // 没有背景,就看背景颜色
  170. if (val.backgroundColor && val.backgroundColor !== '#00000000') {
  171. window.lf.setGridVisible(false)
  172. val.backgroundImage = ''
  173. }
  174. else if (val.backgroundColor === '#00000000') {
  175. window.lf.setGridVisible(true)
  176. val.backgroundColor = ''
  177. val.backgroundImage = 'url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImdyaWQiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTSAwIDEwIEwgNDAgMTAgTSAxMCAwIEwgMTAgNDAgTSAwIDIwIEwgNDAgMjAgTSAyMCAwIEwgMjAgNDAgTSAwIDMwIEwgNDAgMzAgTSAzMCAwIEwgMzAgNDAiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2QwZDBkMCIgb3BhY2l0eT0iMC4yIiBzdHJva2Utd2lkdGg9IjEiLz48cGF0aCBkPSJNIDQwIDAgTCAwIDAgMCA0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZDBkMGQwIiBzdHJva2Utd2lkdGg9IjEiLz48L3BhdHRlcm4+PC9kZWZzPjxyZWN0IHdpZHRoPSIxMDAlIiBoZWlnaHQ9IjEwMCUiIGZpbGw9InVybCgjZ3JpZCkiLz48L3N2Zz4=)'
  178. }
  179. }
  180. window.lf.graphModel.changeBackground({
  181. backgroundImage: val.backgroundImage,
  182. backgroundColor: val.backgroundColor,
  183. backgroundRepeat: val.backgroundRepeat,
  184. backgroundSize: val.backgroundSize,
  185. });
  186. document.querySelector('.lf-background').style.backgroundColor = val.backgroundColor;
  187. }
  188. backHandler(temp)
  189. const graphData = JSON.parse(pictureData)
  190. const nodeNames = graphData.nodes.map(n => n.type)
  191. // 获取画布中的组件信息
  192. service.post('/section/iotsectiondetail/queryListByName', nodeNames || []).then(async res => {
  193. if (res.code !== 0) {
  194. // return message.warning(res.msg)
  195. }
  196. // 注册远程组件
  197. await registerRemoteComponents(res.data || [], document)
  198. // 控件事件监听绑定
  199. eventHandlers(window.lf, undefined)
  200. const resizeGraph = function () {
  201. if (boardType === 'bigscreen') {
  202. const diagramDom = document.getElementById('diagram');
  203. const graph = document.querySelector('.lf-graph');
  204. const { width, height } = diagramDom.getBoundingClientRect();
  205. const heightWidthRatio = pageHeight / pageWidth;
  206. const realHeight = width * heightWidthRatio;
  207. window.lf.resize(width, realHeight);
  208. diagramDom.style.overflowY = 'auto';
  209. graph.style.overflow = 'hidden';
  210. window.lf.updateEditConfig({
  211. stopZoomGraph: true,
  212. });
  213. } else {
  214. // 适配画布
  215. const diagramDom = document.getElementById('diagram');
  216. const { width, height } = diagramDom.getBoundingClientRect();
  217. window.lf.resize(width, height);
  218. const scaleY = height / pageHeight;
  219. const transformModel = window.lf.graphModel.transformModel;
  220. transformModel.SCALE_Y = scaleY;
  221. transformModel.SCALE_X = scaleY;
  222. transformModel.focusOn(pageWidth / 2, pageHeight / 2, width, height);
  223. // 背景同比缩放
  224. const back = document.querySelector('.lf-background-area')
  225. back.style.width = height * (pageWidth / pageHeight) + 'px';
  226. back.style.margin = '0 auto';
  227. }
  228. }
  229. window.onresize = function () {
  230. resizeGraph();
  231. };
  232. // console.log('lf', lf)
  233. setTimeout(() => {
  234. lf.render(graphData);
  235. resizeGraph();
  236. // 注册全局单个控件脚本
  237. setTimeout(() => {
  238. const currentGraphData = lf.getGraphData()
  239. currentGraphData.nodes.forEach((element) => {
  240. const nodeData = lf.getNodeDataById(element.id)
  241. const { dynamic } = nodeData.properties
  242. const { scriptData, normalData, uiData, animationData, hiddenData } = dynamic || {}
  243. // 给绑定了 socket 的数据开启socket
  244. if (normalData) {
  245. try {
  246. const dataPointStrParsed = JSON.parse(normalData.dataPoint || uiData.dataPoint || '{}')
  247. const { dataSource } = dataPointStrParsed
  248. const findSuperApi = iotThingApiDTOList && iotThingApiDTOList.find((i) => i.id === dataSource)
  249. if (findSuperApi) {
  250. // 有部件用到超级api 才去开启socket
  251. const time = JSON.parse(findSuperApi.timeCondition || '{}')
  252. const isHistoryData = time.type !== 'last'
  253. startSocket(dataSource, scriptCallbacks, service, isHistoryData)
  254. }
  255. } catch (err) { }
  256. }
  257. // 部件显示隐藏处理
  258. const nodeModel = lf.getNodeModelById(element.id);
  259. if (element.properties.visible !== undefined) {
  260. nodeModel.visible = element.properties.visible
  261. };
  262. // 数据点处理
  263. if (normalData && normalData.dataPoint) {
  264. dataPointsHandlers(lf, service, element.id, normalData.dataPoint, 'normalData', [])
  265. }
  266. if (uiData && uiData.dataPoint) {
  267. dataPointsHandlers(lf, service, element.id, uiData.dataPoint, 'uiData', [])
  268. }
  269. if (animationData) {
  270. animationData.animationCombo.forEach((anim, index) => {
  271. if (anim.dataPoint) {
  272. dataPointsHandlers(lf, service, element.id, anim.dataPoint, 'animationData', [animationHandler], index)
  273. }
  274. })
  275. }
  276. if (hiddenData) {
  277. hiddenData.hiddenCombo.forEach((hid, index) => {
  278. if (hid.dataPoint) {
  279. dataPointsHandlers(lf, service, element.id, hid.dataPoint, 'hiddenData', [showHideHandler], index)
  280. }
  281. })
  282. }
  283. if (scriptData && scriptData.script) {
  284. setTimeout(() => {
  285. const func = new Function('context', 'service', 'nodeId', scriptData.script)
  286. window.lf.globalDatas = window.globalDashboardDatas || {}
  287. const callback = func(window.lf, service, element.id)
  288. if (callback) {
  289. const callbackBound = callback.bind(null, window.lf, service, element.id)
  290. scriptCallbacks.push(callbackBound)
  291. }
  292. }, 1000)
  293. }
  294. });
  295. currentGraphData.edges.forEach((edge) => {
  296. const dataPointStrParsed = JSON.parse(edge.properties.dataPoint || '{}')
  297. const { dataSource } = dataPointStrParsed
  298. const findSuperApi = iotThingApiDTOList && iotThingApiDTOList.find((i) => i.id === dataSource)
  299. if (findSuperApi) {
  300. // 有部件用到超级api 才去开启socket
  301. const time = JSON.parse(findSuperApi.timeCondition || '{}')
  302. const isHistoryData = time.type !== 'last'
  303. startSocket(dataSource, scriptCallbacks, service, isHistoryData)
  304. }
  305. // 绑定连线的数据点
  306. edgeDataPointHandler(lf, service, edge.id, edge.properties.dataPoint, 'edgeData');
  307. });
  308. window.layer.close(window.loadIdx);
  309. }, 1000)
  310. }, 500)
  311. })
  312. })
  313. }
  314. // 节点点击绑定实时数据处理
  315. lf.on('node:click', ({ data, e }) => {
  316. (window).totalListeners.click.forEach((fn) => {
  317. fn({ data, e })
  318. })
  319. })
  320. // 节点双击绑定实时数据处理
  321. lf.on('node:dbclick', ({ data, e }) => {
  322. (window).totalListeners.dbclick.forEach((fn) => {
  323. fn({ data, e })
  324. })
  325. })
  326. // 节点滑入绑定实时数据处理
  327. lf.on('node:mouseenter', ({ data, e }) => {
  328. (window).totalListeners.mouseenter.forEach((fn) => {
  329. fn({ data, e })
  330. })
  331. })
  332. // 节点离开绑定实时数据处理
  333. lf.on('node:mouseleave', ({ data, e }) => {
  334. (window).totalListeners.mouseleave.forEach((fn) => {
  335. fn({ data, e })
  336. })
  337. })
  338. // 监测屏幕缩放
  339. window.onresize = function () {
  340. const screenWidth = window.screen.width;
  341. const screenHeight = window.screen.height;
  342. const innerWidth = window.innerWidth;
  343. const innerHeight = window.innerHeight;
  344. if (screenWidth === innerWidth && screenHeight === innerHeight) {
  345. // 全屏
  346. document.body.classList = 'overflow-hidden'
  347. } else {
  348. document.body.classList = ''
  349. }
  350. }
  351. // 监听点击空白处,消除文本编辑状态
  352. lf.on('blank:click', () => {
  353. const textEle = window.lf.graphModel.textEditElement;
  354. textEle && textEle.setElementState(0)
  355. })
  356. </script>
  357. </body>
  358. </html>