index.jsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. import React, { useState, useEffect } from 'react'
  2. import ProTable from '@ant-design/pro-table'
  3. import { connect } from 'umi'
  4. import { Row, Col, Divider, Select } from 'antd'
  5. import { AddressBook, City, Finance, Comment } from '@icon-park/react'
  6. import { DualAxes } from '@ant-design/charts'
  7. import consts from '@/consts'
  8. import { useModal } from '@/components/Modal'
  9. import { queryDashboard } from '@/services/dashboard'
  10. import ReminderList from './components/ReminderList'
  11. import LeaderBoard from './components/LeaderBoard'
  12. const cyclicalOp = [
  13. {
  14. label: '今天',
  15. value: 'today'
  16. },
  17. {
  18. label: '昨天',
  19. value: 'yesterday'
  20. },
  21. {
  22. label: '本周',
  23. value: 'week'
  24. },
  25. {
  26. label: '上周',
  27. value: 'lastWeek'
  28. },
  29. {
  30. label: '本月',
  31. value: 'month'
  32. },
  33. {
  34. label: '上月',
  35. value: 'lastMonth'
  36. },
  37. {
  38. label: '本季度',
  39. value: 'quarter'
  40. },
  41. {
  42. label: '上季度',
  43. value: 'lastQuarter'
  44. },
  45. {
  46. label: '本年',
  47. value: 'year'
  48. },
  49. {
  50. label: '去年',
  51. value: 'lastYear'
  52. }
  53. ]
  54. const Dashboard = ({ permData }) => {
  55. const { Option } = Select
  56. const [state, setState] = useState({
  57. visible: false,
  58. reminderData: [],
  59. leaderboard: [],
  60. clientChainRatio: {},
  61. customerChainRatio: {},
  62. businessChainRatio: {},
  63. serviceLogChainRatio: {},
  64. aggregation: {},
  65. char: [],
  66. thread: [],
  67. params: {}
  68. })
  69. const { toggleModal, setModalProps } = useModal()
  70. // 展示服务弹窗详情
  71. const handleReminderList = (dataType, reminderCyclical) => {
  72. setModalProps({
  73. width: '90vw',
  74. modalRender: () => <ReminderList dataType={dataType} reminderCyclical={reminderCyclical} />,
  75. onCancel: () => toggleModal()
  76. })
  77. toggleModal()
  78. }
  79. const initData = async payload => {
  80. const {
  81. code = -1,
  82. data: {
  83. reminderData = [],
  84. leaderboard = [],
  85. clientChainRatio = {},
  86. customerChainRatio = {},
  87. businessChainRatio = {},
  88. serviceLogChainRatio = {},
  89. aggregation = {},
  90. char = [],
  91. thread = []
  92. }
  93. } = await queryDashboard(payload)
  94. if (code === consts.RET_CODE.SUCCESS) {
  95. setState({
  96. ...state,
  97. reminderData,
  98. leaderboard,
  99. clientChainRatio,
  100. customerChainRatio,
  101. businessChainRatio,
  102. serviceLogChainRatio,
  103. aggregation,
  104. char,
  105. thread
  106. })
  107. }
  108. }
  109. useEffect(() => {
  110. initData(state.params)
  111. }, [state.params])
  112. const columns = [
  113. {
  114. title: '超过周期未服务提醒',
  115. dataIndex: 'name'
  116. },
  117. {
  118. title: '7天',
  119. dataIndex: 'day7',
  120. render: (name, record) => (
  121. // console.log(record)
  122. <span
  123. onClick={() => handleReminderList(record.type, '7day')}
  124. className="text-primary cursor-pointer hover:text-[#967bbd]">
  125. {name}
  126. </span>
  127. )
  128. },
  129. {
  130. title: '15天',
  131. dataIndex: 'day15',
  132. render: (name, record) => (
  133. <span
  134. onClick={() => handleReminderList(record.type, '15day')}
  135. className="text-primary cursor-pointer hover:text-[#967bbd]">
  136. {name}
  137. </span>
  138. )
  139. },
  140. {
  141. title: '30天',
  142. dataIndex: 'day30',
  143. render: (name, record) => (
  144. <span
  145. onClick={() => handleReminderList(record.type, '30day')}
  146. className="text-primary cursor-pointer hover:text-[#967bbd]">
  147. {name}
  148. </span>
  149. )
  150. },
  151. {
  152. title: '3个月',
  153. dataIndex: 'month3',
  154. render: (name, record) => (
  155. <span
  156. onClick={() => handleReminderList(record.type, '3month')}
  157. className="text-primary cursor-pointer hover:text-[#967bbd]">
  158. {name}
  159. </span>
  160. )
  161. },
  162. {
  163. title: '6个月',
  164. dataIndex: 'month6',
  165. render: (name, record) => (
  166. <span
  167. onClick={() => handleReminderList(record.type, '6month')}
  168. className="text-primary cursor-pointer hover:text-[#967bbd]">
  169. {name}
  170. </span>
  171. )
  172. },
  173. {
  174. title: '提醒逾期',
  175. dataIndex: 'remind',
  176. render: (name, record) => (
  177. <span
  178. onClick={() => handleReminderList(record.type, 'reminder')}
  179. className="text-primary cursor-pointer hover:text-[#967bbd]">
  180. {name}
  181. </span>
  182. )
  183. }
  184. ]
  185. const charData = state.char
  186. const threadData = state.thread
  187. // const uvBillData = [
  188. // {
  189. // time: '验证阶段',
  190. // value: 350,
  191. // type: '上个月'
  192. // },
  193. // {
  194. // time: '开发阶段',
  195. // value: 900,
  196. // type: '上个月'
  197. // },
  198. // {
  199. // time: '提案阶段',
  200. // value: 300,
  201. // type: '上个月'
  202. // },
  203. // {
  204. // time: '赢单',
  205. // value: 450,
  206. // type: '上个月'
  207. // },
  208. // {
  209. // time: '输单',
  210. // value: 470,
  211. // type: '上个月'
  212. // },
  213. // {
  214. // time: '验证阶段',
  215. // value: 220,
  216. // type: '本月'
  217. // },
  218. // {
  219. // time: '开发阶段',
  220. // value: 300,
  221. // type: '本月'
  222. // },
  223. // {
  224. // time: '提案阶段',
  225. // value: 250,
  226. // type: '本月'
  227. // },
  228. // {
  229. // time: '赢单',
  230. // value: 220,
  231. // type: '本月'
  232. // },
  233. // {
  234. // time: '输单',
  235. // value: 362,
  236. // type: '本月'
  237. // }
  238. // ]
  239. // const transformData = [
  240. // {
  241. // time: '验证阶段',
  242. // 增幅: 800
  243. // },
  244. // {
  245. // time: '开发阶段',
  246. // 增幅: 600
  247. // },
  248. // {
  249. // time: '提案阶段',
  250. // 增幅: 400
  251. // },
  252. // {
  253. // time: '赢单',
  254. // 增幅: 380
  255. // },
  256. // {
  257. // time: '输单',
  258. // 增幅: 220
  259. // }
  260. // ]
  261. const config = {
  262. data: [charData, threadData],
  263. height: 230,
  264. xField: 'time',
  265. yField: ['value', '增幅'],
  266. geometryOptions: [
  267. {
  268. geometry: 'column',
  269. isGroup: true,
  270. seriesField: 'type',
  271. color: ['#A088C4', '#605B67']
  272. },
  273. {
  274. geometry: 'line',
  275. lineStyle: { lineWidth: 2 },
  276. color: '#FEB51A'
  277. }
  278. ]
  279. }
  280. return (
  281. <div>
  282. <div className="shadow-card">
  283. <ProTable
  284. bordered
  285. rowKey={record => record.type + Date.now()}
  286. columns={columns}
  287. dataSource={state.reminderData}
  288. search={false}
  289. toolBarRender={false}
  290. pagination={false}
  291. />
  292. </div>
  293. <div className="mt-4">
  294. <Select
  295. options={permData?.workbench || []}
  296. dropdownMatchSelectWidth={false}
  297. defaultValue="oneself"
  298. onChange={e => setState({ ...state, params: { ...state.params, dataPermission: e } })}
  299. />
  300. <span className="ml-4">
  301. <Select
  302. options={cyclicalOp}
  303. dropdownMatchSelectWidth={false}
  304. defaultValue="month"
  305. onChange={e => setState({ ...state, params: { ...state.params, cyclical: e } })}
  306. />
  307. </span>
  308. </div>
  309. <div className="mt-4">
  310. <div className="p-4 bg-white border rounded-lg shadow-card">
  311. <Row gutter={16}>
  312. <Col className="gutter-row" span={6}>
  313. <div className="p-4 bg-white border rounded-lg">
  314. <Row>
  315. <Col span={6}>
  316. <div className="w-48px h-48px border rounded-48px bg-[#0c7cd5] flex items-center justify-center">
  317. {/* <span className="absolute top-9px left-9px"> */}
  318. <City size="26" fill="#fff" />
  319. {/* </span> */}
  320. </div>
  321. </Col>
  322. <Col span={9}>
  323. <h3 className="text-2xl">{state.customerChainRatio.count}</h3>
  324. <span>新增客户</span>
  325. </Col>
  326. <Col span={9} className="text-right">
  327. <h3 className="text-xl text-green-500">
  328. {state.customerChainRatio.percentage}
  329. </h3>
  330. <span>较上月</span>
  331. </Col>
  332. </Row>
  333. </div>
  334. </Col>
  335. <Col className="gutter-row" span={6}>
  336. <div className="p-4 bg-white border rounded-lg shadow-card">
  337. <Row>
  338. <Col span={6}>
  339. <div className="w-48px h-48px border rounded-48px bg-[#0c7cd5] flex items-center justify-center">
  340. <AddressBook size="26" fill="#fff" />
  341. </div>
  342. </Col>
  343. <Col span={9}>
  344. <h3 className="text-2xl">{state.clientChainRatio.count}</h3>
  345. <span>新增联系人</span>
  346. </Col>
  347. <Col span={9} className="text-right">
  348. <h3 className="text-xl text-red-500">{state.clientChainRatio.percentage}</h3>
  349. <span>较上月</span>
  350. </Col>
  351. </Row>
  352. </div>
  353. </Col>
  354. <Col className="gutter-row" span={6}>
  355. <div className="p-4 bg-white border rounded-lg">
  356. <Row>
  357. <Col span={6}>
  358. <div className="w-48px h-48px border rounded-48px bg-[#0c7cd5] flex items-center justify-center">
  359. <Finance size="26" fill="#fff" />
  360. </div>
  361. </Col>
  362. <Col span={9}>
  363. <h3 className="text-2xl">{state.businessChainRatio.count}</h3>
  364. <span>新增商机</span>
  365. </Col>
  366. <Col span={9} className="text-right">
  367. <h3 className="text-xl text-green-500">
  368. {state.businessChainRatio.percentage}
  369. </h3>
  370. <span>较上月</span>
  371. </Col>
  372. </Row>
  373. </div>
  374. </Col>
  375. <Col className="gutter-row" span={6}>
  376. <div className="p-4 bg-white border rounded-lg">
  377. <Row>
  378. <Col span={6}>
  379. <div className="w-48px h-48px border rounded-48px bg-[#0c7cd5] flex items-center justify-center">
  380. <Comment size="26" fill="#fff" />
  381. </div>
  382. </Col>
  383. <Col span={9}>
  384. <h3 className="text-2xl">{state.serviceLogChainRatio.count}</h3>
  385. <span>服务记录</span>
  386. </Col>
  387. <Col span={9} className="text-right">
  388. <h3 className="text-xl text-green-500">
  389. {state.serviceLogChainRatio.percentage}
  390. </h3>
  391. <span>较上月</span>
  392. </Col>
  393. </Row>
  394. </div>
  395. </Col>
  396. </Row>
  397. </div>
  398. </div>
  399. <div className="mt-4">
  400. <Row gutter={16}>
  401. <Col className="gutter-row" span={16}>
  402. <div className="p-4 bg-white border rounded-lg shadow-card">
  403. <div className="relative">
  404. <span>销售漏斗</span>
  405. <div className="absolute right-1 top-0">
  406. <Select defaultValue="销售漏斗组">
  407. <Option value="销售漏斗组">销售漏斗组</Option>
  408. </Select>
  409. </div>
  410. </div>
  411. <Divider />
  412. <DualAxes {...config} />
  413. </div>
  414. </Col>
  415. <Col className="gutter-row" span={8}>
  416. <div className="bg-white border rounded-lg shadow-card">
  417. <h4 className="px-12px py-16px border-b-1">数据汇总</h4>
  418. <ul>
  419. <li className="px-12px py-16px border-b-1">
  420. 新增客户<b className="px-1">{state.aggregation.customerCount}</b>个,服务
  421. <b className="px-1">{state.aggregation.customerServiceCount}</b>条
  422. </li>
  423. <li className="px-12px py-16px border-b-1">
  424. 新增联系人<b className="px-1">{state.aggregation.clientCount}</b>个,服务
  425. <b className="px-1">{state.aggregation.clientServiceCount}</b> 条
  426. </li>
  427. <li className="px-12px py-16px">
  428. 新增商机<b className="px-1">{state.aggregation.businessCount}</b>个,金额
  429. <b className="px-1">{state.aggregation.businessSum}</b> 元,服务
  430. <b className="px-1">{state.aggregation.businessServiceCount}</b>
  431. 条,赢单<b className="px-1">{state.aggregation.businessWinCount}</b>个
  432. </li>
  433. </ul>
  434. </div>
  435. </Col>
  436. </Row>
  437. </div>
  438. <div className="mt-4">
  439. <Row gutter={16}>
  440. <Col className="gutter-row" span={12}>
  441. <div className="bg-white border rounded-lg shadow-card">
  442. <LeaderBoard dataList={state.leaderboard} />
  443. </div>
  444. </Col>
  445. </Row>
  446. </div>
  447. </div>
  448. )
  449. }
  450. export default connect(({ global }) => ({
  451. permData: global.premData
  452. }))(Dashboard)