index.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. import {
  2. defineComponent,
  3. nextTick,
  4. onMounted,
  5. PropType,
  6. ref,
  7. shallowRef,
  8. watch
  9. } from 'vue'
  10. import styles from './index.module.less'
  11. import * as echarts from 'echarts/core'
  12. import {
  13. LineChart
  14. // LineSeriesOption
  15. } from 'echarts/charts'
  16. // import { PieChart } from 'echarts/charts'
  17. import {
  18. TitleComponent,
  19. // 组件类型的定义后缀都为 ComponentOption
  20. // TitleComponentOption,
  21. TooltipComponent,
  22. // TooltipComponentOption,
  23. GridComponent,
  24. // 数据集组件
  25. DatasetComponent,
  26. // DatasetComponentOption,
  27. // 内置数据转换器组件 (filter, sort)
  28. // TransformComponent,
  29. LegendComponent,
  30. ToolboxComponent,
  31. DataZoomComponent
  32. } from 'echarts/components'
  33. import { LabelLayout } from 'echarts/features'
  34. import { CanvasRenderer } from 'echarts/renderers'
  35. import { formatSecToHMS } from '..'
  36. import { boxShadow } from 'html2canvas/dist/types/css/property-descriptors/box-shadow'
  37. // 注册必须的组件
  38. echarts.use([
  39. TitleComponent,
  40. TooltipComponent,
  41. GridComponent,
  42. DatasetComponent,
  43. // TransformComponent,
  44. LabelLayout,
  45. // UniversalTransition,
  46. CanvasRenderer,
  47. // PieChart,
  48. ToolboxComponent,
  49. LegendComponent,
  50. DataZoomComponent,
  51. LineChart
  52. ])
  53. const lineChartOption = (params: {
  54. xAxisData: any
  55. seriesData: any
  56. colors: {
  57. lineColor?: string
  58. startColor?: string
  59. endColor?: string
  60. unit?: string
  61. },
  62. splitNumber: number
  63. }) => {
  64. return {
  65. title: {
  66. text: '单位:' + (params.colors.unit || '人'),
  67. textStyle: {
  68. color: '#777777',
  69. fontSize: 13,
  70. fontWeight: 400
  71. }
  72. },
  73. legend: { show: false },
  74. emphasis: { lineStyle: { width: 2 } },
  75. xAxis: {
  76. boundaryGap: true,
  77. data: params.xAxisData,
  78. type: 'category',
  79. axisLine: { lineStyle: { color: '#8C8C8C' } },
  80. lineStyle: { color: '#F2F2F2' },
  81. axisLabel: {
  82. margin: 16,
  83. // alignWithLabel: true,
  84. // inside: false
  85. },
  86. axisTick: {
  87. show: false
  88. }
  89. },
  90. color: [
  91. params.colors.lineColor || '#2DC7AA'
  92. // '#FF6079'
  93. // '#2DC7AA',
  94. // '#FF602C',
  95. // '#91DD1C',
  96. // '#FFA92C',
  97. // '#BE7E2E',
  98. // '#1C96DD',
  99. // '#D22CFF',
  100. // '#FF3C3C',
  101. // '#1AEE3E',
  102. // '#00c9ff'
  103. ],
  104. series: [
  105. {
  106. lineStyle: { width: 2 },
  107. data: params.seriesData,
  108. symbol: 'circle',
  109. showSymbol: false,
  110. name: '购买次数',
  111. type: 'line',
  112. areaStyle: {
  113. color: {
  114. type: 'linear',
  115. x: 0,
  116. y: 0,
  117. x2: 0,
  118. y2: 1,
  119. colorStops: [
  120. {
  121. offset: 0,
  122. color: params.colors.startColor || 'rgba(45, 199, 170, 0.23)'
  123. // 0% 处的颜色
  124. },
  125. {
  126. offset: 1,
  127. // 100% 处的颜色
  128. color: params.colors.endColor || 'rgba(45, 199, 170, 0)'
  129. }
  130. ]
  131. }
  132. },
  133. emphasis: { lineStyle: { width: 2 } }
  134. }
  135. ],
  136. grid: {
  137. bottom: '3%',
  138. containLabel: true,
  139. left: '4%',
  140. right: '5%',
  141. top: '40'
  142. },
  143. tooltip: {
  144. trigger: 'axis',
  145. confine: true,
  146. formatter: function (params: any) {
  147. return params[0].name
  148. },
  149. position: function (point) {
  150. // 固定在顶部
  151. return [point[0], '10%']
  152. },
  153. backgroundColor: '#FF6079',
  154. boxShadow: 'none',
  155. borderWidth: 0,
  156. borderRadius: 24,
  157. padding: [1, 4],
  158. textStyle: {
  159. color: '#FFFFFF',
  160. fontSize: 12
  161. },
  162. extraCssText: 'z-index: 2;box-shadow: none;',
  163. axisPointer: {
  164. lineStyle: {
  165. color: '#FF6079'
  166. }
  167. }
  168. },
  169. yAxis: {
  170. type: 'value',
  171. splitLine: {
  172. axisLine: { lineStyle: { color: '#8C8C8C' } },
  173. lineStyle: { color: ['#f2f2f2'], type: 'dashed' }
  174. },
  175. splitNumber: params.splitNumber || 5
  176. },
  177. dataZoom: [{ type: 'inside', throttle: 100 }],
  178. toolbox: { feature: { saveAsImage: { show: false } } }
  179. }
  180. }
  181. export default defineComponent({
  182. name: 'eChats-model',
  183. props: {
  184. obj: {
  185. type: Object,
  186. default: () => ({})
  187. },
  188. type: {
  189. type: String as PropType<'TIME' | 'NUM'>,
  190. default: 'TIME'
  191. }
  192. },
  193. setup(props) {
  194. const chartId = 'eChart_' + Date.now() + props.type
  195. const color = props.type === 'NUM' ? '#FF955D' : '#2DC7AA'
  196. const statisticCounts = ref({
  197. time: '' as any,
  198. count: 0 as any
  199. })
  200. let myChart: echarts.ECharts
  201. nextTick(() => {
  202. myChart = echarts.init(document.getElementById(chartId) as HTMLDivElement)
  203. })
  204. const _initData = () => {
  205. nextTick(() => {
  206. statisticCounts.value.time = props.obj.time || ''
  207. statisticCounts.value.count =
  208. props.type === 'NUM'
  209. ? props.obj.count
  210. : formatSecToHMS(props.obj.count).all
  211. myChart.clear()
  212. lineChartOption &&
  213. myChart.setOption(
  214. lineChartOption({
  215. xAxisData: props.obj.xAxisData,
  216. seriesData: props.obj.yAxisData,
  217. colors: {
  218. lineColor: color,
  219. startColor:
  220. props.type === 'NUM'
  221. ? 'rgba(255, 149, 93, 0.23)'
  222. : 'rgba(45, 199, 170, 0.23)',
  223. endColor:
  224. props.type === 'NUM'
  225. ? 'rgba(255, 149, 93, 0)'
  226. : 'rgba(45, 199, 170, 0)',
  227. unit: props.type === 'NUM' ? '人' : '分钟'
  228. },
  229. splitNumber: props.obj.countMaxCount
  230. })
  231. )
  232. myChart.on('highlight', function (params: any) {
  233. const batch = params.batch || []
  234. // const options: any = myChart.getOption()
  235. batch.forEach((item: any) => {
  236. const batchIndex = item.dataIndex
  237. const count =
  238. props.type === 'NUM'
  239. ? props.obj.yAxisData[batchIndex]
  240. : formatSecToHMS(props.obj.yAxisData[batchIndex] || 0).all
  241. const time = props.obj.xAxisData[batchIndex]
  242. statisticCounts.value = {
  243. count,
  244. time
  245. }
  246. })
  247. })
  248. // 可能出现多个时,图表同时渲染,提示有问题
  249. setTimeout(() => {
  250. const lastIndex = props.obj.xAxisData.length - 1
  251. myChart.dispatchAction({
  252. type: 'showTip',
  253. seriesIndex: 0, // 系列索引
  254. dataIndex: lastIndex // 数据索引
  255. })
  256. }, 0)
  257. })
  258. }
  259. nextTick(() => {
  260. myChart = echarts.init(document.getElementById(chartId) as HTMLDivElement)
  261. _initData()
  262. })
  263. watch(
  264. () => props.obj,
  265. () => {
  266. _initData()
  267. },
  268. {
  269. deep: true
  270. }
  271. )
  272. return () => (
  273. <div class={styles.eChartSection}>
  274. <div class={styles.eChartTitle}>
  275. <div class={styles.left}>
  276. <div class={styles.item} style={{ '--color': color } as any}>
  277. {/* <span class={styles.line}></span> */}
  278. {props.type === 'NUM' ? (
  279. <>
  280. <span class={styles.text}>
  281. {statisticCounts.value.time} 练习人数
  282. </span>
  283. <span class={styles.num}>
  284. {statisticCounts.value.count || 0}人
  285. </span>
  286. </>
  287. ) : (
  288. <>
  289. <span class={styles.text}>
  290. {statisticCounts.value.time} 练习时长
  291. </span>
  292. <span class={styles.num}>{statisticCounts.value.count}</span>
  293. </>
  294. )}
  295. </div>
  296. </div>
  297. </div>
  298. <div class={styles.eChart}>
  299. <div id={chartId} style="width: 100%; height: 100%;"></div>
  300. </div>
  301. </div>
  302. )
  303. }
  304. })