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

400 lines
65 KiB

  1. const t="9c0df275-0475-400d-8ab1-7ce0193ee19b",e="custom-therm-waterfill-node",a="温度计水球图",n="data:image/jpeg;base64,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
  2. "nodes": [
  3. {
  4. "id": "8ab9a2bf-f570-469f-a9a7-dbc62168d0a9",
  5. "type": "custom-therm-waterfill-node",
  6. "x": 200,
  7. "y": 200,
  8. "text": {
  9. "value": "",
  10. "x": 200,
  11. "y": 200
  12. },
  13. "properties": {
  14. "id": "8ab9a2bf-f570-469f-a9a7-dbc62168d0a9",
  15. "width": 200,
  16. "height": 200,
  17. "x": 200,
  18. "y": 200,
  19. "rotation": 0,
  20. "opacity": 1,
  21. "codeConfig": "return option",
  22. "outline": {
  23. "show": false
  24. },
  25. "nodeAlias": "简单温度计水球图",
  26. "showDefaultValue": false,
  27. "showUnit": false,
  28. "valueColor": "rgba(245, 166, 35, 1)",
  29. "fontSize": 12,
  30. "Waves": [
  31. {
  32. "color": "#8bf707"
  33. }
  34. ],
  35. "waterFillShape": "circle",
  36. "backgroundStyle": {
  37. "color": "rgba(255, 255, 255, 0.1)",
  38. "borderColor": "rgba(74, 144, 226, 1)",
  39. "borderWidth": 3,
  40. "shadowBlur": 10,
  41. "shadowColor": "rgba(155, 155, 155, 0.1)"
  42. },
  43. "maxValue": 100,
  44. "title": {
  45. "text": "温度",
  46. "textStyle": {
  47. "color": "#4A90E2",
  48. "fontSize": 18
  49. },
  50. "subtextStyle": {
  51. "color": "#0A5DBF",
  52. "fontSize": 16
  53. }
  54. },
  55. "minValue": 0,
  56. "warnValue": 70,
  57. "seriousValue": 85,
  58. "dynamic": {
  59. "normalData": {
  60. "dataPoint": "",
  61. "compareType": "",
  62. "conditionVariables": [],
  63. "defaultValue": "",
  64. "unit": ""
  65. }
  66. }
  67. }
  68. }
  69. ]
  70. }`,javascript:`const { createApp, createVNode, render } = Vue;
  71. const app = createApp({})
  72. const defaultVal = 62.3;
  73. const ThermWaterFill = {
  74. template: '<div :id="chartId" :style="getStyle"></div>',
  75. props: {
  76. chartId: {
  77. type: String,
  78. default: ''
  79. },
  80. currentData: {
  81. type: Number,
  82. default: 100
  83. },
  84. width: {
  85. type: Number,
  86. default: 350
  87. },
  88. height: {
  89. type: Number,
  90. default: 150
  91. },
  92. chartProps: {
  93. type: Object,
  94. default: () => { }
  95. },
  96. thingName: {
  97. type: String,
  98. default: ''
  99. },
  100. attr: {
  101. type: String,
  102. default: ''
  103. },
  104. unit: {
  105. type: String,
  106. default: ''
  107. },
  108. },
  109. computed: {
  110. getStyle() {
  111. return {
  112. width: \`\${this.width}px\`,
  113. height: \`\${this.height}px\`
  114. }
  115. }
  116. },
  117. setup(props) {
  118. const { onMounted, nextTick, toRefs, watch } = Vue;
  119. const { chartProps, currentData, thingName, attr, width, height } = toRefs(props);
  120. let myChart = null;
  121. const initChart = (data, pros) => {
  122. // 基于准备好的dom,初始化echarts实例
  123. const dom = document.getElementById(props.chartId);
  124. if (dom) {
  125. if (!myChart) {
  126. myChart = echarts.init(dom);
  127. }
  128. // 由于实时推送时候不会重复创建实例,但是需更新画布大小。
  129. myChart.resize({
  130. width: width.value,
  131. height: height.value,
  132. })
  133. if (data != null) {
  134. const { codeConfig, Waves, waterFillShape, backgroundStyle, outline, maxValue, title, minValue, warnValue, seriousValue } = pros;
  135. // 指定图表的配置项和数据
  136. var temperature = (+data).toFixed(1);
  137. const ratioVal = (+data / (maxValue - minValue)).toFixed(1);
  138. const isWarn = +data >= warnValue;
  139. const isSerious = +data >= seriousValue;
  140. const totalColor = Waves.map(i => {
  141. if (isSerious) {
  142. return "#FF0000"
  143. } else if (isWarn){
  144. return "#F8E71C"
  145. } else {
  146. return i.color;
  147. }
  148. });
  149. const totalDatas = totalColor.map(() => {
  150. return {
  151. name: '温度', //数据项名称
  152. value: +ratioVal,
  153. rawValue: +data
  154. }
  155. })
  156. console.log('totalDatas', totalDatas);
  157. var unit = '℃';
  158. var svgPath = 'path://M570,729.5V86.4c0-42.2-31.4-76.4-70-76.4s-70,34.2-70,76.4v643c-41.7,24.3-70,68.9-70,120.6c0,77.3,62.7,140,140,140s140-62.7,140-140C640,798.3,611.7,753.7,570,729.5z';//温度计SVG路径
  159. var option = {
  160. backgroundColor: '', //背景颜色
  161. title: { //标题样式
  162. text: title.text, //主标题
  163. subtext: \`\${temperature}\${unit}\`, //副标题
  164. textStyle: { //标题的样式
  165. color: title.textStyle.color,
  166. fontFamily: 'Microsoft YaHei',
  167. align: 'center',
  168. verticalAlign: 'middle',
  169. fontSize: title.textStyle.fontSize
  170. },
  171. subtextStyle: { //副标题的样式
  172. color: title.subtextStyle.color,
  173. fontSize: title.subtextStyle.fontSize
  174. },
  175. top: '45%',
  176. left: '50%',
  177. itemGap: 10,//主副标题之间的间距。
  178. backgroundColor: 'transparent' //标题背景色,默认透明,设置无效
  179. },
  180. tooltip: { //提示框浮层属性
  181. show: true, //默认为true
  182. transitionDuration: 0.8, //提示框浮层的移动动画过渡时间,单位是 s,设置为 0 的时候会紧跟着鼠标移动
  183. formatter: function (item) { //提示框浮层内容格式器,支持字符串模板和回调函数两种形式
  184. return \`\${temperature}\${unit}\`;
  185. }
  186. },
  187. series: [{
  188. name: '温度', //系列名称
  189. type: 'liquidFill', //系列类型
  190. shape: svgPath, //水填充图的形状 circle默认圆形 rect圆角矩形 triangle三角形 diamond菱形 pin水滴状 arrow箭头状 还可以是svg的path
  191. center: ['30%', '50%'], //图表相对于盒子的位置[水平, 垂直],默认是[50%,50%]在水平、垂直方向居中 可设置百分比活着具体数值
  192. radius: '90%', //图表的大小 值是圆的直径 可以是百分比 也可以是具体值 100%则占满整个盒子 默认是40% 百分比下是根据宽高最小的一个为参照依据
  193. amplitude: 3, //振幅 是波浪的震荡幅度 可以取具体的值 也可以是百分比 百分比下是按图标的直径来算
  194. waveLength: '42%', //波的长度 可以是百分比也可以是具体的像素值 百分比下是相对于直径的 取得越大波浪的起伏越小
  195. phase: 0, //波的相位弧度 默认情况下是自动
  196. direction: 'left', //波移动的速度 两个参数 left 从右往左 right 从左往右
  197. waveAnimation: true, //控制波动画的开关,布尔值: false关闭动画,true开启动画(默认值)
  198. animationEasing: 'linear', //初始动画
  199. animationEasingUpdate: 'quarticInOut', //数据更新的动画效果
  200. animationDuration: 1500, //初始动画的时长,支持回调函数,可以通过每个数据返回不同的 delay 时间实现更绚丽的初始动画效果
  201. animationDurationUpdate: 200, //数据更新动画的时长
  202. data: totalDatas,
  203. label: { //图表内部字体
  204. normal: {
  205. formatter: ''
  206. }
  207. },
  208. outline: outline,
  209. backgroundStyle,
  210. color: totalColor,
  211. itemStyle: {
  212. opacity: 0.8, //波浪的透明度
  213. shadowBlur: 10, //波浪的阴影范围
  214. shadowColor: '#ecfc03' //阴影颜色
  215. },
  216. emphasis: {
  217. itemStyle: {
  218. opacity: 1 //鼠标经过波浪颜色的透明度
  219. }
  220. }
  221. }]
  222. };
  223. // console.log('option', option);
  224. const func = new Function('option', 'datas', codeConfig);
  225. const opt = func(window._.cloneDeep(option), data);
  226. // console.log('opt', opt);
  227. // 使用刚指定的配置项和数据显示图表。
  228. myChart.setOption(opt);
  229. }
  230. }
  231. }
  232. watch([currentData, chartProps], ([val, pros]) => {
  233. nextTick(() => {
  234. initChart(val, pros)
  235. })
  236. }, {
  237. immediate: true,
  238. deep: true,
  239. })
  240. }
  241. }
  242. class CustomThermWaterFillNode extends HtmlResize.view {
  243. realValue = defaultVal
  244. oldProperties = {}
  245. chartRendered = false
  246. instance = null
  247. setHtml(rootEl) {
  248. if (!rootEl) return;
  249. const { properties, width, height } = this.props.model;
  250. const { normalData } = properties.dynamic || {}
  251. let thingName = 'pressure';
  252. let attr = 'score';
  253. if (normalData && normalData.dataPoint) {
  254. const dataPointStrParsed = JSON.parse(normalData.dataPoint || '{}')
  255. const { deviceCode, dataPoint } = dataPointStrParsed;
  256. thingName = deviceCode;
  257. attr = dataPoint.split(',')[0];
  258. }
  259. if (this.instance) {
  260. // 实时数据不能推送一次就创建一次图表,可以在原有实例基础之上更改数据。
  261. Object.assign(this.instance.component.props, {
  262. name: properties.nodeAlias,
  263. chartId: \`waterfill-\${properties.id}\`,
  264. currentData: this.realValue,
  265. width,
  266. height,
  267. chartProps: properties,
  268. thingName,
  269. attr,
  270. unit: normalData.unit || 'km/h'
  271. })
  272. return
  273. }
  274. const el = document.createElement('div');
  275. rootEl.innerHTML = '';
  276. const instance = createVNode(ThermWaterFill, {
  277. name: properties.nodeAlias,
  278. chartId: \`gauge-\${properties.id}\`,
  279. currentData: this.realValue,
  280. width,
  281. height,
  282. chartProps: properties,
  283. thingName,
  284. attr,
  285. unit: normalData.unit || 'km/h'
  286. })
  287. instance.appContext = app._context
  288. render(instance, el)
  289. rootEl.appendChild(el);
  290. this.instance = instance;
  291. }
  292. sameProps(properties) {
  293. const isSame = window._.isEqual(this.oldProperties, properties);
  294. if (isSame) return true;
  295. this.oldProperties = properties;
  296. return false
  297. }
  298. // 生命周期 支持重写内容, 但格式需一致
  299. shouldUpdate() {
  300. const { properties } = this.props.model;
  301. const { normalData } = properties.dynamic || {};
  302. if (normalData && !normalData.dataPoint && !normalData.defaultValue) {
  303. this.realValue = defaultVal;
  304. return true
  305. }
  306. if (normalData) {
  307. const { defaultValue } = normalData || {};
  308. if (defaultValue) {
  309. const realValue = window.resolveScadaNewValue(defaultValue)
  310. if (this.realValue !== Number(realValue)) {
  311. this.realValue = Number(realValue);
  312. return true;
  313. }
  314. }
  315. }
  316. const propertiesBack = window._.cloneDeep(properties);
  317. if (propertiesBack.dynamic.normalData) {
  318. const isSameProps = this.sameProps(propertiesBack);
  319. if (isSameProps && this.chartRendered) {
  320. return false
  321. } else {
  322. if (!this.chartRendered) {
  323. this.chartRendered = true
  324. return true
  325. }
  326. if (!isSameProps) {
  327. return true;
  328. }
  329. }
  330. }
  331. }
  332. updateHtml() {
  333. this.setHtml(this.rootEl);
  334. }
  335. componentDidMount() {
  336. // 防止拖动时候频繁渲染图表
  337. this.updateHtmlDebounced = window._.debounce(this.updateHtml.bind(this), 500);
  338. if (this.shouldUpdate()) {
  339. this.setHtml(this.rootEl);
  340. }
  341. }
  342. componentDidUpdate() {
  343. if (this.shouldUpdate()) {
  344. this.updateHtmlDebounced();
  345. }
  346. }
  347. }
  348. class CustomThermWaterFillModel extends HtmlResize.model {
  349. initNodeData(data) {
  350. // 自定义组件,需最开始重���一下text 。
  351. data.text = {
  352. value: "",
  353. x: data.x,
  354. y: data.y,
  355. };
  356. super.initNodeData(data);
  357. const { properties } = this;
  358. this.width = properties.width || 80;
  359. this.height = properties.height || 35;
  360. this.text.editable = false; // 不允许文本被编辑
  361. }
  362. setAttributes() {
  363. // 自定义组件需重置 text
  364. const { x, y, properties } = this;
  365. const { textHorizontalMove = 0, textVerticalMove = 0 } = properties;
  366. this.text = {
  367. ...this.text,
  368. x: x + textHorizontalMove,
  369. y: y + textVerticalMove,
  370. value: "",
  371. }
  372. }
  373. }
  374. lf.register({
  375. type: 'custom-therm-waterfill-node',
  376. view: CustomThermWaterFillNode,
  377. model: CustomThermWaterFillModel,
  378. })`,css:"",fakeData:""},v={id:t,name:e,aliasName:a,image:n,imageType:f,groupName:l,groupType:o,isRemote:!1,isDefault:!0,sectionType:r,config:s,files:d};export{a as aliasName,s as config,v as default,d as files,l as groupName,o as groupType,t as id,n as image,f as imageType,A as isDefault,i as isRemote,e as name,r as sectionType};