index.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. import { computed, defineComponent, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
  2. import styles from './index.module.less'
  3. import CircleProgress from './component/CircleProgress'
  4. import iconBackup from './image/icon-backup.png'
  5. import iconEnsemble from './image/icon-ensemble.png'
  6. import * as echarts from 'echarts'
  7. import { listenerMessage, postMessage, removeListenerMessage } from '@/helpers/native-message'
  8. type EChartsOption = echarts.EChartsOption
  9. interface ISubjectItem {
  10. /** 声部名称 */
  11. subjectName: string
  12. /**达标率 */
  13. practiceRate: number
  14. /** 达标人数 */
  15. passNum: number
  16. /** 未达标人数 */
  17. noPassNum: number
  18. /** 非会员 */
  19. noMemberNum: number
  20. }
  21. const symbolData: any = {
  22. type: 'line',
  23. symbol: 'circle',
  24. symbolSize: 6,
  25. triggerLineEvent: true,
  26. stack: 'Total'
  27. }
  28. export default defineComponent({
  29. name: 'subject-echarts',
  30. setup() {
  31. const activeData = reactive({
  32. index: -1,
  33. sum: {
  34. /**达标率 */
  35. practiceRate: 0,
  36. /**达标人数 */
  37. passNum: 0,
  38. /**未达标人数 */
  39. noPassNum: 0,
  40. /**未会员 */
  41. noMemberNum: 0
  42. },
  43. practiceThisWeeks: [] as ISubjectItem[],
  44. colors: [
  45. {
  46. color: '#FF8057',
  47. borderColor: 'rgba(255,128,87,0.5)',
  48. text: '达标率',
  49. select: true
  50. },
  51. {
  52. color: '#2FC58D',
  53. borderColor: 'rgba(47,197,141,0.5)',
  54. text: '达标',
  55. select: true
  56. },
  57. {
  58. color: '#4A99FF',
  59. borderColor: 'rgba(74,153,255,0.5)',
  60. text: '未达标',
  61. select: true
  62. },
  63. {
  64. color: '#9884BA',
  65. borderColor: 'rgba(152,132,186,0.5)',
  66. text: '非会员',
  67. select: true
  68. }
  69. ]
  70. })
  71. const subjects = computed(() => {
  72. return activeData.practiceThisWeeks.map((n) => n.subjectName)
  73. })
  74. let myChart: echarts.ECharts
  75. const handleInit = (data: any) => {
  76. const { content } = data
  77. if (!content) return
  78. if (myChart){
  79. myChart.clear()
  80. }
  81. activeData.sum = content.sum || {}
  82. activeData.practiceThisWeeks =
  83. content.practiceThisWeeks
  84. // .concat(
  85. // ...[
  86. // {
  87. // memberNum: 1,
  88. // noMemberNum: 10,
  89. // noPassNum: 12,
  90. // passNum: 30,
  91. // practiceRate: 20,
  92. // subjectName: '单簧管',
  93. // trainingNum: 0
  94. // },
  95. // {
  96. // memberNum: 1,
  97. // noMemberNum: 20,
  98. // noPassNum: 31,
  99. // passNum: 10,
  100. // practiceRate: 30,
  101. // subjectName: '萨克斯',
  102. // trainingNum: 0
  103. // },
  104. // {
  105. // memberNum: 1,
  106. // noMemberNum: 10,
  107. // noPassNum: 16,
  108. // passNum: 40,
  109. // practiceRate: 20,
  110. // subjectName: '小号',
  111. // trainingNum: 0
  112. // },
  113. // {
  114. // memberNum: 1,
  115. // noMemberNum: 10,
  116. // noPassNum: 16,
  117. // passNum: 40,
  118. // practiceRate: 20,
  119. // subjectName: '小号',
  120. // trainingNum: 0
  121. // }
  122. // ]
  123. // )
  124. || []
  125. const chartDom = document.getElementById('subjectEcharts')!
  126. myChart = echarts.init(chartDom)
  127. const option: EChartsOption = {
  128. tooltip: {
  129. trigger: 'axis',
  130. showContent: false,
  131. axisPointer: {
  132. type: 'line',
  133. lineStyle: {
  134. width: 40,
  135. opacity: 0.5,
  136. cap: 'square'
  137. },
  138. label:{
  139. show: true,
  140. },
  141. }
  142. },
  143. grid: {
  144. left: 5,
  145. top: 5,
  146. right: 5,
  147. bottom: 5,
  148. containLabel: true
  149. },
  150. legend: {
  151. type: 'scroll',
  152. right: 12,
  153. itemGap: 0,
  154. itemWidth: 2,
  155. itemStyle: {
  156. opacity: 0
  157. },
  158. lineStyle: {
  159. width: 0,
  160. opacity: 0
  161. },
  162. textStyle: {
  163. opacity: 0
  164. }
  165. },
  166. xAxis: {
  167. type: 'category',
  168. axisLabel: {
  169. interval: 0,
  170. color: '#333',
  171. fontSize: 9
  172. },
  173. axisTick: {
  174. show: false
  175. },
  176. boundaryGap: false,
  177. data: subjects.value
  178. },
  179. yAxis: [
  180. {
  181. type: 'value',
  182. position: 'left',
  183. axisLine: {
  184. show: true,
  185. lineStyle: {
  186. color: '#999'
  187. }
  188. },
  189. axisLabel: {
  190. formatter: '{value}'
  191. }
  192. },
  193. {
  194. type: 'value',
  195. position: 'right',
  196. axisLine: {
  197. show: true,
  198. lineStyle: {
  199. color: '#999'
  200. }
  201. },
  202. axisLabel: {
  203. formatter: '{value} %'
  204. }
  205. }
  206. ],
  207. series: ['practiceRate', 'passNum', 'noPassNum', 'noMemberNum'].map(
  208. (key: string, index: number) => {
  209. return {
  210. name: activeData.colors[index].text,
  211. color: activeData.colors[index].color,
  212. ...symbolData,
  213. yAxisIndex: index === 0 ? 1 : 0,
  214. data: activeData.practiceThisWeeks.map((n) => n[key])
  215. }
  216. }
  217. )
  218. }
  219. option && myChart.setOption(option)
  220. myChart.on('highlight', function (params: any) {
  221. console.log("🚀 ~ params:", params)
  222. activeData.index = params.batch[0].dataIndex
  223. })
  224. // console.log('🚀 ~ myChart:', myChart)
  225. }
  226. const handleAction = (index: number) => {
  227. activeData.index = index
  228. myChart?.dispatchAction({
  229. type: 'showTip',
  230. seriesIndex: 0,
  231. dataIndex: index
  232. })
  233. }
  234. const changeLegend = (item: any) => {
  235. myChart?.dispatchAction({
  236. type: item.select ? 'legendUnSelect' : 'legendSelect',
  237. // 图例名称
  238. name: item.text
  239. })
  240. item.select = !item.select
  241. }
  242. onMounted(() => {
  243. // handleInit({ content: { practiceThisWeeks: [], sum: {} } })
  244. listenerMessage('setAccomanyEcharts', handleInit)
  245. postMessage({
  246. api: 'setAccomanyEcharts'
  247. })
  248. })
  249. onBeforeUnmount(() => {
  250. removeListenerMessage('setAccomanyEcharts', handleInit)
  251. })
  252. return () => (
  253. <div class={styles.subjectEcharts}>
  254. <div class={[styles.container, styles.ensemble]}>
  255. <div class={styles.head}>
  256. <div class={styles.headLeft} onClick={() => handleInit({ content: { practiceThisWeeks: [], sum: {} } })}>
  257. <img class={styles.icon} src={iconEnsemble} />
  258. <div>总体情况</div>
  259. </div>
  260. </div>
  261. <div class={styles.content}>
  262. <CircleProgress
  263. value={activeData.sum.practiceRate}
  264. size={80}
  265. color="#FF8057"
  266. strokeWidth={6}
  267. duration={3000}
  268. />
  269. <div class={styles.items}>
  270. <div class={styles.item}>
  271. <div class={styles.itemNum}>
  272. <span class={styles.rect}></span>
  273. <span style={{ color: '#FF8057' }}>{activeData.sum.passNum}</span>
  274. </div>
  275. <div class={styles.itemTitle}>达标人数</div>
  276. </div>
  277. <div class={[styles.item, styles.line]}>
  278. <div class={styles.itemNum}>
  279. <span class={styles.rect} style={{ background: '#FFE7DF' }}></span>
  280. <span>{activeData.sum.noPassNum}</span>
  281. </div>
  282. <div class={styles.itemTitle}>未达标人数</div>
  283. </div>
  284. <div class={styles.item}>
  285. <div class={styles.itemNum}>
  286. <span>{activeData.sum.noMemberNum}</span>
  287. </div>
  288. <div class={styles.itemTitle}>非会员人数</div>
  289. </div>
  290. </div>
  291. </div>
  292. </div>
  293. <div class={[styles.container, styles.ensemble]}>
  294. <div class={styles.head}>
  295. <div class={styles.headLeft}>
  296. <img class={styles.icon} src={iconBackup} />
  297. <div>声部情况</div>
  298. </div>
  299. <div class={styles.headRight}>
  300. {activeData.colors.map((c) => (
  301. <div
  302. style={
  303. c.select
  304. ? { color: '#fff', borderColor: c.color, backgroundColor: c.color }
  305. : { color: c.color, borderColor: c.borderColor }
  306. }
  307. onClick={() => changeLegend(c)}
  308. >
  309. {c.text}
  310. </div>
  311. ))}
  312. </div>
  313. </div>
  314. <div id="subjectEcharts" class={styles.echartsMain}></div>
  315. {!activeData.practiceThisWeeks.length && <div class={styles.emtry}>暂无练习记录</div>}
  316. </div>
  317. <div class={[styles.container, styles.subjectWrap]}>
  318. {activeData.practiceThisWeeks.map((item, index: number) => (
  319. <div
  320. class={[styles.listItem, index === activeData.index ? styles.listItemActive : '']}
  321. onClick={() => handleAction(index)}
  322. >
  323. <div class={styles.dot}></div>
  324. <div class={styles.itemLeft}>
  325. <div class={styles.subjectName}>{item.subjectName}</div>
  326. <div class={styles.subjectType}>声部</div>
  327. </div>
  328. <div class={styles.listitems}>
  329. {activeData.colors.map((c, index: number) => {
  330. return (
  331. <div class={styles.item}>
  332. <div class={styles.itemNum}>
  333. <span style={{ color: c.color }}>{item.practiceRate}</span>
  334. </div>
  335. <div class={styles.itemTitle}>
  336. {c.text}
  337. {index === 0 ? '' : '人数'}
  338. </div>
  339. </div>
  340. )
  341. })}
  342. </div>
  343. </div>
  344. ))}
  345. </div>
  346. </div>
  347. )
  348. }
  349. })