123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- import { computed, defineComponent, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
- import styles from './index.module.less'
- import CircleProgress from './component/CircleProgress'
- import iconBackup from './image/icon-backup.png'
- import iconEnsemble from './image/icon-ensemble.png'
- import * as echarts from 'echarts'
- import { listenerMessage, postMessage, removeListenerMessage } from '@/helpers/native-message'
- type EChartsOption = echarts.EChartsOption
- interface ISubjectItem {
- /** 声部名称 */
- subjectName: string
- /**达标率 */
- practiceRate: number
- /** 达标人数 */
- passNum: number
- /** 未达标人数 */
- noPassNum: number
- /** 非会员 */
- noMemberNum: number
- }
- const symbolData: any = {
- type: 'line',
- symbol: 'circle',
- symbolSize: 6,
- triggerLineEvent: true
- }
- export default defineComponent({
- name: 'subject-echarts',
- setup() {
- const activeData = reactive({
- index: -1,
- sum: {
- /**达标率 */
- practiceRate: 0,
- /**达标人数 */
- passNum: 0,
- /**未达标人数 */
- noPassNum: 0,
- /**未会员 */
- noMemberNum: 0
- },
- practiceThisWeeks: [] as ISubjectItem[],
- colors: [
- {
- color: '#FF8057',
- borderColor: 'rgba(255,128,87,0.5)',
- text: '达标率',
- select: true,
- key: 'practiceRate'
- },
- {
- color: '#2FC58D',
- borderColor: 'rgba(47,197,141,0.5)',
- text: '达标',
- select: true,
- key: 'passNum'
- },
- {
- color: '#4A99FF',
- borderColor: 'rgba(74,153,255,0.5)',
- text: '未达标',
- select: true,
- key: 'noPassNum'
- },
- {
- color: '#9884BA',
- borderColor: 'rgba(152,132,186,0.5)',
- text: '非会员',
- select: true,
- key: 'noMemberNum'
- }
- ]
- })
- const subjects = computed(() => {
- return activeData.practiceThisWeeks.map((n) => n.subjectName)
- })
- let myChart: echarts.ECharts
- const handleInit = (data: any) => {
- const { content } = data
- if (!content) return
- if (myChart) {
- myChart.dispose()
- }
- activeData.sum = content.sum || {}
- activeData.practiceThisWeeks = content.practiceThisWeeks || []
- const chartDom = document.getElementById('subjectEcharts')!
- myChart = echarts.init(chartDom)
- const option: EChartsOption = {
- tooltip: {
- trigger: 'axis',
- showContent: false,
- axisPointer: {
- type: 'line',
- lineStyle: {
- width: 30,
- type: 'solid',
- opacity: 0.2
- }
- }
- },
- grid: {
- left: 8,
- top: 5,
- right: 5,
- bottom: 5,
- containLabel: true
- },
- legend: {
- type: 'plain',
- align: 'auto',
- itemGap: 10,
- itemWidth: 6,
- itemStyle: {
- opacity: 0
- },
- lineStyle: {
- width: 0,
- opacity: 0
- },
- textStyle: {
- opacity: 0
- }
- },
- xAxis: {
- type: 'category',
- axisLine: {
- show: false
- },
- axisLabel: {
- interval: 0,
- color: '#333',
- fontSize: 9
- },
- axisTick: {
- alignWithLabel: true,
- show: false
- },
- // boundaryGap: false,
- data: subjects.value
- },
- yAxis: [
- {
- type: 'value',
- position: 'left',
- axisLine: {
- show: false,
- lineStyle: {
- color: '#999'
- }
- },
- splitLine: {
- show: true, // 是否显示y轴分割线
- lineStyle: {
- color: ['rgba(249, 234, 220, 1)'] // 分隔线颜色。
- }
- },
- min: 0,
- splitNumber: 5
- },
- {
- type: 'value',
- position: 'right',
- splitLine: {
- show: true, // 是否显示y轴分割线
- lineStyle: {
- color: ['rgba(249, 234, 220, 1)'] // 分隔线颜色。
- }
- },
- axisLine: {
- show: false,
- lineStyle: {
- color: '#999'
- }
- },
- interval: 20,
- min: 0,
- max: 100,
- splitNumber: 5,
- axisLabel: {
- formatter: function (value: number, index: number) {
- return value + '%'
- }
- }
- }
- ],
- series: ['practiceRate', 'passNum', 'noPassNum', 'noMemberNum'].map(
- (key: string, index: number) => {
- return {
- name: activeData.colors[index].text,
- color: activeData.colors[index].color,
- ...symbolData,
- yAxisIndex: index === 0 ? 1 : 0,
- data: activeData.practiceThisWeeks.map((n) => n[key])
- }
- }
- )
- }
- let maxNum = Math.max(...(option.series as any[]).map((n) => n.data).flat())
- let minNum = Math.min(...(option.series as any[]).map((n) => n.data).flat())
- maxNum = Math.ceil(maxNum / 9.5) * 10
- minNum = Math.floor(minNum / 12) * 10
- ;(option.yAxis as any[])[0].interval = Math.ceil(Math.ceil(maxNum) / 5)
- ;(option.yAxis as any[])[0].max = Math.ceil(Math.ceil(maxNum) / 5) * 5
- option && myChart.setOption(option)
- myChart.on('highlight', function (params: any) {
- activeData.index = params.batch[0].dataIndex
- })
- }
- const handleAction = (index: number) => {
- activeData.index = index
- myChart?.dispatchAction({
- type: 'showTip',
- seriesIndex: 0,
- dataIndex: index
- })
- }
- const changeLegend = (item: any) => {
- myChart?.dispatchAction({
- type: item.select ? 'legendUnSelect' : 'legendSelect',
- // 图例名称
- name: item.text
- })
- item.select = !item.select
- }
- const handleMock = () => {
- // console.log('handleMock1232')
- const resulst = {
- sum: {
- noMemberNum: Math.floor(Math.random() * 1000),
- noPassNum: Math.floor(Math.random() * 1000),
- passNum: Math.floor(Math.random() * 1000),
- practiceRate: Math.ceil(Math.random() * 100)
- },
- practiceThisWeeks: new Array(Math.ceil(Math.random() * 9)).fill(1).map((n, i) => {
- return {
- /** 声部名称 */
- subjectName: '声部' + (i + 1),
- /**达标率 */
- practiceRate: Math.ceil(Math.random() * 100),
- /** 达标人数 */
- passNum: Math.floor(Math.random() * 1000),
- /** 未达标人数 */
- noPassNum: Math.floor(Math.random() * 1000),
- /** 非会员 */
- noMemberNum: Math.floor(Math.random() * 1000)
- }
- })
- }
- console.log(resulst)
- handleInit({ content: resulst })
- }
- onMounted(() => {
- // handleMock()
- listenerMessage('setAccomanyEcharts', handleInit)
- postMessage({
- api: 'setAccomanyEcharts'
- })
- })
- onBeforeUnmount(() => {
- removeListenerMessage('setAccomanyEcharts', handleInit)
- })
- const goto = () => {
- const url = location.origin + location.pathname + `#/exercise-record`
- console.log('🚀 ~ url:', url)
- postMessage({
- api: 'openWebView',
- content: {
- url: url,
- orientation: 1
- }
- })
- }
- return () => (
- <div class={styles.subjectEcharts}>
- <div class={[styles.container, styles.ensemble]}>
- <div class={styles.head}>
- <div class={styles.headLeft}>
- <img class={styles.icon} src={iconEnsemble} />
- <div>总体情况</div>
- </div>
- </div>
- <div class={styles.content} onClick={() => goto()}>
- <CircleProgress
- value={Number(activeData.sum.practiceRate)}
- size={80}
- color="#FF8057"
- strokeWidth={6}
- />
- <div class={styles.items}>
- <div class={styles.item}>
- <div class={styles.itemNum}>
- <span class={styles.rect}></span>
- <span style={{ color: '#FF8057' }}>{activeData.sum.passNum}</span>
- </div>
- <div class={styles.itemTitle}>达标人数</div>
- </div>
- <div class={[styles.item, styles.line]}>
- <div class={styles.itemNum}>
- <span class={styles.rect} style={{ background: '#FFE7DF' }}></span>
- <span>{activeData.sum.noPassNum}</span>
- </div>
- <div class={styles.itemTitle}>未达标人数</div>
- </div>
- <div class={styles.item}>
- <div class={styles.itemNum}>
- <span>{activeData.sum.noMemberNum}</span>
- </div>
- <div class={styles.itemTitle}>非会员人数</div>
- </div>
- </div>
- </div>
- </div>
- <div class={[styles.container, styles.ensemble]}>
- <div class={styles.head}>
- <div class={styles.headLeft}>
- <img class={styles.icon} src={iconBackup} />
- <div>声部情况</div>
- </div>
- <div class={styles.headRight}>
- {activeData.colors.map((c) => (
- <div
- style={
- c.select
- ? { color: '#fff', borderColor: c.color, backgroundColor: c.color }
- : { color: c.color, borderColor: c.borderColor }
- }
- onClick={() => changeLegend(c)}
- >
- {c.text}
- </div>
- ))}
- </div>
- </div>
- <div class={styles.headLabelWrap}>
- {activeData.colors.map((c, cIndex) => {
- return (
- <div class={styles.headLabel} style={{ display: c.select ? '' : 'none' }}>
- <div
- class={styles.headLabelDot}
- style={{ background: c.color, color: c.color }}
- ></div>
- <div style={{ color: c.color }}>
- {activeData.sum[c.key] || 0}
- {cIndex === 0 ? '%' : '人'}
- </div>
- </div>
- )
- })}
- </div>
- <div id="subjectEcharts" class={styles.echartsMain}></div>
- {!activeData.practiceThisWeeks.length && <div class={styles.emtry}>暂无练习记录</div>}
- </div>
- <div class={[styles.container, styles.subjectWrap]}>
- {activeData.practiceThisWeeks.map((item, index: number) => (
- <div
- class={[styles.listItem, index === activeData.index ? styles.listItemActive : '']}
- onClick={() => handleAction(index)}
- >
- <div class={styles.dot}></div>
- <div class={styles.itemLeft}>
- <div class={styles.subjectName}>{item.subjectName}</div>
- <div class={styles.subjectType}>声部</div>
- </div>
- <div class={styles.listitems}>
- {activeData.colors.map((c, index: number) => {
- return (
- <div class={styles.item}>
- <div class={styles.itemNum}>
- <span style={{ color: c.color }}>
- {item[c.key]}
- {index === 0 ? '%' : ''}
- </span>
- </div>
- <div class={styles.itemTitle}>
- {c.text}
- {index === 0 ? '' : '人数'}
- </div>
- </div>
- )
- })}
- </div>
- </div>
- ))}
- </div>
- </div>
- )
- }
- })
|