setup.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. import { ref, watch } from "vue";
  2. import { getTheme, ThemeEnum } from '@/common/config/theme';
  3. import { deepMerge } from '@/utils/objHandle'
  4. import { EChartsOption } from 'echarts';
  5. import moment from 'moment';
  6. // 命名待优化
  7. type Colors = {
  8. backgroundColor: string, // 图表背景颜色
  9. axisPointerLabelColor: string,
  10. legendTextColor: string,
  11. xAxisLineColor: string
  12. yAxisLineColor: string,
  13. seriesMarkLabelColor: string,
  14. seriesMarkLineColor: string,
  15. }
  16. // 图表数据
  17. export type ChartData = {
  18. datas: number[][], // 历史数据
  19. times: string[], // 历史日期
  20. ma5: (number | string)[], //均线数据
  21. ma10: (number | string)[], //均线数据
  22. ma15: (number | string)[], //均线数据
  23. dif: (number | string)[],
  24. dea: (number | string)[],
  25. macd: (number | string)[],
  26. }
  27. export function handleEchart() {
  28. const options = ref<EChartsOption>();
  29. // 当前主题
  30. const theme = getTheme();
  31. // 图表数据
  32. const chartData = ref<ChartData>({
  33. datas: [],
  34. times: [],
  35. ma5: [],
  36. ma10: [],
  37. ma15: [],
  38. dif: [],
  39. dea: [],
  40. macd: []
  41. });
  42. // 图表当前指示数据
  43. let chartDataIndex = 0;
  44. // 初始化图表配置
  45. const initOptions = () => {
  46. const { datas, times, ma5, ma10, ma15, dea, dif, macd } = chartData.value;
  47. const option: EChartsOption = {
  48. axisPointer: {
  49. link: [
  50. {
  51. xAxisIndex: 'all'
  52. }
  53. ],
  54. },
  55. legend: [
  56. {
  57. //图例控件,点击图例控制哪些系列不显示
  58. type: 'scroll',
  59. data: ['K线', 'MA5', 'MA10', 'MA15'],
  60. itemWidth: 14,
  61. itemHeight: 2,
  62. left: '5%',
  63. top: 0,
  64. textStyle: {
  65. fontSize: 12,
  66. },
  67. },
  68. {
  69. //图例控件,点击图例控制哪些系列不显示
  70. type: 'scroll',
  71. data: ['MACD', 'DIF', 'DEA'],
  72. itemWidth: 14,
  73. itemHeight: 2,
  74. left: '5%',
  75. top: '70%',
  76. textStyle: {
  77. fontSize: 12,
  78. },
  79. },
  80. ],
  81. // 悬浮框
  82. tooltip: {
  83. trigger: 'axis',
  84. axisPointer: {
  85. type: 'cross',
  86. },
  87. backgroundColor: 'rgba(255,255,255,.95)',
  88. borderWidth: 1,
  89. borderRadius: 3,
  90. textStyle: {
  91. color: '#4d535c',
  92. },
  93. className: 'tooltip',
  94. formatter: (params: any) => {
  95. let result = '';
  96. params.forEach((item: any) => {
  97. if (item.seriesType === 'candlestick') {
  98. result += '<div class="tooltip-title">' + moment(item.name).format('YYYY/MM/DD') + '</div>';
  99. item.value.forEach((data: number[], i: number) => {
  100. if (i > 0) {
  101. result += '<div class="tooltip-item"><span><i style="background-color:' + item.color + ';"></i>';
  102. switch (i) {
  103. case 1:
  104. result += '开盘';
  105. break;
  106. case 2:
  107. result += '收盘';
  108. break;
  109. case 3:
  110. result += '最低';
  111. break;
  112. case 4:
  113. result += '最高';
  114. break;
  115. }
  116. result += '</span><span>' + data + '</span></div>';
  117. }
  118. })
  119. } else if (item.seriesType === 'line') {
  120. result += '<div class="tooltip-item"><span><i style="background-color:' + item.color + ';"></i>' + item.seriesName + '</span><span>' + item.value + '</span></div>';
  121. }
  122. })
  123. return result;
  124. },
  125. },
  126. grid: [
  127. // K线
  128. {
  129. top: '8%',
  130. left: '8%',
  131. right: '8%',
  132. height: '55%',
  133. },
  134. // MACD
  135. {
  136. top: '75%',
  137. left: '8%',
  138. right: '8%',
  139. height: '20%',
  140. }
  141. ],
  142. xAxis: [
  143. {
  144. type: 'category',
  145. // X轴时间线
  146. data: times,
  147. axisLabel: {
  148. formatter: (val: any) => moment(val).format('YYYY/MM/DD'),
  149. },
  150. splitLine: {
  151. // 坐标分隔线
  152. show: true,
  153. },
  154. },
  155. {
  156. type: 'category',
  157. // X轴时间线
  158. data: times,
  159. gridIndex: 1,
  160. axisLabel: {
  161. show: false,
  162. },
  163. splitLine: {
  164. // 坐标分隔线
  165. show: true,
  166. },
  167. }
  168. ],
  169. yAxis: [
  170. {
  171. scale: true,
  172. },
  173. {
  174. scale: true,
  175. gridIndex: 1,
  176. }
  177. ],
  178. dataZoom: [
  179. {
  180. type: 'inside',
  181. xAxisIndex: [0, 1],
  182. // 起始显示K线条数(最新200条)
  183. startValue: times.length - 200,
  184. endValue: times.length,
  185. // 限制窗口缩放显示最少数据条数
  186. minValueSpan: 30,
  187. },
  188. {
  189. show: false,
  190. type: 'slider',
  191. xAxisIndex: [0, 1],
  192. },
  193. ],
  194. series: [
  195. {
  196. name: 'K线',
  197. type: 'candlestick',
  198. // Y轴数据
  199. data: datas,
  200. markLine: {
  201. animation: false,
  202. // 标线两端图标
  203. symbol: 'none',
  204. // 标线标签样式
  205. label: {
  206. fontWeight: 'bold',
  207. position: 'end',
  208. },
  209. // 标线样式
  210. lineStyle: {
  211. type: 'dashed',
  212. },
  213. data: [
  214. {
  215. // 最新价
  216. yAxis: datas[datas.length - 1][1],
  217. },
  218. ],
  219. },
  220. },
  221. {
  222. name: 'MA5',
  223. type: 'line',
  224. sampling: 'average', //折线图在数据量远大于像素点时候的降采样策略,开启后可以有效的优化图表的绘制效率
  225. data: ma5,
  226. smooth: true,
  227. symbol: 'none',
  228. lineStyle: {
  229. width: 1,
  230. opacity: 0.8,
  231. },
  232. },
  233. {
  234. name: 'MA10',
  235. type: 'line',
  236. sampling: 'average',
  237. data: ma10,
  238. smooth: true,
  239. symbol: 'none',
  240. lineStyle: {
  241. width: 1,
  242. opacity: 0.8,
  243. },
  244. },
  245. {
  246. name: 'MA15',
  247. type: 'line',
  248. sampling: 'average',
  249. data: ma15,
  250. smooth: true,
  251. symbol: 'none',
  252. lineStyle: {
  253. width: 1,
  254. opacity: 0.8,
  255. },
  256. },
  257. {
  258. name: 'MACD',
  259. type: 'bar',
  260. sampling: 'average',
  261. data: macd,
  262. xAxisIndex: 1,
  263. yAxisIndex: 1,
  264. barWidth: '20%',
  265. },
  266. {
  267. name: 'DIF',
  268. type: 'line',
  269. sampling: 'average',
  270. data: dif,
  271. xAxisIndex: 1,
  272. yAxisIndex: 1,
  273. smooth: true,
  274. symbol: 'none',
  275. lineStyle: {
  276. width: 1,
  277. opacity: 0.8,
  278. },
  279. },
  280. {
  281. name: 'DEA',
  282. type: 'line',
  283. sampling: 'average',
  284. data: dea,
  285. xAxisIndex: 1,
  286. yAxisIndex: 1,
  287. smooth: true,
  288. symbol: 'none',
  289. lineStyle: {
  290. width: 1,
  291. opacity: 0.8,
  292. },
  293. },
  294. ],
  295. };
  296. options.value = deepMerge(option, getColors(theme.value));
  297. };
  298. // 动态更新数据
  299. const updateOptions = () => {
  300. const { datas, ma5, ma10, ma15, macd, dif, dea } = chartData.value;
  301. if (datas.length) {
  302. options.value = {
  303. series: [
  304. {
  305. name: 'K线',
  306. data: datas,
  307. markLine: {
  308. data: [
  309. {
  310. // 最新价
  311. yAxis: datas[datas.length - 1][1],
  312. },
  313. ],
  314. },
  315. },
  316. {
  317. name: 'MA5',
  318. data: ma5,
  319. },
  320. {
  321. name: 'MA10',
  322. data: ma10,
  323. },
  324. {
  325. name: 'MA15',
  326. data: ma15,
  327. },
  328. {
  329. name: 'MACD',
  330. data: macd,
  331. },
  332. {
  333. name: 'DIF',
  334. data: dif,
  335. },
  336. {
  337. name: 'DEA',
  338. data: dea,
  339. },
  340. ],
  341. };
  342. }
  343. };
  344. // 设置图表样式
  345. const setColors = (colors: Colors): EChartsOption => {
  346. return {
  347. // 图表背景颜色
  348. backgroundColor: colors.backgroundColor,
  349. axisPointer: {
  350. label: {
  351. color: colors.axisPointerLabelColor,
  352. },
  353. },
  354. legend: [
  355. {
  356. textStyle: {
  357. color: colors.legendTextColor,
  358. },
  359. }
  360. ],
  361. xAxis: [
  362. {
  363. splitLine: {
  364. lineStyle: {
  365. // 坐标分隔线颜色
  366. color: colors.xAxisLineColor,
  367. },
  368. },
  369. },
  370. {
  371. splitLine: {
  372. lineStyle: {
  373. // 坐标分隔线颜色
  374. color: colors.xAxisLineColor,
  375. },
  376. },
  377. }
  378. ],
  379. yAxis: [
  380. {
  381. splitLine: {
  382. lineStyle: {
  383. // 坐标分隔线颜色
  384. color: colors.xAxisLineColor,
  385. },
  386. },
  387. },
  388. {
  389. splitLine: {
  390. lineStyle: {
  391. // 坐标分隔线颜色
  392. color: colors.xAxisLineColor,
  393. },
  394. },
  395. }
  396. ],
  397. series: [
  398. {
  399. name: 'K线',
  400. markLine: {
  401. // 标线标签样式
  402. label: {
  403. color: colors.seriesMarkLabelColor,
  404. },
  405. // 标线样式
  406. lineStyle: {
  407. color: colors.seriesMarkLineColor,
  408. },
  409. },
  410. },
  411. {
  412. name: 'MA5',
  413. },
  414. {
  415. name: 'MA10',
  416. },
  417. {
  418. name: 'MA15',
  419. },
  420. {
  421. name: 'MACD',
  422. itemStyle: {
  423. color: (params: any) => {
  424. if (params.data > 0) {
  425. return '#eb5454';
  426. } else {
  427. return '#47b262';
  428. }
  429. },
  430. }
  431. },
  432. ]
  433. }
  434. }
  435. // 获取图表样式配置
  436. const getColors = (theme: ThemeEnum) => {
  437. switch (theme) {
  438. case ThemeEnum.default:
  439. case ThemeEnum.dark:
  440. return setColors({
  441. backgroundColor: 'transparent',
  442. axisPointerLabelColor: '#fff',
  443. legendTextColor: '#0e99e2',
  444. xAxisLineColor: '#171B1D',
  445. yAxisLineColor: '#171B1D',
  446. seriesMarkLabelColor: '#3C454B',
  447. seriesMarkLineColor: '#33393D',
  448. });
  449. case ThemeEnum.light:
  450. return setColors({
  451. backgroundColor: 'transparent',
  452. axisPointerLabelColor: '#fff',
  453. legendTextColor: '#FC9618',
  454. xAxisLineColor: '#DAE5EC',
  455. yAxisLineColor: '#DAE5EC',
  456. seriesMarkLabelColor: '#ACB8C0',
  457. seriesMarkLineColor: '#ACB8C0',
  458. });
  459. }
  460. }
  461. watch(theme, (val) => {
  462. options.value = getColors(val);
  463. });
  464. return {
  465. chartData,
  466. options,
  467. initOptions,
  468. updateOptions,
  469. }
  470. }