index.jsx 12 KB

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