index.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. import {
  2. defineComponent,
  3. nextTick,
  4. onMounted,
  5. PropType,
  6. reactive,
  7. ref,
  8. shallowReactive,
  9. watch
  10. } from 'vue'
  11. import styles from './index.module.less'
  12. import icon1 from '../../images/icon1.png'
  13. import iconArrow from '../../images/icon-arrow.png'
  14. import iconArrow1 from '../../images/icon-arrow1.png'
  15. import iconArrow11 from '../../images/icon-arrow1-1.png'
  16. import * as echarts from 'echarts/core'
  17. import {
  18. LineChart
  19. // LineSeriesOption
  20. } from 'echarts/charts'
  21. // import { PieChart } from 'echarts/charts'
  22. import {
  23. TitleComponent,
  24. // 组件类型的定义后缀都为 ComponentOption
  25. // TitleComponentOption,
  26. TooltipComponent,
  27. // TooltipComponentOption,
  28. GridComponent,
  29. // 数据集组件
  30. DatasetComponent,
  31. // DatasetComponentOption,
  32. // 内置数据转换器组件 (filter, sort)
  33. // TransformComponent,
  34. LegendComponent,
  35. ToolboxComponent,
  36. DataZoomComponent
  37. } from 'echarts/components'
  38. import { LabelLayout } from 'echarts/features'
  39. import { CanvasRenderer } from 'echarts/renderers'
  40. import { Button, DatetimePicker, Popup, Toast } from 'vant'
  41. import { formatterDatePicker } from '@/helpers/utils'
  42. import dayjs from 'dayjs'
  43. import { getTimeRange, TIME_TYPE } from '../../home-statistics'
  44. // 注册必须的组件
  45. echarts.use([
  46. TitleComponent,
  47. TooltipComponent,
  48. GridComponent,
  49. DatasetComponent,
  50. // TransformComponent,
  51. LabelLayout,
  52. // UniversalTransition,
  53. CanvasRenderer,
  54. // PieChart,
  55. ToolboxComponent,
  56. LegendComponent,
  57. DataZoomComponent,
  58. LineChart
  59. ])
  60. const lineChartOption = (xAxisData: any, seriesData: any) => {
  61. return {
  62. title: {
  63. text: '单位:次',
  64. textStyle: {
  65. color: '#777777',
  66. fontSize: 13,
  67. fontWeight: 400
  68. }
  69. },
  70. legend: { show: false },
  71. emphasis: { lineStyle: { width: 2 } },
  72. xAxis: {
  73. boundaryGap: false,
  74. data: xAxisData,
  75. type: 'category',
  76. axisLine: { lineStyle: { color: '#8C8C8C' } },
  77. lineStyle: { color: '#F2F2F2' }
  78. },
  79. color: [
  80. '#2DC7AA',
  81. '#FF6079'
  82. // '#2DC7AA',
  83. // '#FF602C',
  84. // '#91DD1C',
  85. // '#FFA92C',
  86. // '#BE7E2E',
  87. // '#1C96DD',
  88. // '#D22CFF',
  89. // '#FF3C3C',
  90. // '#1AEE3E',
  91. // '#00c9ff'
  92. ],
  93. series: [
  94. {
  95. lineStyle: { width: 1 },
  96. data: seriesData[0],
  97. symbol: 'circle',
  98. name: '浏览次数',
  99. type: 'line',
  100. emphasis: { lineStyle: { width: 1 } }
  101. },
  102. {
  103. lineStyle: { width: 1 },
  104. data: seriesData[1],
  105. symbol: 'circle',
  106. name: '购买次数',
  107. type: 'line',
  108. areaStyle: {
  109. color: {
  110. type: 'linear',
  111. x: 0,
  112. y: 0,
  113. x2: 0,
  114. y2: 1,
  115. colorStops: [
  116. {
  117. offset: 0,
  118. color: 'rgba(255, 96, 121, 0.23)'
  119. // 0% 处的颜色
  120. },
  121. {
  122. offset: 1,
  123. // 100% 处的颜色
  124. color: 'rgba(255, 96, 121, 0)'
  125. }
  126. ]
  127. }
  128. },
  129. emphasis: { lineStyle: { width: 1 } }
  130. }
  131. ],
  132. grid: {
  133. bottom: '3%',
  134. containLabel: true,
  135. left: '3%',
  136. right: '5%',
  137. top: '40'
  138. },
  139. tooltip: {
  140. trigger: 'axis',
  141. confine: true,
  142. formatter: function (params: any) {
  143. return params[0].name
  144. },
  145. backgroundColor: '#FF6079',
  146. borderWidth: 0,
  147. borderRadius: 24,
  148. padding: [1, 4],
  149. textStyle: {
  150. color: '#FFFFFF',
  151. fontSize: 12
  152. }
  153. },
  154. yAxis: {
  155. type: 'value',
  156. splitLine: {
  157. axisLine: { lineStyle: { color: '#8C8C8C' } },
  158. lineStyle: { color: ['#f2f2f2'], type: 'dashed' }
  159. }
  160. },
  161. dataZoom: [{ type: 'inside', throttle: 100 }],
  162. toolbox: { feature: { saveAsImage: { show: false } } }
  163. }
  164. }
  165. export default defineComponent({
  166. name: 'eChats-model',
  167. props: {
  168. obj: {
  169. type: Object,
  170. default: () => ({})
  171. },
  172. currentType: {
  173. type: String as PropType<TIME_TYPE>,
  174. default: 'MONTH'
  175. }
  176. },
  177. emits: ['confirm'],
  178. setup(props, { emit }) {
  179. const chartId = 'eChart' + Date.now()
  180. const statisticCounts = ref({
  181. browseCount: 0,
  182. buyCount: 0
  183. })
  184. const currentType = ref<TIME_TYPE>(props.currentType)
  185. const timeRange = getTimeRange(currentType.value)
  186. const searchStatus = ref(false)
  187. const forms = reactive({
  188. loading: false,
  189. dataShow: true,
  190. subjectId: '' as any, // 选择的声部
  191. subjectList: [] as any,
  192. startTimeStatus: false,
  193. endTimeMinDate: new Date(),
  194. endTimeMaxDate: dayjs(new Date()).add(1, 'year').toDate(),
  195. endTimeStatus: false,
  196. startTime: new Date(timeRange?.startTime || ''),
  197. startTimeStr: timeRange?.startTime || '',
  198. endTime: new Date(timeRange?.endTime || ''),
  199. endTimeStr: timeRange?.endTime || ''
  200. })
  201. const showTimeRange = shallowReactive({
  202. startTime: timeRange?.startTime || '',
  203. endTime: timeRange?.endTime || ''
  204. })
  205. let myChart: echarts.ECharts
  206. const _initData = () => {
  207. nextTick(() => {
  208. statisticCounts.value.browseCount = props.obj.browseCount || 0
  209. statisticCounts.value.buyCount = props.obj.buyCount || 0
  210. myChart.clear()
  211. lineChartOption &&
  212. myChart.setOption(
  213. lineChartOption(props.obj.xAxisData, props.obj.yAxisData)
  214. )
  215. myChart.on('highlight', function (params: any) {
  216. const batch = params.batch || []
  217. const options: any = myChart.getOption()
  218. batch.forEach((item: any) => {
  219. const batchIndex = item.dataIndex
  220. const browseCount = options.series[0].data[batchIndex]
  221. const buyCount = options.series[1].data[batchIndex]
  222. statisticCounts.value = {
  223. browseCount,
  224. buyCount
  225. }
  226. })
  227. })
  228. })
  229. }
  230. nextTick(() => {
  231. myChart = echarts.init(document.getElementById(chartId) as HTMLDivElement)
  232. _initData()
  233. })
  234. watch(
  235. () => props.obj,
  236. () => {
  237. _initData()
  238. },
  239. {
  240. deep: true
  241. }
  242. )
  243. watch(
  244. () => props.currentType,
  245. () => {
  246. currentType.value = props.currentType
  247. }
  248. )
  249. const onChangeTime = (type: TIME_TYPE) => {
  250. if (currentType.value === type) return
  251. currentType.value = type
  252. resetTime(type)
  253. // emit('confirm', currentType.value)
  254. }
  255. // 格式化
  256. const resetTime = (type: TIME_TYPE) => {
  257. const timeRang = getTimeRange(type)
  258. forms.startTime = new Date(timeRang?.startTime || '')
  259. forms.startTimeStr = timeRang?.startTime || ''
  260. forms.endTimeMinDate = dayjs(timeRang?.startTime || '').toDate()
  261. forms.endTimeMaxDate = dayjs(timeRang?.startTime || '')
  262. .add(1, 'year')
  263. .toDate()
  264. forms.endTime = new Date(timeRang?.endTime || '')
  265. forms.endTimeStr = timeRang?.endTime || ''
  266. }
  267. return () => (
  268. <div class={styles.eChartSection}>
  269. <div class={styles.homeHead}>
  270. <div class={styles.title}>
  271. <img src={icon1} />
  272. <span>浏览/购买</span>
  273. </div>
  274. <div class={styles.right}>
  275. <div
  276. class={[
  277. styles.showItem,
  278. searchStatus.value && styles.showItemActive
  279. ]}
  280. onClick={() => (searchStatus.value = true)}
  281. >
  282. <span>
  283. {showTimeRange.startTime}~{showTimeRange.endTime}
  284. </span>
  285. <img src={searchStatus.value ? iconArrow11 : iconArrow1} />
  286. </div>
  287. </div>
  288. </div>
  289. <div class={styles.eChartTitle}>
  290. <div class={styles.left}>
  291. <div class={styles.item} style="--color: #2DC7AA">
  292. <span class={styles.line}></span>
  293. <span class={styles.text}>浏览次数</span>
  294. <span class={styles.num}>
  295. {statisticCounts.value.browseCount}次
  296. </span>
  297. </div>
  298. <div class={styles.item} style="--color: #FF6079">
  299. <span class={styles.line}></span>
  300. <span class={styles.text}>购买次数</span>
  301. <span class={styles.num}>{statisticCounts.value.buyCount}次</span>
  302. </div>
  303. </div>
  304. </div>
  305. <div class={styles.eChart}>
  306. <div id={chartId} style="width: 100%; height: 100%;"></div>
  307. </div>
  308. <Popup
  309. v-model:show={searchStatus.value}
  310. closeable
  311. round
  312. position="bottom"
  313. teleport="body"
  314. >
  315. <div class={styles.popupContainer}>
  316. <div class={styles.popupTitle}>筛选</div>
  317. <div class={styles.popupSearchList}>
  318. <div class={styles.popupSection}>
  319. <div class={styles.title}>
  320. <span>时间</span>
  321. </div>
  322. <div class={styles.timeCount}>
  323. <p
  324. onClick={() => onChangeTime('MONTH')}
  325. class={currentType.value === 'MONTH' ? styles.active : ''}
  326. >
  327. 本月
  328. </p>
  329. <p
  330. onClick={() => onChangeTime('THREE_MONTH')}
  331. class={
  332. currentType.value === 'THREE_MONTH' ? styles.active : ''
  333. }
  334. >
  335. 近三个月
  336. </p>
  337. <p
  338. onClick={() => onChangeTime('HALF_YEAR')}
  339. class={
  340. currentType.value === 'HALF_YEAR' ? styles.active : ''
  341. }
  342. >
  343. 近半年
  344. </p>
  345. <p
  346. onClick={() => onChangeTime('YEAR')}
  347. class={currentType.value === 'YEAR' ? styles.active : ''}
  348. >
  349. 近一年
  350. </p>
  351. </div>
  352. <div class={styles.timeRang}>
  353. <p
  354. class={[
  355. styles.timeInput,
  356. forms.startTimeStr && styles.hasValue
  357. ]}
  358. onClick={() => (forms.startTimeStatus = true)}
  359. >
  360. {forms.startTimeStr || '起始时间'}
  361. </p>
  362. <p class={styles.timeUnit}></p>
  363. <p
  364. class={[
  365. styles.timeInput,
  366. forms.endTimeStr && styles.hasValue
  367. ]}
  368. onClick={() => (forms.endTimeStatus = true)}
  369. >
  370. {forms.endTimeStr || '终止时间'}
  371. </p>
  372. </div>
  373. </div>
  374. </div>
  375. <div class={styles.popupBottom}>
  376. <Button
  377. round
  378. block
  379. type="default"
  380. onClick={() => {
  381. currentType.value = props.currentType
  382. resetTime(props.currentType)
  383. }}
  384. >
  385. 重置
  386. </Button>
  387. <Button
  388. round
  389. block
  390. type="primary"
  391. onClick={() => {
  392. if(!forms.startTimeStr || !forms.endTimeStr) {
  393. Toast('请选择时间范围')
  394. return
  395. }
  396. emit('confirm', {
  397. startTime: forms.startTimeStr,
  398. endTime: forms.endTimeStr
  399. })
  400. // 显示的时间
  401. showTimeRange.startTime = forms.startTimeStr
  402. showTimeRange.endTime = forms.endTimeStr
  403. searchStatus.value = false
  404. }}
  405. >
  406. 确定
  407. </Button>
  408. </div>
  409. </div>
  410. </Popup>
  411. {/* 开始日期 */}
  412. <Popup
  413. v-model:show={forms.startTimeStatus}
  414. position="bottom"
  415. round
  416. class={'popupBottomSearch'}
  417. teleport={'body'}
  418. >
  419. <DatetimePicker
  420. v-model={forms.startTime}
  421. type="date"
  422. formatter={formatterDatePicker}
  423. onCancel={() => (forms.startTimeStatus = false)}
  424. onConfirm={(val: any) => {
  425. forms.startTime = val
  426. forms.startTimeStr = dayjs(val).format('YYYY-MM-DD')
  427. forms.startTimeStatus = false
  428. forms.endTime = null as any
  429. forms.endTimeStr = ''
  430. forms.endTimeMinDate = dayjs(val || new Date()).toDate()
  431. forms.endTimeMaxDate = dayjs(val || new Date())
  432. .add(1, 'year')
  433. .toDate()
  434. currentType.value = '' as any
  435. }}
  436. />
  437. </Popup>
  438. {/* 结束日期 */}
  439. <Popup
  440. v-model:show={forms.endTimeStatus}
  441. position="bottom"
  442. round
  443. class={'popupBottomSearch'}
  444. teleport={'body'}
  445. >
  446. <DatetimePicker
  447. v-model={forms.endTime}
  448. type="date"
  449. minDate={forms.endTimeMinDate}
  450. maxDate={forms.endTimeMaxDate}
  451. formatter={formatterDatePicker}
  452. onCancel={() => (forms.endTimeStatus = false)}
  453. onConfirm={(val: any) => {
  454. forms.endTime = val
  455. forms.endTimeStatus = false
  456. forms.endTimeStr = dayjs(val).format('YYYY-MM-DD')
  457. currentType.value = '' as any
  458. }}
  459. />
  460. </Popup>
  461. </div>
  462. )
  463. }
  464. })