index.tsx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. import {
  2. Button,
  3. Cell,
  4. Checkbox,
  5. CheckboxGroup,
  6. Col,
  7. DatetimePicker,
  8. Picker,
  9. Popup,
  10. Row
  11. } from 'vant'
  12. import { defineComponent, markRaw } from 'vue'
  13. import styles from './index.module.less'
  14. import * as echarts from 'echarts/core'
  15. import {
  16. BarChart,
  17. // 系列类型的定义后缀都为 SeriesOption
  18. BarSeriesOption,
  19. LineChart,
  20. LineSeriesOption
  21. } from 'echarts/charts'
  22. import { PieChart } from 'echarts/charts'
  23. import {
  24. TitleComponent,
  25. // 组件类型的定义后缀都为 ComponentOption
  26. TitleComponentOption,
  27. TooltipComponent,
  28. TooltipComponentOption,
  29. GridComponent,
  30. GridComponentOption,
  31. // 数据集组件
  32. DatasetComponent,
  33. DatasetComponentOption,
  34. // 内置数据转换器组件 (filter, sort)
  35. TransformComponent,
  36. LegendComponent,
  37. ToolboxComponent,
  38. DataZoomComponent
  39. } from 'echarts/components'
  40. import { LabelLayout, UniversalTransition } from 'echarts/features'
  41. import { CanvasRenderer } from 'echarts/renderers'
  42. // 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
  43. type ECOption = echarts.ComposeOption<
  44. | BarSeriesOption
  45. | LineSeriesOption
  46. | TitleComponentOption
  47. | TooltipComponentOption
  48. | GridComponentOption
  49. | DatasetComponentOption
  50. >
  51. // 注册必须的组件
  52. echarts.use([
  53. TitleComponent,
  54. TooltipComponent,
  55. GridComponent,
  56. DatasetComponent,
  57. TransformComponent,
  58. BarChart,
  59. LabelLayout,
  60. UniversalTransition,
  61. CanvasRenderer,
  62. PieChart,
  63. ToolboxComponent,
  64. LegendComponent,
  65. DataZoomComponent,
  66. LineChart
  67. ])
  68. import { lineChartOption, pieChartOption } from './echarts'
  69. import request from '@/helpers/request'
  70. import dayjs from 'dayjs'
  71. import { formatterDate, moneyFormat } from '@/helpers/utils'
  72. export const getAssetsHomeFile = (fileName: string) => {
  73. const path = `./images/${fileName}`
  74. const modules = import.meta.globEager('./images/*')
  75. return modules[path].default
  76. }
  77. const yearColumns: any = []
  78. const year = dayjs().year()
  79. let defaultIndex = 10
  80. for (let i = year - 10; i <= year + 10; i++) {
  81. yearColumns.push({
  82. text: `${i}年`,
  83. value: i
  84. })
  85. }
  86. export default defineComponent({
  87. name: 'IncomeConsus',
  88. data() {
  89. return {
  90. moneyInfo: {
  91. totalSingleRate: 0,
  92. totalShareRate: 0,
  93. totalInAmount: 0,
  94. practiceAmount: 0,
  95. practiceRate: 0,
  96. liveAmount: 0,
  97. liveRate: 0,
  98. videoAmount: 0,
  99. videoRate: 0,
  100. musicAmount: 0,
  101. musicRate: 0,
  102. vipShareAmount: 0,
  103. vipShareRate: 0,
  104. liveShareAmount: 0,
  105. liveShareRate: 0,
  106. videoShareAmount: 0,
  107. videoShareRate: 0,
  108. mallShareAmount: 0,
  109. mallShareRate: 0,
  110. musicShareAmount: 0,
  111. musicShareRate: 0,
  112. actiRegistShareAmount: 0,
  113. actiRegistShareRate: 0
  114. },
  115. params: {
  116. timeType: 'YEAR' as 'YEAR' | 'MONTH',
  117. dateTime: `${year}`
  118. },
  119. dateTimeStr: `${year}年`,
  120. myChart: null as any,
  121. myChart2: null as any,
  122. timerStatus: false,
  123. currentDate: new Date()
  124. }
  125. },
  126. async mounted() {
  127. this.myChart = markRaw(
  128. echarts.init(document.getElementById('incomeClass') as HTMLDivElement)
  129. )
  130. this.myChart2 = markRaw(
  131. echarts.init(document.getElementById('structrueClass') as HTMLDivElement)
  132. )
  133. this.getList()
  134. },
  135. methods: {
  136. async getList() {
  137. try {
  138. const params = this.params
  139. const res = await request.post(
  140. '/api-teacher/userAccount/accountTotal',
  141. {
  142. data: params
  143. }
  144. )
  145. const result = res.data || {}
  146. this.moneyInfo = {
  147. totalSingleRate:
  148. result.practiceRate +
  149. result.liveRate +
  150. result.videoRate +
  151. result.musicRate,
  152. totalShareRate:
  153. result.vipShareRate +
  154. result.liveShareRate +
  155. result.videoShareRate +
  156. result.musicShareRate +
  157. result.mallShareRate +
  158. result.actiRegistShareRate,
  159. totalInAmount: result.totalInAmount || 0,
  160. practiceAmount: result.practiceAmount || 0,
  161. practiceRate: result.practiceRate || 0,
  162. liveAmount: result.liveAmount || 0,
  163. liveRate: result.liveRate || 0,
  164. videoAmount: result.videoAmount || 0,
  165. videoRate: result.videoRate || 0,
  166. musicAmount: result.musicAmount || 0,
  167. musicRate: result.musicRate || 0,
  168. vipShareAmount: result.vipShareAmount || 0,
  169. vipShareRate: result.vipShareRate || 0,
  170. liveShareAmount: result.liveShareAmount || 0,
  171. liveShareRate: result.liveShareRate || 0,
  172. videoShareAmount: result.videoShareAmount || 0,
  173. videoShareRate: result.videoShareRate || 0,
  174. mallShareAmount: result.mallShareAmount || 0,
  175. mallShareRate: result.mallShareRate || 0,
  176. musicShareAmount: result.musicShareAmount || 0,
  177. musicShareRate: result.musicShareRate || 0,
  178. actiRegistShareAmount: result.actiRegistShareAmount || 0,
  179. actiRegistShareRate: result.actiRegistShareRate || 0
  180. }
  181. // 处理折线图数据
  182. const lineData = {
  183. xAxis: [] as any,
  184. practiceAmount: [] as any,
  185. liveAmount: [] as any,
  186. videoAmount: [] as any,
  187. musicAmount: [] as any,
  188. vipShareAmount: [] as any,
  189. liveShareAmount: [] as any,
  190. videoShareAmount: [] as any,
  191. mallShareAmount: [] as any,
  192. musicShareAmount: [] as any,
  193. actiRegistShareAmount: [] as any
  194. }
  195. ;(result.infoList || []).forEach((item: any) => {
  196. if (params.timeType === 'YEAR') {
  197. lineData.xAxis.push(dayjs(item.timeStr).format('MM月'))
  198. } else if (params.timeType === 'MONTH') {
  199. lineData.xAxis.push(dayjs(item.timeStr).format('DD日'))
  200. }
  201. lineData.practiceAmount.push(item.practiceAmount)
  202. lineData.liveAmount.push(item.liveAmount)
  203. lineData.videoAmount.push(item.videoAmount)
  204. lineData.musicAmount.push(item.musicAmount)
  205. lineData.vipShareAmount.push(item.vipShareAmount) // 小酷Ai
  206. lineData.liveShareAmount.push(item.liveShareAmount)
  207. lineData.videoShareAmount.push(item.videoShareAmount)
  208. lineData.mallShareAmount.push(item.mallShareAmount)
  209. lineData.musicShareAmount.push(item.musicShareAmount)
  210. lineData.actiRegistShareAmount.push(item.actiRegistShareAmount)
  211. })
  212. // 初始化折线图
  213. lineChartOption.xAxis.data = lineData.xAxis
  214. lineChartOption.series[0].data = lineData.practiceAmount
  215. lineChartOption.series[1].data = lineData.liveAmount
  216. lineChartOption.series[2].data = lineData.videoAmount
  217. lineChartOption.series[3].data = lineData.musicAmount
  218. lineChartOption.series[4].data = lineData.vipShareAmount
  219. lineChartOption.series[5].data = lineData.liveShareAmount
  220. lineChartOption.series[6].data = lineData.videoShareAmount
  221. lineChartOption.series[7].data = lineData.mallShareAmount
  222. lineChartOption.series[8].data = lineData.musicShareAmount
  223. lineChartOption.series[9].data = lineData.actiRegistShareAmount
  224. // console.log(lineChartOption)
  225. this.myChart.clear()
  226. this.myChart.setOption(lineChartOption)
  227. // 处理饼图数据
  228. pieChartOption.series[0].data[0].value = result.practiceAmount
  229. pieChartOption.series[0].data[1].value = result.liveAmount
  230. pieChartOption.series[0].data[2].value = result.videoAmount
  231. pieChartOption.series[0].data[3].value = result.musicAmount
  232. pieChartOption.series[0].data[4].value = result.vipShareAmount
  233. pieChartOption.series[0].data[5].value = result.liveShareAmount
  234. pieChartOption.series[0].data[6].value = result.videoShareAmount
  235. pieChartOption.series[0].data[7].value = result.mallShareAmount
  236. pieChartOption.series[0].data[8].value = result.musicShareAmount
  237. pieChartOption.series[0].data[9].value = result.actiRegistShareAmount
  238. this.myChart2.clear()
  239. this.myChart2.setOption(pieChartOption)
  240. } catch (e) {
  241. // console.log(e)
  242. }
  243. }
  244. },
  245. render() {
  246. return (
  247. <div style={{ overflow: 'hidden' }}>
  248. <div class={styles.incomeConsus}>
  249. <Cell
  250. class={styles.income}
  251. title="总收入(元)"
  252. v-slots={{
  253. label: () => (
  254. <span class={styles.countPrice}>
  255. {moneyFormat(this.moneyInfo.totalInAmount)}
  256. </span>
  257. ),
  258. value: () => (
  259. <span
  260. class={styles.searchTime}
  261. onClick={() => (this.timerStatus = true)}
  262. >
  263. {this.dateTimeStr}
  264. </span>
  265. )
  266. }}
  267. ></Cell>
  268. <div class={styles.section}>
  269. <Row class={styles.numberCount}>
  270. <Col span={6}>
  271. <i></i>
  272. <div class={styles.type}>
  273. <span>陪练课</span>
  274. <span class={styles.price}>
  275. {moneyFormat(this.moneyInfo.practiceAmount)}
  276. </span>
  277. </div>
  278. </Col>
  279. <Col span={6}>
  280. <i class={styles.color1}></i>
  281. <div class={styles.type}>
  282. <span>直播课</span>
  283. <span class={styles.price}>
  284. {moneyFormat(this.moneyInfo.liveAmount)}
  285. </span>
  286. </div>
  287. </Col>
  288. <Col span={6}>
  289. <i class={styles.color2}></i>
  290. <div class={styles.type}>
  291. <span>视频课</span>
  292. <span class={styles.price}>
  293. {moneyFormat(this.moneyInfo.videoAmount)}
  294. </span>
  295. </div>
  296. </Col>
  297. <Col span={6}>
  298. <i class={styles.color3}></i>
  299. <div class={styles.type}>
  300. <span>乐谱</span>
  301. <span class={styles.price}>
  302. {moneyFormat(this.moneyInfo.musicAmount)}
  303. </span>
  304. </div>
  305. </Col>
  306. <Col span={6}>
  307. <i class={styles.color4}></i>
  308. <div class={styles.type}>
  309. <span>小酷Ai推广</span>
  310. <span class={styles.price}>
  311. {moneyFormat(this.moneyInfo.vipShareAmount)}
  312. </span>
  313. </div>
  314. </Col>
  315. <Col span={6}>
  316. <i class={styles.color5}></i>
  317. <div class={styles.type}>
  318. <span>直播课推荐</span>
  319. <span class={styles.price}>
  320. {moneyFormat(this.moneyInfo.liveShareAmount)}
  321. </span>
  322. </div>
  323. </Col>
  324. <Col span={6}>
  325. <i class={styles.color6}></i>
  326. <div class={styles.type}>
  327. <span>视频课推荐</span>
  328. <span class={styles.price}>
  329. {moneyFormat(this.moneyInfo.videoShareAmount)}
  330. </span>
  331. </div>
  332. </Col>
  333. <Col span={6}>
  334. <i class={styles.color7}></i>
  335. <div class={styles.type}>
  336. <span>商品推荐</span>
  337. <span class={styles.price}>
  338. {moneyFormat(this.moneyInfo.mallShareAmount)}
  339. </span>
  340. </div>
  341. </Col>
  342. <Col span={6}>
  343. <i class={styles.color8}></i>
  344. <div class={styles.type}>
  345. <span>乐谱推荐</span>
  346. <span class={styles.price}>
  347. {moneyFormat(this.moneyInfo.musicShareAmount)}
  348. </span>
  349. </div>
  350. </Col>
  351. <Col span={6}>
  352. <i class={styles.color9}></i>
  353. <div class={styles.type}>
  354. <span>活动报名</span>
  355. <span class={styles.price}>
  356. {moneyFormat(this.moneyInfo.actiRegistShareAmount)}
  357. </span>
  358. </div>
  359. </Col>
  360. </Row>
  361. </div>
  362. <div id="incomeClass" class={styles.incomeLine}></div>
  363. <div class={styles.incomeTitle}>
  364. <i></i>收入结构
  365. </div>
  366. <div class={[styles.pieSection, 'van-hairline--bottom']}>
  367. <div id="structrueClass" class={styles.pieIncome}></div>
  368. <div class={styles.rateAll}>
  369. <div>
  370. <img src={getAssetsHomeFile('icon_user.png')} />
  371. <span>个人收入总占比</span>
  372. <span class={styles.rate}>
  373. {this.moneyInfo.totalSingleRate}%
  374. </span>
  375. </div>
  376. <div>
  377. <img src={getAssetsHomeFile('icon_fly.png')} />
  378. <span>推广收益总占比</span>
  379. <span class={styles.rate}>
  380. {this.moneyInfo.totalShareRate}%
  381. </span>
  382. </div>
  383. </div>
  384. </div>
  385. <div class={styles.pieData}>
  386. <div>
  387. <i class={styles.piePractice}></i>
  388. <span class={styles.pieTitle}>陪练课</span>
  389. <span>{this.moneyInfo.practiceRate}%</span>
  390. </div>
  391. <div>
  392. <i class={styles.pie1}></i>
  393. <span class={styles.pieTitle}>小酷Ai推广</span>
  394. <span>{this.moneyInfo.vipShareRate}%</span>
  395. </div>
  396. <div>
  397. <i class={styles.pieLive}></i>
  398. <span class={styles.pieTitle}>直播课</span>
  399. <span>{this.moneyInfo.liveRate}%</span>
  400. </div>
  401. <div>
  402. <i class={styles.pie2}></i>
  403. <span class={styles.pieTitle}>直播课推荐</span>
  404. <span>{this.moneyInfo.liveShareRate}%</span>
  405. </div>
  406. <div>
  407. <i class={styles.pieVideo}></i>
  408. <span class={styles.pieTitle}>视频课</span>
  409. <span>{this.moneyInfo.videoRate}%</span>
  410. </div>
  411. <div>
  412. <i class={styles.pie3}></i>
  413. <span class={styles.pieTitle}>视频课推荐</span>
  414. <span>{this.moneyInfo.videoShareRate}%</span>
  415. </div>
  416. <div>
  417. <i class={styles.pieMusic}></i>
  418. <span class={styles.pieTitle}>乐谱</span>
  419. <span>{this.moneyInfo.musicRate}%</span>
  420. </div>
  421. <div>
  422. <i class={styles.pie5}></i>
  423. <span class={styles.pieTitle}>乐谱推荐</span>
  424. <span>{this.moneyInfo.musicShareRate}%</span>
  425. </div>
  426. <div>
  427. <i class={styles.pie6}></i>
  428. <span class={styles.pieTitle}>活动报名</span>
  429. <span>{this.moneyInfo.actiRegistShareRate}%</span>
  430. </div>
  431. <div>
  432. <i class={styles.pie4}></i>
  433. <span class={styles.pieTitle}>商品推荐</span>
  434. <span>{this.moneyInfo.mallShareRate}%</span>
  435. </div>
  436. </div>
  437. </div>
  438. <Popup
  439. v-model:show={this.timerStatus}
  440. position="bottom"
  441. round
  442. zIndex={99999999}
  443. >
  444. {this.params.timeType === 'MONTH' && (
  445. <DatetimePicker
  446. v-model={this.currentDate}
  447. type="year-month"
  448. title="选择时间"
  449. formatter={formatterDate}
  450. onCancle={() => (this.timerStatus = false)}
  451. onConfirm={(value: any) => {
  452. this.params.dateTime = dayjs(value).format('YYYY-MM')
  453. this.dateTimeStr = dayjs(value).format('YYYY年MM月')
  454. this.timerStatus = false
  455. this.getList()
  456. }}
  457. v-slots={{
  458. 'columns-top': () => (
  459. <div class={styles.timePopup}>
  460. <Button
  461. type="primary"
  462. plain={this.params.timeType !== 'MONTH'}
  463. size="mini"
  464. class={styles.timeMonth}
  465. onClick={() => (this.params.timeType = 'MONTH')}
  466. >
  467. 按月
  468. </Button>
  469. <Button
  470. type="primary"
  471. plain={this.params.timeType !== 'YEAR'}
  472. size="mini"
  473. class={styles.timeYear}
  474. onClick={() => (this.params.timeType = 'YEAR')}
  475. >
  476. 按年
  477. </Button>
  478. </div>
  479. )
  480. }}
  481. />
  482. )}
  483. {this.params.timeType === 'YEAR' && (
  484. <Picker
  485. v-model={this.currentDate}
  486. title="选择时间"
  487. columns={yearColumns}
  488. defaultIndex={defaultIndex}
  489. onCancel={() => (this.timerStatus = false)}
  490. onConfirm={(obj: any, index: number) => {
  491. this.params.dateTime = obj.value
  492. defaultIndex = index // 记录当前选择的年份
  493. this.dateTimeStr = obj.text
  494. this.timerStatus = false
  495. this.getList()
  496. }}
  497. v-slots={{
  498. 'columns-top': () => (
  499. <div class={styles.timePopup}>
  500. <Button
  501. type="primary"
  502. plain={this.params.timeType !== 'MONTH'}
  503. size="mini"
  504. class={styles.timeMonth}
  505. onClick={() => (this.params.timeType = 'MONTH')}
  506. >
  507. 按月
  508. </Button>
  509. <Button
  510. type="primary"
  511. plain={this.params.timeType !== 'YEAR'}
  512. onClick={() => (this.params.timeType = 'YEAR')}
  513. size="mini"
  514. class={styles.timeYear}
  515. >
  516. 按年
  517. </Button>
  518. </div>
  519. )
  520. }}
  521. />
  522. )}
  523. </Popup>
  524. </div>
  525. )
  526. }
  527. })