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

342 lines
24 KiB

  1. const e="52d9f250-25be-450a-9aa4-809fefd989fa",n="custom-iframe-node",t="网页",a='<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1698398010161" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5163" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M64 904V120c0-30.93 25.07-56 56-56h784c30.93 0 56 25.07 56 56v784c0 30.93-25.07 56-56 56H120c-30.93 0-56-25.07-56-56z" fill="#E1F5FF" p-id="5164"></path><path d="M960 260H64V120c0-30.93 25.07-56 56-56h784c30.93 0 56 25.07 56 56v140z" fill="#8C9EFF" p-id="5165"></path><path d="M176 162m-28 0a28 28 0 1 0 56 0 28 28 0 1 0-56 0Z" fill="#FF4343" p-id="5166"></path><path d="M260 162m-28 0a28 28 0 1 0 56 0 28 28 0 1 0-56 0Z" fill="#FFD600" p-id="5167"></path><path d="M344 162m-28 0a28 28 0 1 0 56 0 28 28 0 1 0-56 0Z" fill="#65FF40" p-id="5168"></path><path d="M582 451.33H274c-23.2 0-42 18.8-42 42s18.8 42 42 42h308c23.2 0 42-18.8 42-42 0-23.19-18.8-42-42-42zM750 684.67H274c-23.2 0-42 18.8-42 42s18.8 42 42 42h476c23.2 0 42-18.8 42-42s-18.8-42-42-42z" fill="#313FA0" p-id="5169"></path></svg>',i="svg",l="动态",s="数据展示",p=!1,c=!0,o="时间",r=`{"id":"u:270584784ce1","type":"page","name":"page1","asideResizor":false,"style":{"boxShadow":" 0px 0px 0px 0px transparent"},"pullRefresh":{"disabled":true},"body":[{"type":"tabs","name":"tab","tabs":[{"title":"样式","icon":"fa fa-th-large","body":[{"type":"form","title":"","name":"basicPropForm","body":[{"type":"input-text","label":"名称","name":"nodeAlias","id":"u:6b126f0520cb","size":"full","mode":"horizontal","inputControlClassName":"w-100","className":"m-b"},{"type":"input-text","label":"ID&nbsp;&nbsp;&nbsp;&nbsp;","name":"id","id":"u:6232710ac003","size":"full","mode":"horizontal","inputControlClassName":"w-100","className":"m-b"},{"type":"grid","id":"u:c605398a724c","className":"m-b","columns":[{"body":[{"type":"input-number","label":"宽度","name":"width","keyboard":true,"id":"u:dcc0c21d16f6","step":1,"suffix":"px","placeholder":"组件左边距","size":"full","mode":"horizontal","className":"m-b","value":200,"labelAlign":"left","precision":2,"inputClassName":"w-full","labelClassName":"w-8"}],"id":"u:14cc19d6ffb0","md":6},{"body":[{"type":"input-number","label":"高度","name":"height","keyboard":true,"id":"u:cd6fdff9ca88","step":1,"suffix":"px","placeholder":"组件上边距","size":"full","mode":"horizontal","className":"m-b","value":200,"labelAlign":"left","precision":2,"inputClassName":"w-full","labelClassName":"w-8"}],"id":"u:4931801ca9b8","md":6}]},{"type":"grid","id":"u:da449a94908a","className":"m-b","columns":[{"body":[{"type":"input-number","label":"X 轴","name":"x","keyboard":true,"id":"u:29852d093d9d","step":1,"suffix":"px","placeholder":"组件左边距","size":"full","mode":"horizontal","className":"m-b","value":200,"labelAlign":"left","precision":2,"inputClassName":"w-full","labelClassName":"w-8"}],"id":"u:1b561d652acc","md":6},{"body":[{"type":"input-number","label":"Y 轴","name":"y","keyboard":true,"id":"u:dc8c1daed8ed","step":1,"suffix":"px","placeholder":"组件上边距","size":"full","mode":"horizontal","className":"m-b","value":200,"labelAlign":"left","precision":2,"inputClassName":"w-full","labelClassName":"w-8"}],"id":"u:9672575193ac","md":6}]},{"type":"grid","id":"u:a332a7bf83c1","className":"m-b","columns":[{"body":[{"type":"input-number","label":"旋转","name":"rotation","id":"u:f6a2dbb518f9","placeholder":"组件旋转角度","mode":"horizontal","size":"full","className":"","keyboard":true,"step":1,"suffix":"deg","value":0,"labelAlign":"left","inputClassName":"w-full"}],"id":"u:646cd98b7955","md":6},{"body":[{"type":"input-number","label":"透明","name":"opacity","id":"u:cf80f59d8d42","placeholder":"组件透明度","mode":"horizontal","size":"full","className":"m-b","keyboard":true,"step":0,"suffix":"","value":1,"inputClassName":"w-full","precision":2}],"id":"u:51ddf54ac749","md":6}],"gap":""},{"type":"input-text","label":"链�
  2. "nodes": [
  3. {
  4. "id": "28c1cc50-eecd-4534-afef-bd752f52aaa2",
  5. "type": "custom-iframe-node",
  6. "x": 200,
  7. "y": 200,
  8. "text": {
  9. "value": "",
  10. "x": 200,
  11. "y": 200
  12. },
  13. "properties": {
  14. "id": "28c1cc50-eecd-4534-afef-bd752f52aaa2",
  15. "width": 500,
  16. "height": 300,
  17. "x": 200,
  18. "y": 200,
  19. "rotation": 0,
  20. "opacity": 1,
  21. "disableClick": false,
  22. "nodeAlias": "外链",
  23. "showDefaultValue": false,
  24. "showUnit": false,
  25. "valueColor": "rgba(245, 166, 35, 1)",
  26. "fontSize": 12,
  27. "linkAddress": "",
  28. "dynamic": {
  29. "normalData": {
  30. "dataPoint": "",
  31. "compareType": "",
  32. "conditionVariables": [],
  33. "defaultValue": "",
  34. "unit": "",
  35. "iframeScript": "",
  36. "iframeOuterScript": "",
  37. "iframePlugins": [
  38. {
  39. "input-text": "./plugins/vue.min.js"
  40. },
  41. {
  42. "input-text": "./plugins/axios.min.js"
  43. },
  44. {
  45. "input-text": "./plugins/lodash.js"
  46. },
  47. {
  48. "input-text": "./plugins/dayjs.min.js"
  49. },
  50. {
  51. "input-text": "./plugins/service.js"
  52. },
  53. {
  54. "input-text": "./iconfonts/iconfont.css"
  55. },
  56. {
  57. "input-text": "./plugins/layui/css/layui.css"
  58. },
  59. {
  60. "input-text": "./plugins/layui/layui.js"
  61. }
  62. ]
  63. },
  64. "eventsData": {
  65. "eventCombo": [
  66. {
  67. "eventType": "change",
  68. "enable": false,
  69. "config": ""
  70. }
  71. ]
  72. }
  73. }
  74. }
  75. }
  76. ]
  77. }`,javascript:`const { createApp, createVNode, render } = Vue;
  78. const app = createApp({})
  79. const timeArr = new Array(24).fill('');
  80. const totals = [];
  81. timeArr.forEach((i, index) => { const t = window.dayjs().hour(index).valueOf(); totals.push({ val: Math.random(1000) * 100, ts: t, attrKey: "A29" }) });
  82. const defaultSocketValue = []
  83. const generateHTML = (innerPage, plugins, iframeScript) => {
  84. return \`<html>
  85. <head>
  86. </head>
  87. <body>
  88. \${innerPage}
  89. <script type="module" id="_script">
  90. window.addEventListener('message', function(event) {
  91. if (event.data.event === 'tokenSend') {
  92. window.iframeNodeToken = event.data.token;
  93. }
  94. });
  95. const loadInnerScript = () => {
  96. if (\${JSON.stringify(iframeScript)}) {
  97. const innderScript = document.createElement('script');
  98. innderScript.type = 'module';
  99. innderScript.innerHTML = \${JSON.stringify(iframeScript)};
  100. setTimeout(() => {
  101. document.body.append(innderScript);
  102. }, 200);
  103. };
  104. };
  105. let loadPlugin = function (url, index) {
  106. if (url.endsWith('.css')) {
  107. const linkDom = document.createElement('link');
  108. linkDom.rel = "stylesheet";
  109. linkDom.href = url;
  110. linkDom.onload = () => {
  111. if (index === \${JSON.stringify(plugins)}.length - 1) {
  112. loadInnerScript();
  113. } else {
  114. loadPlugin(\${JSON.stringify(plugins)}[index + 1]['input-text'], index + 1);
  115. }
  116. };
  117. document.body.append(linkDom);
  118. } else {
  119. const scriptDom = document.createElement('script');
  120. scriptDom.src = url;
  121. scriptDom.onload = () => {
  122. if (index === \${JSON.stringify(plugins)}.length - 1) {
  123. loadInnerScript();
  124. } else {
  125. loadPlugin(\${JSON.stringify(plugins)}[index + 1]['input-text'], index + 1);
  126. }
  127. };
  128. document.body.append(scriptDom);
  129. }
  130. };
  131. if (\${JSON.stringify(plugins)}.length > 0) {
  132. loadPlugin(\${JSON.stringify(plugins)}[0]['input-text'], 0);
  133. }
  134. window?.parent.postMessage('childFrameLoaded', '*');
  135. </\\script>
  136. </body>
  137. </html\`
  138. }
  139. const IframeNode = {
  140. template: \`<div :style="getIframeOuterStyle">
  141. <iframe v-if="!linkAddress && iframeScript" :id="chartId" ref="iframeRef" width="100%" height="100%" sandbox="allow-scripts allow-modals allow-same-origin" frameborder="0" draggable="false"></iframe>
  142. <iframe v-if="linkAddress && !iframeScript" :src="linkAddress" :id="chartId" ref="iframeRef" width="100%" height="100%" sandbox="allow-scripts allow-modals allow-same-origin" frameborder="0" draggable="false"></iframe>
  143. <div v-if="!linkAddress && !iframeScript" style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; border: 1px solid #00ffff; box-sizing: border-box;color: #F5A623">暂无内容</div>
  144. </div>
  145. \`,
  146. props: {
  147. chartId: {
  148. type: String,
  149. default: ''
  150. },
  151. width: {
  152. type: Number,
  153. default: 350
  154. },
  155. height: {
  156. type: Number,
  157. default: 150
  158. },
  159. codeConfig: {
  160. type: String,
  161. default: ''
  162. },
  163. linkAddress: {
  164. type: String,
  165. default: ''
  166. },
  167. iframeScript: {
  168. type: String,
  169. default: ''
  170. },
  171. iframePlugins: {
  172. type: Array,
  173. default: () => []
  174. },
  175. disableClick: {
  176. type: Boolean,
  177. default: false
  178. }
  179. },
  180. computed: {
  181. getIframeOuterStyle() {
  182. return {
  183. width: '100%',
  184. height: '100%',
  185. cursor: 'default',
  186. 'pointer-events': this.disableClick ? 'none' : 'auto',
  187. }
  188. },
  189. },
  190. emits: ['iframeDataSend'],
  191. setup(props, { emit }) {
  192. const { nextTick, ref, onMounted } = Vue;
  193. const iframeRef = ref(null);
  194. onMounted(() => {
  195. nextTick(() => {
  196. if (iframeRef.value) {
  197. // 给iframe 注入token
  198. const cacheToken = sessionStorage.getItem('v1@CacheToken');
  199. const tokenParsed = JSON.parse(cacheToken || '{}');
  200. const tokenArr = window.location.search.split('token=');
  201. const token = tokenParsed.token || sessionStorage.getItem('token') || window.developToken || tokenArr[1];
  202. window.addEventListener('message', function (event) {
  203. emit('iframeDataSend', event.data);
  204. if (event.data === 'childFrameLoaded') {
  205. if (iframeRef.value.contentWindow) {
  206. iframeRef.value.contentWindow.postMessage({ event: 'tokenSend', token: token });
  207. }
  208. }
  209. })
  210. if (props.linkAddress && props.iframeScript) {
  211. const xhr = new XMLHttpRequest();
  212. xhr.open("GET", props.linkAddress, true);
  213. xhr.send();
  214. xhr.addEventListener("load", () => {
  215. const resText = xhr.responseText;
  216. iframeRef.value.srcdoc = generateHTML(resText, props.iframePlugins, props.iframeScript)
  217. });
  218. } else if (!props.linkAddress && props.iframeScript) {
  219. iframeRef.value.srcdoc = generateHTML('', props.iframePlugins, props.iframeScript)
  220. }
  221. }
  222. })
  223. })
  224. return {
  225. iframeRef
  226. }
  227. }
  228. }
  229. class CustomIframeNode extends HtmlResize.view {
  230. oldProperties = {}
  231. setHtml(rootEl) {
  232. if (!rootEl) return;
  233. const { properties, width, height, } = this.props.model;
  234. const { nodeAlias, codeConfig, linkAddress, disableClick } = properties;
  235. const { iframeScript, iframeOuterScript, iframePlugins } = properties.dynamic.normalData || {};
  236. const { model, graphModel } = this.props;
  237. const fn = new Function('context', 'iframeData', iframeOuterScript);
  238. const handleIframeData = (iframeData) => {
  239. if (iframeOuterScript) {
  240. fn(window.lf || this, iframeData);
  241. } else {
  242. graphModel.eventCenter.emit("node:change", {
  243. data: model,
  244. e: iframeData,
  245. });
  246. }
  247. }
  248. const el = document.createElement('div');
  249. el.style.width = "100%";
  250. el.style.height = "100%";
  251. rootEl.innerHTML = '';
  252. const instance = createVNode(IframeNode, {
  253. name: nodeAlias,
  254. chartId: \`iframe-\${properties.id}\`,
  255. width,
  256. height,
  257. codeConfig,
  258. linkAddress,
  259. iframeScript,
  260. iframePlugins,
  261. disableClick,
  262. onIframeDataSend: handleIframeData
  263. })
  264. instance.appContext = app._context
  265. render(instance, el)
  266. rootEl.appendChild(el);
  267. }
  268. sameProps(properties) {
  269. const isSame = window._.isEqual(this.oldProperties, properties);
  270. if (isSame) return true;
  271. this.oldProperties = properties;
  272. return false
  273. }
  274. // 生命周期 支持重写内容, 但格式需一致
  275. shouldUpdate() {
  276. const { properties } = this.props.model;
  277. const propertiesBack = window._.cloneDeep(properties);
  278. // 由于事件change 会给properties 增加一个 event 属性(见目录scadaDashboard/Diagram/useDynamicEventsHandler),会引发属性的改变,导致组件重渲染。
  279. delete propertiesBack.event;
  280. if (this.sameProps(propertiesBack)) {
  281. return false
  282. }
  283. return true;
  284. }
  285. }
  286. class CustomIframeModel extends HtmlResize.model {
  287. initNodeData(data) {
  288. // 自定义组件,需最开始重置一下text 。
  289. data.text = {
  290. value: "",
  291. x: data.x,
  292. y: data.y,
  293. };
  294. super.initNodeData(data);
  295. const { properties } = this;
  296. this.width = properties.width || 80;
  297. this.height = properties.height || 35;
  298. this.text.editable = false; // 不允许文本被编辑
  299. }
  300. setAttributes() {
  301. // 自定义组件需重置 text
  302. const { x, y, properties } = this;
  303. const { textHorizontalMove = 0, textVerticalMove = 0 } = properties;
  304. this.text = {
  305. ...this.text,
  306. x: x + textHorizontalMove,
  307. y: y + textVerticalMove,
  308. value: "",
  309. }
  310. }
  311. getResizeOutlineStyle() {
  312. return {
  313. stroke: "#00ffff",
  314. strokeWidth: 1,
  315. strokeDasharray: "none",
  316. };
  317. }
  318. }
  319. lf.register({
  320. type: 'custom-iframe-node',
  321. view: CustomIframeNode,
  322. model: CustomIframeModel,
  323. })`,css:"",fakeData:""},u={id:e,name:n,aliasName:t,image:a,imageType:i,groupName:l,groupType:s,isRemote:!1,isDefault:!0,sectionType:o,config:r,files:d};export{t as aliasName,r as config,u as default,d as files,l as groupName,s as groupType,e as id,a as image,i as imageType,c as isDefault,p as isRemote,n as name,o as sectionType};