widgetBarStackChart.vue 20 KB

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