index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. <template>
  2. <div class="new-line-single">
  3. <div :id="index" class="base">堆叠折线图</div>
  4. <div
  5. class="zuan"
  6. v-show="config.zuanFlag && config.zuanLevel > 0"
  7. :style="`color:${option.zuanFontColor}`"
  8. >
  9. <i
  10. class="el-icon-back"
  11. @click="onZuanBack"
  12. style="cursor: pointer; margin-right: 10px"
  13. >
  14. </i>
  15. {{ config.zuanStack.join(` ${option.zuanFenge} `) }}
  16. </div>
  17. </div>
  18. </template>
  19. <script>
  20. import {
  21. countLabel,
  22. countLine,
  23. grid,
  24. legend,
  25. title,
  26. countLegendSelect,
  27. countXlabel,
  28. } from "../../newStyles/styleCommon";
  29. export default {
  30. name: "new-line-single",
  31. props: ["option", "config", "modalSet", "index", "site"],
  32. data() {
  33. return {
  34. originData: [],
  35. chartOption: {
  36. tooltip: {
  37. trigger: "axis",
  38. axisPointer: { type: "shadow" },
  39. },
  40. legend: { data: [] },
  41. grid: { left: "3%", right: "3%", bottom: "3%", containLabel: true },
  42. xAxis: {
  43. type: "category",
  44. data: [],
  45. },
  46. yAxis: [{ type: "value" }],
  47. series: [],
  48. },
  49. chart: null,
  50. };
  51. },
  52. watch: {
  53. site: {
  54. handler() {
  55. if (this.chart) {
  56. this.chart.resize();
  57. }
  58. },
  59. deep: true,
  60. },
  61. option: {
  62. handler() {
  63. if (this.originData.length) {
  64. this.refresh();
  65. }
  66. },
  67. deep: true,
  68. },
  69. },
  70. methods: {
  71. handleClick(event) {
  72. if (this.config.zuanLevel + 1 >= this.config.zuan.length) {
  73. return this.$message({
  74. type: "warning",
  75. message: "已经钻取到最后一层了",
  76. });
  77. }
  78. let key = this.modalSet.find((i) => i.zuan).key;
  79. let index = this.config[key].findIndex((i) => i.zuan);
  80. this.config.zuanStack.push(event.data.name[index]);
  81. this.config.zuanLevel += 1;
  82. this.$emit("preview");
  83. },
  84. onZuanBack() {
  85. if (this.config.zuanLevel <= 0) return;
  86. this.config.zuanLevel -= 1;
  87. this.config.zuanStack = this.config.zuanStack.slice(
  88. 0,
  89. this.config.zuanLevel
  90. );
  91. this.$emit("preview");
  92. },
  93. refresh() {
  94. if (this.config.serie.length) {
  95. this.refreshSingle();
  96. } else {
  97. this.refreshNormal();
  98. }
  99. },
  100. refreshNormal() {
  101. let { dimension, measure } = this.config;
  102. dimension = [...dimension].filter((i) => !i.extJson.hidden);
  103. measure = measure.filter((i) => !i.extJson.hidden);
  104. if (this.config.zuanFlag) {
  105. let zuanField = this.config.zuan[this.config.zuanLevel];
  106. let zuanIndex = dimension.findIndex((i) => i.zuan);
  107. dimension.splice(zuanIndex, 1, zuanField);
  108. }
  109. let clearData = this.originData.map((i) => {
  110. let item = { key: [], value: [] };
  111. dimension.reduce((r, d) => {
  112. r.key.push(i[d.columnTitle]);
  113. return r;
  114. }, item);
  115. measure.reduce((r, d) => {
  116. r.value.push(parseFloat(i[d.columnTitle] || 0));
  117. return r;
  118. }, item);
  119. return item;
  120. });
  121. this.chartOption.grid = grid(this.option);
  122. //标题设置
  123. if (this.option.isTitleShow) {
  124. this.chartOption.title = title(this.option);
  125. }
  126. // 图例设置
  127. this.chartOption.legend = legend(this.option);
  128. let chartOption = { ...this.chartOption };
  129. let xAxisData = clearData.map((i) => i.key);
  130. chartOption.xAxis.data = xAxisData.map((i) => i.join("-"));
  131. let legengData = [];
  132. let seriesLabel = countLabel(this.option);
  133. chartOption.series = measure.map((m, i) => {
  134. legengData.push(m.columnTitle);
  135. let serie = {
  136. name: m.columnTitle,
  137. type: "line",
  138. data: clearData.map((d, index) => ({
  139. value: d.value[i],
  140. name: xAxisData[index],
  141. })),
  142. label: seriesLabel,
  143. itemStyle: {},
  144. };
  145. return serie;
  146. });
  147. chartOption.legend.data = legengData;
  148. chartOption.xAxis.axisLabel = countXlabel(this.option);
  149. this.setYlabel(chartOption);
  150. this.setSerieColor(chartOption);
  151. countLegendSelect(this.option, chartOption);
  152. this.chart = echarts.init(document.getElementById(this.index));
  153. this.chart.off("click", this.handleClick);
  154. if (this.config.zuanFlag) {
  155. this.chart.on("click", this.handleClick);
  156. }
  157. this.chart.setOption(chartOption, true);
  158. },
  159. refreshSingle() {
  160. let { dimension, measure, serie } = this.config;
  161. dimension = [...dimension].filter((i) => !i.extJson.hidden);
  162. measure = measure.filter((i) => !i.extJson.hidden);
  163. serie = serie.filter((i) => !i.extJson.hidden);
  164. if (this.config.zuanFlag) {
  165. let zuanField = this.config.zuan[this.config.zuanLevel];
  166. let zuanIndex = dimension.findIndex((i) => i.zuan);
  167. dimension.splice(zuanIndex, 1, zuanField);
  168. }
  169. let { stackSwitch } = this.option;
  170. this.chartOption.grid = grid(this.option);
  171. if (this.option.isTitleShow) {
  172. this.chartOption.title = title(this.option);
  173. }
  174. this.chartOption.legend = legend(this.option);
  175. let xAxisData = [];
  176. let legendData = [];
  177. let valueKey = measure[0].columnTitle;
  178. let serieKey = serie[0].columnTitle;
  179. let clearData = this.originData.map((i) => {
  180. let item = { key: "", value: "", label: "" };
  181. let keyArr = dimension.map((d) => i[d.columnTitle]);
  182. let key = keyArr.join("-");
  183. item.keyArr = keyArr;
  184. item.key = key;
  185. if (!xAxisData.includes(key)) {
  186. xAxisData.push(key);
  187. }
  188. if (!legendData.includes(i[serieKey])) {
  189. legendData.push(i[serieKey]);
  190. }
  191. item.value = i[valueKey];
  192. item.label = i[serieKey];
  193. return item;
  194. });
  195. let chartOption = { ...this.chartOption };
  196. chartOption.xAxis.data = xAxisData;
  197. let seriesLabel = countLabel(this.option);
  198. let seriesLine = countLine(this.option);
  199. chartOption.series = legendData.map((name) => {
  200. let serieItem = {
  201. name,
  202. data: xAxisData.map((i) => {
  203. let item = clearData.find((j) => j.key == i && j.label == name);
  204. let nameItem = clearData.find((j) => j.key == i);
  205. let value = item ? parseFloat(item.value) : 0;
  206. return { value, name: nameItem.keyArr };
  207. }),
  208. type: "line",
  209. label: seriesLabel,
  210. itemStyle: {},
  211. lineStyle: seriesLine,
  212. smooth: this.option.lineSmooth,
  213. };
  214. if (stackSwitch) {
  215. serieItem.areaStyle = {};
  216. }
  217. return serieItem;
  218. });
  219. if (stackSwitch) {
  220. chartOption.series.forEach((item) => (item.stack = "stack"));
  221. }
  222. chartOption.legend.data = legendData;
  223. chartOption.xAxis.axisLabel = countXlabel(this.option);
  224. this.setYlabel(chartOption);
  225. this.setSerieColor(chartOption);
  226. countLegendSelect(this.option, chartOption);
  227. this.chart = echarts.init(document.getElementById(this.index));
  228. this.chart.off("click", this.handleClick);
  229. if (this.config.zuanFlag) {
  230. this.chart.on("click", this.handleClick);
  231. }
  232. this.chart.setOption(chartOption, true);
  233. },
  234. // y轴设置
  235. setYlabel(chartOption) {
  236. let { ylabelFontSize, ylabelColor } = this.option;
  237. chartOption.yAxis[0].axisLabel = {
  238. fontSize: ylabelFontSize,
  239. color: ylabelColor,
  240. };
  241. },
  242. // 设置系列颜色
  243. setSerieColor(chartOption) {
  244. let list = this.option.colorList.slice(0, chartOption.series.length);
  245. list.forEach(({ color1, color2, direction }, index) => {
  246. direction = direction || "top";
  247. if (color1 && color2) {
  248. chartOption.series[index].itemStyle.color = {
  249. x: direction == "left" ? 1 : 0,
  250. y: direction == "top" ? 1 : 0,
  251. x2: direction == "right" ? 1 : 0,
  252. y2: direction == "bottom" ? 1 : 0,
  253. colorStops: [
  254. { offset: 0, color: color1 },
  255. { offset: 1, color: color2 },
  256. ],
  257. };
  258. } else if (color1 && !color2) {
  259. chartOption.series[index].itemStyle.color = color1;
  260. }
  261. });
  262. },
  263. update(data) {
  264. this.originData = data;
  265. this.refresh();
  266. },
  267. initRender() {
  268. let option = {
  269. tooltip: { trigger: "axis", axisPointer: { type: "shadow" } },
  270. legend: { data: ["大", "小", "中"] },
  271. grid: { left: "3%", right: "3%", bottom: "3%", containLabel: true },
  272. xAxis: {
  273. type: "category",
  274. data: ["老年", "青年", "少年", "中年"],
  275. axisLabel: {
  276. fontSize: 12,
  277. color: "#333333",
  278. interval: "0",
  279. rotate: 0,
  280. },
  281. },
  282. yAxis: [
  283. { type: "value", axisLabel: { fontSize: 12, color: "#333333" } },
  284. ],
  285. series: [
  286. {
  287. name: "大",
  288. data: [
  289. { value: 2614, name: ["老年"] },
  290. { value: 5480, name: ["青年"] },
  291. { value: 2771, name: ["少年"] },
  292. { value: 2578, name: ["中年"] },
  293. ],
  294. type: "line",
  295. barWidth: 20,
  296. label: {
  297. show: true,
  298. color: "#333333",
  299. fontSize: 12,
  300. position: "inside",
  301. textBorderWidth: 0,
  302. textBorderColor: "#ffffff",
  303. rotate: 0,
  304. },
  305. itemStyle: { borderRadius: 0 },
  306. areaStyle: {},
  307. stack: "stack",
  308. },
  309. {
  310. name: "小",
  311. data: [
  312. { value: 2570, name: ["老年"] },
  313. { value: 5605, name: ["青年"] },
  314. { value: 2906, name: ["少年"] },
  315. { value: 2766, name: ["中年"] },
  316. ],
  317. type: "line",
  318. label: {
  319. show: true,
  320. color: "#333333",
  321. fontSize: 12,
  322. position: "inside",
  323. textBorderWidth: 0,
  324. textBorderColor: "#ffffff",
  325. rotate: 0,
  326. },
  327. itemStyle: { borderRadius: 0 },
  328. areaStyle: {},
  329. stack: "stack",
  330. },
  331. {
  332. name: "中",
  333. data: [
  334. { value: 5066, name: ["老年"] },
  335. { value: 11154, name: ["青年"] },
  336. { value: 5371, name: ["少年"] },
  337. { value: 5364, name: ["中年"] },
  338. ],
  339. type: "line",
  340. barWidth: 20,
  341. label: {
  342. show: true,
  343. color: "#333333",
  344. fontSize: 12,
  345. position: "inside",
  346. textBorderWidth: 0,
  347. textBorderColor: "#ffffff",
  348. rotate: 0,
  349. },
  350. itemStyle: { borderRadius: 0 },
  351. areaStyle: {},
  352. stack: "stack",
  353. },
  354. ],
  355. };
  356. this.chart = echarts.init(document.getElementById(this.index));
  357. this.chart.setOption(option, true);
  358. this.$nextTick(this.chart.resize);
  359. },
  360. },
  361. };
  362. </script>
  363. <style lang="scss">
  364. .new-line-single {
  365. .base {
  366. width: 100%;
  367. height: calc(100% - 20px);
  368. display: flex;
  369. justify-content: center;
  370. align-items: center;
  371. }
  372. .zuan {
  373. position: absolute;
  374. left: 0;
  375. bottom: 0;
  376. width: 100%;
  377. line-height: 20px;
  378. padding: 0 10px;
  379. font-size: 12px;
  380. }
  381. }
  382. </style>