widgetBarStackChart.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. <template>
  2. <div :style="styleObj">
  3. <v-chart ref="myVChart" :options="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. title.text = optionsSetup.titleText;
  113. title.show = optionsSetup.isNoTitle;
  114. title.left = optionsSetup.textAlign;
  115. title.textStyle = {
  116. color: optionsSetup.textColor,
  117. fontSize: optionsSetup.textFontSize,
  118. fontWeight: optionsSetup.textFontWeight,
  119. fontStyle: optionsSetup.textFontStyle,
  120. };
  121. title.subtext = optionsSetup.subText;
  122. title.subtextStyle = {
  123. color: optionsSetup.subTextColor,
  124. fontWeight: optionsSetup.subTextFontWeight,
  125. fontSize: optionsSetup.subTextFontSize,
  126. fontStyle: optionsSetup.subTextFontStyle,
  127. };
  128. this.options.title = title;
  129. },
  130. // X轴设置
  131. setOptionsX() {
  132. const optionsSetup = this.optionsSetup;
  133. const xAxis = {
  134. type: "category",
  135. // 坐标轴是否显示
  136. show: optionsSetup.hideX,
  137. // 坐标轴名称
  138. name: optionsSetup.nameX,
  139. nameTextStyle: {
  140. color: optionsSetup.nameColorX,
  141. fontSize: optionsSetup.nameFontSizeX,
  142. },
  143. // 轴反转
  144. inverse: optionsSetup.reversalX,
  145. axisLabel: {
  146. show: true,
  147. interval: optionsSetup.textInterval,
  148. // 文字角度
  149. rotate: optionsSetup.textAngleX,
  150. textStyle: {
  151. // 坐标文字颜色
  152. color: optionsSetup.colorX,
  153. fontSize: optionsSetup.fontSizeX,
  154. },
  155. },
  156. axisLine: {
  157. show: true,
  158. lineStyle: {
  159. color: optionsSetup.lineColorX,
  160. width: optionsSetup.lineWidthX,
  161. },
  162. },
  163. splitLine: {
  164. show: optionsSetup.isShowSplitLineX,
  165. lineStyle: {
  166. color: optionsSetup.splitLineColorX,
  167. width: optionsSetup.splitLineWidthX,
  168. },
  169. },
  170. };
  171. this.options.xAxis = xAxis;
  172. },
  173. // Y轴设置
  174. setOptionsY() {
  175. const optionsSetup = this.optionsSetup;
  176. const yAxis = {
  177. max: optionsSetup.maxY !== "" ? optionsSetup.maxY : null,
  178. type: "value",
  179. scale: optionsSetup.scale,
  180. // 均分
  181. splitNumber: optionsSetup.splitNumberY,
  182. // 坐标轴是否显示
  183. show: optionsSetup.isShowY,
  184. // 坐标轴名称
  185. name: optionsSetup.textNameY,
  186. nameTextStyle: {
  187. color: optionsSetup.nameColorY,
  188. fontSize: optionsSetup.nameFontSizeY,
  189. },
  190. // 轴反转
  191. inverse: optionsSetup.reversalY,
  192. axisLabel: {
  193. show: true,
  194. // 文字角度
  195. rotate: optionsSetup.textAngleY,
  196. textStyle: {
  197. // 坐标文字颜色
  198. color: optionsSetup.colorY,
  199. fontSize: optionsSetup.fontSizeY,
  200. },
  201. },
  202. axisLine: {
  203. show: true,
  204. lineStyle: {
  205. color: optionsSetup.lineColorY,
  206. width: optionsSetup.lineWidthY,
  207. },
  208. },
  209. splitLine: {
  210. show: optionsSetup.isShowSplitLineY,
  211. lineStyle: {
  212. color: optionsSetup.splitLineColorY,
  213. width: optionsSetup.splitLineWidthY,
  214. },
  215. },
  216. };
  217. this.options.yAxis = yAxis;
  218. },
  219. // tooltip 提示语设置,鼠标放置显示
  220. setOptionsTooltip() {
  221. const optionsSetup = this.optionsSetup;
  222. const tooltip = {
  223. show: optionsSetup.isShowTooltip,
  224. trigger: optionsSetup.tooltipTrigger,
  225. axisPointer: {
  226. type: optionsSetup.tooltipAxisPointerType,
  227. },
  228. textStyle: {
  229. color: optionsSetup.tipsColor,
  230. fontSize: optionsSetup.tipsFontSize,
  231. },
  232. };
  233. this.options.tooltip = tooltip;
  234. },
  235. // 边距设置
  236. setOptionsMargin() {
  237. const optionsSetup = this.optionsSetup;
  238. const grid = {
  239. left: optionsSetup.marginLeft,
  240. right: optionsSetup.marginRight,
  241. bottom: optionsSetup.marginBottom,
  242. top: optionsSetup.marginTop,
  243. containLabel: true,
  244. };
  245. this.options.grid = grid;
  246. },
  247. // 图例操作 legend
  248. setOptionsLegend() {
  249. const optionsSetup = this.optionsSetup;
  250. const legend = this.options.legend;
  251. legend.show = optionsSetup.isShowLegend;
  252. legend.left = optionsSetup.lateralPosition;
  253. legend.top = optionsSetup.longitudinalPosition;
  254. legend.bottom = optionsSetup.longitudinalPosition;
  255. legend.orient = optionsSetup.layoutFront;
  256. legend.textStyle = {
  257. color: optionsSetup.legendColor,
  258. fontSize: optionsSetup.legendFontSize,
  259. };
  260. legend.itemWidth = optionsSetup.legendWidth;
  261. },
  262. // 图例名称设置
  263. setOptionsLegendName(name) {
  264. const optionsSetup = this.optionsSetup;
  265. const series = this.options.series;
  266. const legendName = optionsSetup.legendName;
  267. // 图例没有手动写则显示原值,写了则显示新值
  268. if (null == legendName || legendName == "") {
  269. for (let i = 0; i < name.length; i++) {
  270. series[i].name = name[i];
  271. }
  272. this.options.legend["data"] = name;
  273. } else {
  274. const arr = legendName.split("|");
  275. for (let i = 0; i < arr.length; i++) {
  276. series[i].name = arr[i];
  277. }
  278. this.options.legend["data"] = arr;
  279. }
  280. },
  281. // 数据解析
  282. setOptionsData(e, paramsConfig) {
  283. const optionsSetup = this.optionsSetup;
  284. // 数据类型 静态 or 动态
  285. const optionsData = this.optionsData;
  286. // 联动接收者逻辑开始
  287. optionsData.dynamicData = optionsData.dynamicData || {}; // 兼容 dynamicData undefined
  288. const myDynamicData = optionsData.dynamicData;
  289. clearInterval(this.flagInter); // 不管咋,先干掉上一次的定时任务,避免多跑
  290. if (
  291. e &&
  292. optionsData.dataType !== "staticData" &&
  293. Object.keys(myDynamicData.contextData).length
  294. ) {
  295. const keyArr = Object.keys(myDynamicData.contextData);
  296. paramsConfig.forEach((conf) => {
  297. if (keyArr.includes(conf.targetKey)) {
  298. myDynamicData.contextData[conf.targetKey] = e[conf.originKey];
  299. }
  300. });
  301. }
  302. // 联动接收者逻辑结束
  303. optionsData.dataType == "staticData"
  304. ? this.staticDataFn(optionsData.staticData, optionsSetup)
  305. : this.dynamicDataFn(
  306. optionsData.dynamicData,
  307. optionsData.refreshTime,
  308. optionsSetup
  309. );
  310. },
  311. //去重
  312. setUnique(arr) {
  313. let newArr = [];
  314. arr.forEach((item) => {
  315. return newArr.includes(item) ? "" : newArr.push(item);
  316. });
  317. return newArr;
  318. },
  319. //获取堆叠样式
  320. getStackStyle() {
  321. const optionsSetup = this.optionsSetup;
  322. let style = "";
  323. if (optionsSetup.stackStyle == "upDown") {
  324. style = "total";
  325. }
  326. return style;
  327. },
  328. //静态数据
  329. staticDataFn(val) {
  330. const optionsSetup = this.optionsSetup;
  331. //颜色
  332. const customColor = optionsSetup.customColor;
  333. const arrColor = [];
  334. for (let i = 0; i < customColor.length; i++) {
  335. arrColor.push(customColor[i].color);
  336. }
  337. //数据
  338. const series = [];
  339. let xAxisList = [];
  340. let yAxisList = [];
  341. const legendName = [];
  342. for (const i in val) {
  343. xAxisList[i] = val[i].axis;
  344. yAxisList[i] = val[i].name;
  345. }
  346. xAxisList = this.setUnique(xAxisList);
  347. yAxisList = this.setUnique(yAxisList);
  348. for (const i in yAxisList) {
  349. const data = new Array(xAxisList.length).fill(0);
  350. for (const j in xAxisList) {
  351. for (const k in val) {
  352. if (val[k].name == yAxisList[i]) {
  353. if (val[k].axis == xAxisList[j]) {
  354. data[j] = val[k].data;
  355. }
  356. }
  357. }
  358. }
  359. series.push({
  360. name: yAxisList[i],
  361. type: "bar",
  362. data: data,
  363. barGap: optionsSetup.barGap + "%",
  364. barMinHeight: optionsSetup.minHeight,
  365. stack: this.getStackStyle(),
  366. barWidth: optionsSetup.maxWidth,
  367. label: {
  368. show: optionsSetup.isShow,
  369. position: optionsSetup.fontPosition,
  370. distance: optionsSetup.fontDistance,
  371. fontSize: optionsSetup.fontSize,
  372. color: optionsSetup.dataColor,
  373. fontWeight: optionsSetup.fontWeight,
  374. formatter: !!optionsSetup.percentSign ? '{c}%' : '{c}'
  375. },
  376. //颜色,圆角属性
  377. itemStyle: {
  378. normal: {
  379. color: arrColor[i],
  380. barBorderRadius: optionsSetup.radius,
  381. },
  382. },
  383. //柱体背景属性
  384. showBackground: optionsSetup.isShowBackground,
  385. backgroundStyle: {
  386. color: optionsSetup.backgroundStyleColor,
  387. borderColor: optionsSetup.backgroundStyleBorderColor,
  388. borderWidth: optionsSetup.backgroundStyleBorderWidth,
  389. borderType: optionsSetup.backgroundStyleBorderType,
  390. shadowBlur: optionsSetup.backgroundStyleShadowBlur,
  391. shadowColor: optionsSetup.backgroundStyleShadowColor,
  392. opacity: optionsSetup.backgroundStyleOpacity / 100,
  393. }
  394. });
  395. legendName.push(yAxisList[i]);
  396. }
  397. this.options.series = series;
  398. // 根据图表的宽度 x轴的字体大小、长度来估算X轴的label能展示多少个字
  399. const rowsNum = optionsSetup.textRowsNum !== "" ? optionsSetup.textRowsNum : parseInt((this.optionsStyle.width / xAxisList.length) / optionsSetup.fontSizeX);
  400. const axisLabel = {
  401. show: true,
  402. interval: optionsSetup.textInterval,
  403. // 文字角度
  404. rotate: optionsSetup.textAngleX,
  405. textStyle: {
  406. // 坐标文字颜色
  407. color: optionsSetup.colorX,
  408. fontSize: optionsSetup.fontSizeX,
  409. },
  410. // 自动换行
  411. formatter: function (value, index) {
  412. const strs = value.split('');
  413. let str = ''
  414. for (let i = 0, s; s = strs[i++];) {
  415. str += s;
  416. if (!(i % rowsNum)) str += '\n';
  417. }
  418. return str
  419. }
  420. }
  421. if (optionsSetup.verticalShow) {
  422. this.options.xAxis.data = [];
  423. this.options.yAxis.data = xAxisList;
  424. this.options.xAxis.type = "value";
  425. this.options.yAxis.type = "category";
  426. } else {
  427. this.options.xAxis.data = xAxisList;
  428. this.options.yAxis.data = [];
  429. this.options.xAxis.type = "category";
  430. this.options.yAxis.type = "value";
  431. if (optionsSetup.textRowsBreakAuto) {
  432. this.options.xAxis.axisLabel = axisLabel;
  433. }
  434. }
  435. this.options.legend["data"] = legendName;
  436. this.setOptionsLegendName(legendName);
  437. },
  438. // 动态数据
  439. dynamicDataFn(val, refreshTime, optionsSetup) {
  440. if (!val) return;
  441. if (this.ispreview) {
  442. this.getEchartData(val, optionsSetup);
  443. this.flagInter = setInterval(() => {
  444. this.getEchartData(val, optionsSetup);
  445. }, refreshTime);
  446. } else {
  447. this.getEchartData(val, optionsSetup);
  448. }
  449. },
  450. getEchartData(val, optionsSetup) {
  451. const data = this.queryEchartsData(val);
  452. data.then((res) => {
  453. this.renderingFn(optionsSetup, res);
  454. });
  455. },
  456. renderingFn(optionsSetup, val) {
  457. //颜色
  458. const customColor = optionsSetup.customColor;
  459. const arrColor = [];
  460. for (let i = 0; i < customColor.length; i++) {
  461. arrColor.push(customColor[i].color);
  462. }
  463. const series = [];
  464. const legendName = [];
  465. for (const i in val.series) {
  466. if (val.series[i].type == "bar") {
  467. series.push({
  468. name: val.series[i].name,
  469. type: "bar",
  470. data: val.series[i].data,
  471. barGap: optionsSetup.barGap + "%",
  472. barMinHeight: optionsSetup.minHeight,
  473. stack: this.getStackStyle(),
  474. barWidth: optionsSetup.maxWidth,
  475. label: {
  476. show: optionsSetup.isShow,
  477. position: optionsSetup.fontPosition,
  478. distance: optionsSetup.fontDistance,
  479. fontSize: optionsSetup.fontSize,
  480. color: optionsSetup.dataColor,
  481. fontWeight: optionsSetup.fontWeight,
  482. formatter: !!optionsSetup.percentSign ? '{c}%' : '{c}'
  483. },
  484. //颜色,圆角属性
  485. itemStyle: {
  486. normal: {
  487. color: arrColor[i],
  488. barBorderRadius: optionsSetup.radius,
  489. },
  490. },
  491. //柱体背景属性
  492. showBackground: optionsSetup.isShowBackground,
  493. backgroundStyle: {
  494. color: optionsSetup.backgroundStyleColor,
  495. borderColor: optionsSetup.backgroundStyleBorderColor,
  496. borderWidth: optionsSetup.backgroundStyleBorderWidth,
  497. borderType: optionsSetup.backgroundStyleBorderType,
  498. shadowBlur: optionsSetup.backgroundStyleShadowBlur,
  499. shadowColor: optionsSetup.backgroundStyleShadowColor,
  500. opacity: optionsSetup.backgroundStyleOpacity / 100,
  501. }
  502. });
  503. }
  504. legendName.push(val.series[i].name);
  505. }
  506. // 根据图表的宽度 x轴的字体大小、长度来估算X轴的label能展示多少个字
  507. const xAxisDataLength = val.length !== 0 ? val.xAxis.length : 1;
  508. const rowsNum = optionsSetup.textRowsNum !== "" ? optionsSetup.textRowsNum : parseInt((this.optionsStyle.width / xAxisDataLength) / optionsSetup.fontSizeX);
  509. const axisLabel = {
  510. show: true,
  511. interval: optionsSetup.textInterval,
  512. // 文字角度
  513. rotate: optionsSetup.textAngleX,
  514. textStyle: {
  515. // 坐标文字颜色
  516. color: optionsSetup.colorX,
  517. fontSize: optionsSetup.fontSizeX,
  518. },
  519. // 自动换行
  520. formatter: function (value, index) {
  521. const strs = value.split('');
  522. let str = ''
  523. for (let i = 0, s; s = strs[i++];) {
  524. str += s;
  525. if (!(i % rowsNum)) str += '\n';
  526. }
  527. return str
  528. }
  529. }
  530. // x轴
  531. if (optionsSetup.verticalShow) {
  532. this.options.xAxis.data = [];
  533. this.options.yAxis.data = val.xAxis;
  534. this.options.xAxis.type = "value";
  535. this.options.yAxis.type = "category";
  536. } else {
  537. this.options.xAxis.data = val.xAxis;
  538. this.options.yAxis.data = [];
  539. this.options.xAxis.type = "category";
  540. this.options.yAxis.type = "value";
  541. if (optionsSetup.textRowsBreakAuto) {
  542. this.options.xAxis.axisLabel = axisLabel;
  543. }
  544. }
  545. this.options.series = series;
  546. this.options.legend["data"] = legendName;
  547. this.setOptionsLegendName(legendName);
  548. },
  549. },
  550. };
  551. </script>
  552. <style scoped lang="scss">
  553. .echarts {
  554. width: 100%;
  555. height: 100%;
  556. overflow: hidden;
  557. }
  558. </style>