widgetLineStackChart.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. <template>
  2. <div :style="styleObj">
  3. <v-chart v-if="vChartVisible" ref="myVChart" :option="options" autoresize/>
  4. </div>
  5. </template>
  6. <script>
  7. import {targetWidgetLinkageLogic} from "@/views/bigscreenDesigner/designer/linkageLogic";
  8. export default {
  9. name: "WidgetLineStackChart",
  10. components: {},
  11. props: {
  12. value: Object,
  13. ispreview: Boolean,
  14. },
  15. data() {
  16. return {
  17. vChartVisible: true,
  18. options: {
  19. grid: {},
  20. legend: {
  21. textStyle: {
  22. color: "#fff",
  23. },
  24. },
  25. xAxis: {
  26. type: "category",
  27. data: [],
  28. axisLabel: {
  29. show: true,
  30. textStyle: {
  31. color: "#fff",
  32. },
  33. },
  34. },
  35. yAxis: {
  36. type: "value",
  37. data: [],
  38. axisLabel: {
  39. show: true,
  40. textStyle: {
  41. color: "#fff",
  42. },
  43. },
  44. },
  45. series: [
  46. {
  47. data: [],
  48. name: "",
  49. type: "line",
  50. barGap: "0%",
  51. itemStyle: {
  52. barBorderRadius: null,
  53. },
  54. },
  55. ],
  56. },
  57. optionsStyle: {}, // 样式
  58. optionsData: {}, // 数据
  59. optionsSetup: {},
  60. flagInter: null,
  61. };
  62. },
  63. computed: {
  64. styleObj() {
  65. return {
  66. position: this.ispreview ? "absolute" : "static",
  67. width: this.optionsStyle.width + "px",
  68. height: this.optionsStyle.height + "px",
  69. left: this.optionsStyle.left + "px",
  70. top: this.optionsStyle.top + "px",
  71. background: this.optionsSetup.background,
  72. };
  73. },
  74. allComponentLinkage() {
  75. return this.$store.state.designer.allComponentLinkage;
  76. },
  77. },
  78. watch: {
  79. value: {
  80. handler(val) {
  81. this.optionsStyle = val.position;
  82. this.optionsData = val.data;
  83. this.optionsCollapse = val.setup;
  84. this.optionsSetup = val.setup;
  85. this.editorOptions();
  86. },
  87. deep: true,
  88. },
  89. },
  90. mounted() {
  91. this.optionsStyle = this.value.position;
  92. this.optionsData = this.value.data;
  93. this.optionsCollapse = this.value.setup;
  94. this.optionsSetup = this.value.setup;
  95. this.editorOptions();
  96. targetWidgetLinkageLogic(this); // 联动-目标组件逻辑
  97. },
  98. methods: {
  99. // 修改图标options属性
  100. editorOptions() {
  101. this.setOptionsTitle();
  102. this.setOptionsX();
  103. this.setOptionsY();
  104. this.setOptionsTooltip();
  105. this.setOptionsMargin();
  106. this.setOptionsLegend();
  107. this.setOptionsData();
  108. },
  109. // 标题修改
  110. setOptionsTitle() {
  111. const optionsSetup = this.optionsSetup;
  112. const title = {
  113. text: optionsSetup.text,
  114. show: optionsSetup.isShowTitle,
  115. left: optionsSetup.titleLeft,
  116. top: optionsSetup.titleTop + "%",
  117. itemGap: optionsSetup.titleItemGap,
  118. textStyle: {
  119. color: optionsSetup.textColor,
  120. fontSize: optionsSetup.textFontSize,
  121. fontWeight: optionsSetup.textFontWeight,
  122. fontStyle: optionsSetup.textFontStyle,
  123. fontFamily: optionsSetup.textFontFamily,
  124. },
  125. subtext: optionsSetup.subtext,
  126. subtextStyle: {
  127. color: optionsSetup.subtextColor,
  128. fontWeight: optionsSetup.subtextFontWeight,
  129. fontSize: optionsSetup.subtextFontSize,
  130. fontStyle: optionsSetup.subtextFontStyle,
  131. fontFamily: optionsSetup.subtextFontFamily
  132. },
  133. };
  134. this.options.title = title;
  135. },
  136. // X轴设置
  137. setOptionsX() {
  138. const optionsSetup = this.optionsSetup;
  139. const xAxis = {
  140. type: "category",
  141. // 坐标轴是否显示
  142. show: optionsSetup.isShowX,
  143. max: optionsSetup.maxX !== "" ? optionsSetup.maxX : null,
  144. min: optionsSetup.minX !== "" ? optionsSetup.minX : null,
  145. position: optionsSetup.positionX,
  146. offset: optionsSetup.offsetX,
  147. // 坐标轴名称
  148. name: optionsSetup.nameX,
  149. nameLocation: optionsSetup.nameLocationX,
  150. nameTextStyle: {
  151. color: optionsSetup.nameColorX,
  152. fontSize: optionsSetup.nameFontSizeX,
  153. fontWeight: optionsSetup.nameFontWeightX,
  154. fontStyle: optionsSetup.nameFontStyleX,
  155. fontFamily: optionsSetup.nameFontFamilyX,
  156. },
  157. // 轴反转
  158. inverse: optionsSetup.reversalX,
  159. axisLabel: {
  160. show: optionsSetup.isShowAxisLabelX,
  161. interval: optionsSetup.textIntervalX,
  162. // 文字角度
  163. rotate: optionsSetup.textAngleX,
  164. textStyle: {
  165. // 坐标文字颜色
  166. color: optionsSetup.textColorX,
  167. fontSize: optionsSetup.textFontSizeX,
  168. fontWeight: optionsSetup.textFontWeightX,
  169. fontStyle: optionsSetup.textFontStyleX,
  170. fontFamily: optionsSetup.textFontFamilyX,
  171. },
  172. },
  173. // X轴线
  174. axisLine: {
  175. show: optionsSetup.isShowAxisLineX,
  176. lineStyle: {
  177. color: optionsSetup.lineColorX,
  178. width: optionsSetup.lineWidthX,
  179. },
  180. },
  181. // X轴刻度线
  182. axisTick: {
  183. show: optionsSetup.isShowAxisLineX,
  184. lineStyle: {
  185. color: optionsSetup.lineColorX,
  186. width: optionsSetup.lineWidthX,
  187. },
  188. },
  189. // X轴分割线
  190. splitLine: {
  191. show: optionsSetup.isShowSplitLineX,
  192. lineStyle: {
  193. color: optionsSetup.splitLineColorX,
  194. width: optionsSetup.splitLineWidthX,
  195. },
  196. },
  197. };
  198. this.options.xAxis = xAxis;
  199. },
  200. // Y轴设置
  201. setOptionsY() {
  202. const optionsSetup = this.optionsSetup;
  203. const yAxis = {
  204. max: optionsSetup.maxY !== "" ? optionsSetup.maxY : null,
  205. min: optionsSetup.minY !== "" ? optionsSetup.minY : null,
  206. type: "value",
  207. scale: optionsSetup.scale,
  208. // 均分
  209. splitNumber: optionsSetup.splitNumberY,
  210. // 坐标轴是否显示
  211. show: optionsSetup.isShowY,
  212. position: optionsSetup.positionY,
  213. offset: optionsSetup.offsetY,
  214. // 坐标轴名称
  215. name: optionsSetup.textNameY,
  216. nameLocation: optionsSetup.nameLocationY,
  217. nameTextStyle: {
  218. color: optionsSetup.nameColorY,
  219. fontSize: optionsSetup.nameFontSizeY,
  220. fontWeight: optionsSetup.nameFontWeightY,
  221. fontStyle: optionsSetup.nameFontStyleY,
  222. fontFamily: optionsSetup.nameFontFamilyY,
  223. },
  224. // 轴反转
  225. inverse: optionsSetup.reversalY,
  226. axisLabel: {
  227. show: optionsSetup.isShowAxisLabelY,
  228. // 文字角度
  229. rotate: optionsSetup.textAngleY,
  230. //interval: optionsSetup.textIntervalY,
  231. textStyle: {
  232. // 坐标文字颜色
  233. color: optionsSetup.textColorY,
  234. fontSize: optionsSetup.textFontSizeY,
  235. fontWeight: optionsSetup.textFontWeightY,
  236. fontStyle: optionsSetup.textFontStyleY,
  237. fontFamily: optionsSetup.textFontFamilyY,
  238. },
  239. },
  240. axisLine: {
  241. show: optionsSetup.isShowAxisLineY,
  242. lineStyle: {
  243. color: optionsSetup.lineColorY,
  244. width: optionsSetup.lineWidthY,
  245. },
  246. },
  247. axisTick: {
  248. show: optionsSetup.isShowAxisLineY,
  249. lineStyle: {
  250. color: optionsSetup.lineColorY,
  251. width: optionsSetup.lineWidthY,
  252. },
  253. },
  254. splitLine: {
  255. show: optionsSetup.isShowSplitLineY,
  256. lineStyle: {
  257. color: optionsSetup.splitLineColorY,
  258. width: optionsSetup.splitLineWidthY,
  259. },
  260. },
  261. };
  262. this.options.yAxis = yAxis;
  263. },
  264. // 获取面积
  265. getOptionArea() {
  266. const optionsSetup = this.optionsSetup;
  267. let areaStyle = [];
  268. if (optionsSetup.area) {
  269. areaStyle = {
  270. opacity: optionsSetup.areaThickness / 100,
  271. };
  272. } else {
  273. areaStyle = {
  274. opacity: 0,
  275. };
  276. }
  277. return areaStyle;
  278. },
  279. // tooltip 提示语设置,鼠标放置显示
  280. setOptionsTooltip() {
  281. const optionsSetup = this.optionsSetup;
  282. const tooltip = {
  283. show: optionsSetup.isShowTooltip,
  284. trigger: optionsSetup.tooltipTrigger,
  285. axisPointer: {
  286. type: optionsSetup.tooltipAxisPointerType,
  287. },
  288. backgroundColor: optionsSetup.tooltipBackgroundColor,
  289. borderColor: optionsSetup.tooltipBorderColor,
  290. borderWidth: optionsSetup.tooltipBorderWidth,
  291. textStyle: {
  292. color: optionsSetup.tooltipColor,
  293. fontSize: optionsSetup.tooltipFontSize,
  294. fontWeight: optionsSetup.tooltipFontWeight,
  295. fontStyle: optionsSetup.tooltipFontStyle,
  296. fontFamily: optionsSetup.tooltipFontFamily,
  297. },
  298. };
  299. this.options.tooltip = tooltip;
  300. },
  301. // 边距设置
  302. setOptionsMargin() {
  303. const optionsSetup = this.optionsSetup;
  304. const grid = {
  305. left: optionsSetup.marginLeft,
  306. right: optionsSetup.marginRight,
  307. bottom: optionsSetup.marginBottom,
  308. top: optionsSetup.marginTop,
  309. containLabel: true,
  310. };
  311. this.options.grid = grid;
  312. },
  313. // 图例操作 legend
  314. setOptionsLegend() {
  315. const optionsSetup = this.optionsSetup;
  316. const legend = {
  317. show: optionsSetup.isShowLegend,
  318. left: optionsSetup.lateralPosition,
  319. //right: optionsSetup.lateralPosition,
  320. top: optionsSetup.longitudinalPosition,
  321. //bottom: optionsSetup.longitudinalPosition,
  322. orient: optionsSetup.layoutFront,
  323. textStyle: {
  324. color: optionsSetup.legendColor,
  325. fontSize: optionsSetup.legendFontSize,
  326. fontWeight: optionsSetup.legendFontWeight,
  327. fontStyle: optionsSetup.legendFontStyle,
  328. fontFamily: optionsSetup.legendFontFamily,
  329. },
  330. itemHeight: optionsSetup.legendHeight,
  331. itemWidth: optionsSetup.legendWidth,
  332. };
  333. this.options.legend = legend;
  334. },
  335. // 图例名称设置
  336. setOptionsLegendName(name) {
  337. const optionsSetup = this.optionsSetup;
  338. const series = this.options.series;
  339. const legendName = optionsSetup.legendName;
  340. // 图例没有手动写则显示原值,写了则显示新值
  341. if (null == legendName || legendName === "") {
  342. for (let i = 0; i < name.length; i++) {
  343. series[i].name = name[i];
  344. }
  345. this.options.legend["data"] = name;
  346. } else {
  347. const arr = legendName.split("|");
  348. for (let i = 0; i < arr.length; i++) {
  349. series[i].name = arr[i];
  350. }
  351. this.options.legend["data"] = arr;
  352. }
  353. },
  354. // 图例颜色修改
  355. setOptionsColor() {
  356. const optionsCollapse = this.optionsSetup;
  357. const customColor = optionsCollapse.customColor;
  358. if (!customColor) return;
  359. const arrColor = [];
  360. for (let i = 0; i < customColor.length; i++) {
  361. arrColor.push(customColor[i].color);
  362. }
  363. this.options.color = arrColor;
  364. this.options = Object.assign({}, this.options);
  365. },
  366. // 数据解析
  367. setOptionsData(e, paramsConfig) {
  368. const optionsSetup = this.optionsSetup;
  369. // 数据类型 静态 or 动态
  370. const optionsData = this.optionsData;
  371. // 联动接收者逻辑开始
  372. optionsData.dynamicData = optionsData.dynamicData || {}; // 兼容 dynamicData undefined
  373. const myDynamicData = optionsData.dynamicData;
  374. clearInterval(this.flagInter); // 不管咋,先干掉上一次的定时任务,避免多跑
  375. if (
  376. e &&
  377. optionsData.dataType !== "staticData" &&
  378. Object.keys(myDynamicData.contextData).length
  379. ) {
  380. const keyArr = Object.keys(myDynamicData.contextData);
  381. paramsConfig.forEach((conf) => {
  382. if (keyArr.includes(conf.targetKey)) {
  383. myDynamicData.contextData[conf.targetKey] = e[conf.originKey];
  384. }
  385. });
  386. }
  387. // 联动接收者逻辑结束
  388. optionsData.dataType === "staticData"
  389. ? this.staticDataFn(optionsData.staticData, optionsSetup)
  390. : this.dynamicDataFn(
  391. optionsData.dynamicData,
  392. optionsData.refreshTime,
  393. optionsSetup
  394. );
  395. },
  396. //去重
  397. setUnique(arr) {
  398. let newArr = [];
  399. arr.forEach((item) => {
  400. return newArr.includes(item) ? "" : newArr.push(item);
  401. });
  402. return newArr;
  403. },
  404. //静态数据
  405. staticDataFn(val) {
  406. const optionsSetup = this.optionsSetup;
  407. //颜色
  408. const customColor = optionsSetup.customColor;
  409. const arrColor = [];
  410. for (let i = 0; i < customColor.length; i++) {
  411. arrColor.push(customColor[i].color);
  412. }
  413. //数据
  414. const series = [];
  415. let xAxisList = [];
  416. let yAxisList = [];
  417. const legendName = [];
  418. for (const i in val) {
  419. xAxisList[i] = val[i].axis;
  420. yAxisList[i] = val[i].name;
  421. }
  422. xAxisList = this.setUnique(xAxisList);
  423. yAxisList = this.setUnique(yAxisList);
  424. for (const i in yAxisList) {
  425. const data = new Array(xAxisList.length).fill(0);
  426. for (const j in xAxisList) {
  427. for (const k in val) {
  428. if (val[k].name === yAxisList[i]) {
  429. if (val[k].axis === xAxisList[j]) {
  430. data[j] = val[k].data;
  431. }
  432. }
  433. }
  434. }
  435. series.push({
  436. name: yAxisList[i],
  437. type: "line",
  438. data: data,
  439. width: optionsSetup.lineWidth,
  440. symbol: optionsSetup.symbol,
  441. showSymbol: optionsSetup.markPoint,
  442. symbolSize: optionsSetup.pointSize,
  443. smooth: optionsSetup.smoothCurve,
  444. // 线条
  445. lineStyle: {
  446. color: arrColor[i],
  447. width: optionsSetup.lineWidth,
  448. },
  449. //点
  450. itemStyle: {
  451. color: arrColor[i],
  452. },
  453. areaStyle: this.getOptionArea(),
  454. //数值设定
  455. label: {
  456. show: optionsSetup.isShow,
  457. position: optionsSetup.fontPosition,
  458. distance: optionsSetup.fontDistance,
  459. fontSize: optionsSetup.fontSize,
  460. color: optionsSetup.fontColor,
  461. fontWeight: optionsSetup.fontWeight,
  462. formatter: !!optionsSetup.percentSign ? '{c}%' : '{c}',
  463. fontStyle: optionsSetup.fontStyle,
  464. fontFamily: optionsSetup.fontFamily,
  465. },
  466. });
  467. legendName.push(yAxisList[i]);
  468. }
  469. this.options.series = series;
  470. if (optionsSetup.verticalShow) {
  471. this.options.xAxis.data = [];
  472. this.options.yAxis.data = xAxisList;
  473. this.options.xAxis.type = "value";
  474. this.options.yAxis.type = "category";
  475. } else {
  476. this.options.xAxis.data = xAxisList;
  477. this.options.yAxis.data = [];
  478. this.options.xAxis.type = "category";
  479. this.options.yAxis.type = "value";
  480. }
  481. // 根据图表的宽度 x轴的字体大小、长度来估算X轴的label能展示多少个字
  482. const rowsNum = optionsSetup.textRowsNum !== "" ? optionsSetup.textRowsNum : parseInt((this.optionsStyle.width / xAxisList.length) / optionsSetup.textFontSizeX);
  483. const axisLabel = {
  484. show: optionsSetup.isShowAxisLabelX,
  485. interval: optionsSetup.textIntervalX,
  486. // 文字角度
  487. rotate: optionsSetup.textAngleX,
  488. textStyle: {
  489. // 坐标文字颜色
  490. color: optionsSetup.textColorX,
  491. fontSize: optionsSetup.textFontSizeX,
  492. fontWeight: optionsSetup.textFontWeightX,
  493. fontStyle: optionsSetup.textFontStyleX,
  494. fontFamily: optionsSetup.textFontFamilyX,
  495. },
  496. // 自动换行
  497. formatter: function (value, index) {
  498. const strs = value.split('');
  499. let str = ''
  500. for (let i = 0, s; s = strs[i++];) {
  501. str += s;
  502. if (!(i % rowsNum)) str += '\n';
  503. }
  504. return str
  505. }
  506. }
  507. if (optionsSetup.textRowsBreakAuto) {
  508. this.options.xAxis.axisLabel = axisLabel;
  509. }
  510. this.options.legend["data"] = legendName;
  511. this.setOptionsLegendName(legendName);
  512. },
  513. // 动态数据
  514. dynamicDataFn(val, refreshTime, optionsSetup) {
  515. if (!val) return;
  516. if (this.ispreview) {
  517. this.getEchartData(val, optionsSetup);
  518. this.flagInter = setInterval(() => {
  519. this.getEchartData(val, optionsSetup);
  520. }, refreshTime);
  521. } else {
  522. this.getEchartData(val, optionsSetup);
  523. }
  524. },
  525. getEchartData(val, optionsSetup) {
  526. const data = this.queryEchartsData(val);
  527. data.then((res) => {
  528. this.renderingFn(optionsSetup, res);
  529. });
  530. },
  531. renderingFn(optionsSetup, val) {
  532. //颜色
  533. const customColor = optionsSetup.customColor;
  534. const arrColor = [];
  535. for (let i = 0; i < customColor.length; i++) {
  536. arrColor.push(customColor[i].color);
  537. }
  538. // x轴
  539. if (optionsSetup.verticalShow) {
  540. this.options.xAxis.data = [];
  541. this.options.yAxis.data = val.xAxis;
  542. this.options.xAxis.type = "value";
  543. this.options.yAxis.type = "category";
  544. } else {
  545. this.options.xAxis.data = val.xAxis;
  546. this.options.yAxis.data = [];
  547. this.options.xAxis.type = "category";
  548. this.options.yAxis.type = "value";
  549. }
  550. const series = [];
  551. const legendName = [];
  552. for (const i in val.series) {
  553. if (val.series[i].type === "line") {
  554. series.push({
  555. name: val.series[i].name,
  556. type: "line",
  557. data: val.series[i].data,
  558. width: optionsSetup.lineWidth,
  559. symbol: optionsSetup.symbol,
  560. showSymbol: optionsSetup.markPoint,
  561. symbolSize: optionsSetup.pointSize,
  562. symbolColor: arrColor[i],
  563. smooth: optionsSetup.smoothCurve,
  564. // 线条
  565. lineStyle: {
  566. color: arrColor[i],
  567. width: optionsSetup.lineWidth,
  568. },
  569. //点
  570. itemStyle: {
  571. color: arrColor[i],
  572. },
  573. areaStyle: this.getOptionArea(),
  574. // 数值设定
  575. label: {
  576. show: optionsSetup.isShow,
  577. position: optionsSetup.fontPosition,
  578. distance: optionsSetup.fontDistance,
  579. fontSize: optionsSetup.fontSize,
  580. color: optionsSetup.fontColor,
  581. fontWeight: optionsSetup.fontWeight,
  582. formatter: !!optionsSetup.percentSign ? '{c}%' : '{c}',
  583. fontStyle: optionsSetup.fontStyle,
  584. fontFamily: optionsSetup.fontFamily,
  585. },
  586. });
  587. }
  588. legendName.push(val.series[i].name);
  589. }
  590. // 根据图表的宽度 x轴的字体大小、长度来估算X轴的label能展示多少个字
  591. const xAxisDataLength = val.length !== 0 ? val.xAxis.length : 1;
  592. const rowsNum = optionsSetup.textRowsNum !== "" ? optionsSetup.textRowsNum : parseInt((this.optionsStyle.width / xAxisDataLength) / optionsSetup.textFontSizeX);
  593. const axisLabel = {
  594. show: optionsSetup.isShowAxisLabelX,
  595. interval: optionsSetup.textIntervalX,
  596. // 文字角度
  597. rotate: optionsSetup.textAngleX,
  598. textStyle: {
  599. // 坐标文字颜色
  600. color: optionsSetup.textColorX,
  601. fontSize: optionsSetup.textFontSizeX,
  602. fontWeight: optionsSetup.textFontWeightX,
  603. fontStyle: optionsSetup.textFontStyleX,
  604. fontFamily: optionsSetup.textFontFamilyX,
  605. },
  606. // 自动换行
  607. formatter: function (value, index) {
  608. const strs = value.split('');
  609. let str = ''
  610. for (let i = 0, s; s = strs[i++];) {
  611. str += s;
  612. if (!(i % rowsNum)) str += '\n';
  613. }
  614. return str
  615. }
  616. }
  617. if (optionsSetup.textRowsBreakAuto) {
  618. this.options.xAxis.axisLabel = axisLabel;
  619. }
  620. this.options.series = series;
  621. this.options.legend["data"] = legendName;
  622. this.setOptionsLegendName(legendName);
  623. this.vChartVisible = false
  624. this.$nextTick(() => {
  625. this.vChartVisible = true
  626. })
  627. },
  628. },
  629. };
  630. </script>
  631. <style scoped lang="scss">
  632. .echarts {
  633. width: 100%;
  634. height: 100%;
  635. overflow: hidden;
  636. }
  637. </style>