statistics.jsx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. import React, { Component } from "react";
  2. import {
  3. AutoComplete,
  4. Button,
  5. DatePicker,
  6. Input,
  7. message,
  8. Modal,
  9. Select,
  10. Spin,
  11. Table,
  12. Tabs,
  13. Tag,
  14. Tooltip,
  15. } from "antd";
  16. import { ShowModal, getPercentage } from "@/tools";
  17. import { salesList, } from "@/dataDic"
  18. import { ChooseList } from "../../order/orderNew/chooseList";
  19. import $ from "jquery/src/ajax";
  20. import "./index.less";
  21. import moment from "moment";
  22. import DetailList from "./detaillist";
  23. import Cascaders from '../../../common/cascaders'
  24. const { TabPane } = Tabs;
  25. const { RangePicker } = DatePicker;
  26. class SignStatistics extends Component {
  27. constructor(props) {
  28. super(props);
  29. this.state = {
  30. pageNo: 1,
  31. loading: false,
  32. changeList: undefined,
  33. columns: [
  34. {
  35. title: "营销员",
  36. dataIndex: "name",
  37. key: "name",
  38. },
  39. {
  40. title: "签单客户",
  41. dataIndex: "userCount",
  42. key: "userCount",
  43. sorter: (a, b) => a.userCount - b.userCount,
  44. },
  45. {
  46. title: "签单个数",
  47. dataIndex: "orderCount",
  48. key: "orderCount",
  49. sorter: (a, b) => a.orderCount - b.orderCount,
  50. onCellClick: (r) => {
  51. let cellData = {};
  52. cellData["aid"] = r.id
  53. cellData["aname"] = r.name
  54. this.setState({
  55. showDetail: true,
  56. rowData: cellData,
  57. })
  58. },
  59. render: (text) =>
  60. <div
  61. style={{
  62. color: "#58A3FF",
  63. textDecoration: "underline",
  64. cursor: "pointer"
  65. }}>
  66. {text}
  67. </div>
  68. },
  69. {
  70. title: "签单总金额(万元)",
  71. dataIndex: "totalAmount",
  72. key: "totalAmount",
  73. sorter: (a, b) => a.totalAmount - b.totalAmount,
  74. },
  75. {
  76. title: "新签数",
  77. dataIndex: "newSignNumber",
  78. key: "newSignNumber",
  79. sorter: (a, b) => a.newSignNumber - b.newSignNumber,
  80. onCellClick: (r) => {
  81. let cellData = {};
  82. cellData["aid"] = r.id
  83. cellData["aname"] = r.name
  84. cellData["orderType"] = "1"
  85. this.setState({
  86. showDetail: true,
  87. rowData: cellData,
  88. })
  89. },
  90. render: (text) =>
  91. <div
  92. style={{
  93. color: "#58A3FF",
  94. textDecoration: "underline",
  95. cursor: "pointer"
  96. }}>
  97. {text}
  98. </div>
  99. },
  100. {
  101. title: "新签率",
  102. dataIndex: "newRatio",
  103. key: "newRatio",
  104. render: (text) =>
  105. <span >
  106. {getPercentage(text)}
  107. </span>
  108. },
  109. {
  110. title: "复购数",
  111. dataIndex: "repeatSignNumber",
  112. key: "repeatSignNumber",
  113. sorter: (a, b) => a.repeatSignNumber - b.repeatSignNumber,
  114. onCellClick: (r) => {
  115. let cellData = {};
  116. cellData["aid"] = r.id
  117. cellData["aname"] = r.name
  118. cellData["orderType"] = "2"
  119. this.setState({
  120. showDetail: true,
  121. rowData: cellData,
  122. })
  123. },
  124. render: (text) =>
  125. <div
  126. style={{
  127. color: "#58A3FF",
  128. textDecoration: "underline",
  129. cursor: "pointer"
  130. }}>
  131. {text}
  132. </div>
  133. },
  134. {
  135. title: "复购率",
  136. dataIndex: "repeatRatio",
  137. key: "repeatRatio",
  138. render: (text) =>
  139. <span >
  140. {getPercentage(text)}
  141. </span>
  142. },
  143. {
  144. title: "渠道签单数",
  145. dataIndex: "channelSignNumber",
  146. key: "channelSignNumber",
  147. sorter: (a, b) => a.channelSignNumber - b.channelSignNumber,
  148. onCellClick: (r) => {
  149. let cellData = {};
  150. cellData["aid"] = r.id
  151. cellData["aname"] = r.name
  152. cellData["orderType"] = "3"
  153. this.setState({
  154. showDetail: true,
  155. rowData: cellData,
  156. })
  157. },
  158. render: (text) =>
  159. <div
  160. style={{
  161. color: "#58A3FF",
  162. textDecoration: "underline",
  163. cursor: "pointer"
  164. }}>
  165. {text}
  166. </div>
  167. },
  168. {
  169. title: "渠道签单率",
  170. dataIndex: "channelRatio",
  171. key: "channelRatio",
  172. render: (text) =>
  173. <span >
  174. {getPercentage(text)}
  175. </span>
  176. },
  177. ],
  178. pagination: {
  179. defaultCurrent: 1,
  180. defaultPageSize: 10,
  181. showQuickJumper: true,
  182. pageSize: 99999,
  183. onChange: function (page) {
  184. this.loadData(page);
  185. }.bind(this),
  186. showTotal: function (total) {
  187. return "共" + total + "条数据";
  188. },
  189. },
  190. dataSource: [],
  191. searchInfor: {},
  192. params: {},
  193. showDetail: false,
  194. companyList: [],
  195. };
  196. this.loadData = this.loadData.bind(this);
  197. this.resetAll = this.resetAll.bind(this);
  198. this.changeList = this.changeList.bind(this);
  199. this.selectSuperId = this.selectSuperId.bind(this);
  200. this.supervisor = this.supervisor.bind(this);
  201. this.httpChange = this.httpChange.bind(this);
  202. this.blurChange = this.blurChange.bind(this);
  203. this.selectAuto = this.selectAuto.bind(this);
  204. this.exportExec = this.exportExec.bind(this);
  205. }
  206. //获取上级组织
  207. selectSuperId() {
  208. $.ajax({
  209. method: "get",
  210. dataType: "json",
  211. crossDomain: false,
  212. url: globalConfig.context + "/api/admin/organization/selectSuperId",
  213. data: {},
  214. success: function (data) {
  215. let theArr = [];
  216. if (data.error && data.error.length === 0) {
  217. for (let i = 0; i < data.data.length; i++) {
  218. let theData = data.data[i];
  219. theArr.push(
  220. <Select.Option value={theData.id} key={theData.id}>
  221. {theData.name}
  222. </Select.Option>
  223. );
  224. }
  225. this.setState({
  226. contactsOption: theArr,
  227. companyList: data.data,
  228. searchInfor: Object.assign(this.state.searchInfor, {
  229. depId: data.data[0].id,
  230. }),
  231. }, () => {
  232. this.loadData();
  233. });
  234. } else {
  235. message.warning(data.error[0].message);
  236. }
  237. }.bind(this),
  238. }).always(
  239. function () {
  240. this.setState({
  241. // loading: false,
  242. });
  243. }.bind(this)
  244. );
  245. }
  246. changeList(arr) {
  247. const newArr = [];
  248. this.state.columns.forEach((item) => {
  249. arr.forEach((val) => {
  250. if (val === item.title) {
  251. newArr.push(item);
  252. }
  253. });
  254. });
  255. this.setState({
  256. changeList: newArr,
  257. });
  258. }
  259. // 搜索
  260. submit() {
  261. this.setState({
  262. params: this.state.searchInfor
  263. })
  264. this.loadData()
  265. }
  266. loadData(pageNo) {
  267. const { searchInfor, pagination } = this.state;
  268. if (!searchInfor.deps) {
  269. message.warn("请选择你要查询的部门!")
  270. return
  271. }
  272. this.setState({
  273. loading: true,
  274. });
  275. let datas = Object.assign(searchInfor, {
  276. pageNo: pageNo || 1,
  277. pageSize: pagination.pageSize,
  278. });
  279. $.ajax({
  280. method: "get",
  281. dataType: "json",
  282. crossDomain: false,
  283. url: globalConfig.context + "/api/admin/statistis/signStatistics",
  284. data: datas,
  285. success: function (data) {
  286. ShowModal(this);
  287. if (data.error && data.error.length === 0) {
  288. if (data.data.list) {
  289. pagination.current = data.data.pageNo;
  290. pagination.total = data.data.totalCount;
  291. if (data.data && data.data.list && !data.data.list.length) {
  292. pagination.current = 0;
  293. pagination.total = 0;
  294. }
  295. this.setState({
  296. dataSource: data.data.list,
  297. pagination: this.state.pagination,
  298. pageNo: data.data.pageNo,
  299. });
  300. } else {
  301. this.setState({
  302. dataSource: data.data,
  303. pagination: false,
  304. });
  305. }
  306. } else {
  307. message.warning(data.error[0].message);
  308. }
  309. this.setState({
  310. loading: false,
  311. });
  312. }.bind(this),
  313. }).always(
  314. function () {
  315. this.setState({
  316. loading: false,
  317. });
  318. }.bind(this)
  319. );
  320. }
  321. resetAll() {
  322. const { companyList } = this.state
  323. this.setState(
  324. {
  325. // searchInfor: { depId: companyList[0].id },
  326. searchInfor: JSON.parse(JSON.stringify({})),
  327. params: JSON.parse(JSON.stringify({})),
  328. aname: "",
  329. dataSource: [],
  330. },
  331. () => {
  332. this.Cascaders.empty();
  333. // this.loadData();
  334. }
  335. );
  336. }
  337. supervisor(e) {
  338. $.ajax({
  339. method: "get",
  340. dataType: "json",
  341. crossDomain: false,
  342. url: globalConfig.context + "/api/admin/customer/listAdminByName",
  343. data: {
  344. adminName: e,
  345. status: "2",
  346. },
  347. success: function (data) {
  348. if (data.error && data.error.length === 0) {
  349. this.setState({
  350. customerArr: data.data,
  351. });
  352. } else {
  353. message.warning(data.error[0].message);
  354. }
  355. }.bind(this),
  356. }).always(function () { }.bind(this));
  357. }
  358. httpChange(e) {
  359. if (e.length >= 1) {
  360. this.supervisor(e);
  361. }
  362. this.setState({
  363. aname: e,
  364. });
  365. }
  366. selectAuto(value, options) {
  367. this.setState({
  368. aname: value,
  369. });
  370. }
  371. blurChange(e) {
  372. const { searchInfor } = this.state
  373. let theType = undefined;
  374. let contactLists = this.state.customerArr || [];
  375. if (e) {
  376. contactLists.map(function (item) {
  377. if (item.name == e.toString()) {
  378. theType = item.id;
  379. }
  380. });
  381. }
  382. this.setState({
  383. searchInfor: Object.assign(searchInfor, {
  384. aid: theType,
  385. }),
  386. });
  387. }
  388. // 导出Excel
  389. exportExec() {
  390. this.setState({
  391. exportPendingLoading: true,
  392. });
  393. message.config({
  394. duration: 20,
  395. });
  396. let loading = message.loading("下载中...");
  397. let data = this.state.searchInfor;
  398. $.ajax({
  399. method: "get",
  400. dataType: "json",
  401. crossDomain: false,
  402. url:
  403. globalConfig.context + "/api/admin/statistis/signStatistics/export",
  404. data,
  405. success: function (data) {
  406. if (data.error.length === 0) {
  407. this.download(data.data);
  408. } else {
  409. message.warning(data.error[0].message);
  410. }
  411. }.bind(this),
  412. }).always(
  413. function () {
  414. loading();
  415. this.setState({
  416. exportPendingLoading: false,
  417. });
  418. }.bind(this)
  419. );
  420. }
  421. // 下载
  422. download(fileName) {
  423. window.location.href =
  424. globalConfig.context + "/open/download?fileName=" + fileName;
  425. }
  426. // 关闭弹窗
  427. closeDesc() {
  428. this.setState({
  429. showDetail: false,
  430. // params: {},
  431. });
  432. this.loadData(this.state.pageNo);
  433. }
  434. componentWillMount() {
  435. // this.selectSuperId();
  436. }
  437. render() {
  438. const { searchInfor, showDetail, rowData, params, dataSource } = this.state
  439. const dataSources = this.state.customerArr || [];
  440. const options = dataSources.map((group) => (
  441. <Select.Option key={group.id} value={group.name}>
  442. {group.name}
  443. </Select.Option>
  444. ));
  445. return (
  446. <div>
  447. <div className="user-content">
  448. <div className="content-title">
  449. <span style={{ fontWeight: 900, fontSize: 16 }}>签单客户数据统计(销售角度)</span>
  450. </div>
  451. <Tabs defaultActiveKey="1" className="test">
  452. <TabPane tab="搜索" key="1">
  453. <div
  454. className="user-search"
  455. style={{
  456. marginTop: "10px",
  457. marginLeft: "10px",
  458. marginRight: "10px",
  459. }}
  460. >
  461. <span style={{ marginRight: "10px" }}>
  462. <Cascaders
  463. ref={node => this.Cascaders = node}
  464. placeholder="选择部门"
  465. id="id"
  466. name="name"
  467. children="list"
  468. height={28}
  469. onSel={(e) => {
  470. searchInfor["deps"] = JSON.stringify(e);
  471. this.setState({
  472. searchInfor: searchInfor,
  473. });
  474. }}
  475. />
  476. {/* <Select
  477. placeholder="选择部门"
  478. style={{ width: 200 }}
  479. value={searchInfor.depId}
  480. onChange={(e) => {
  481. this.setState({
  482. searchInfor: Object.assign(searchInfor, {
  483. depId: e,
  484. }),
  485. });
  486. }}
  487. notFoundContent="未获取到上级组织列表"
  488. >
  489. {this.state.contactsOption}
  490. </Select> */}
  491. </span>
  492. <span style={{ marginRight: "10px" }}>
  493. <AutoComplete
  494. className="certain-category-search"
  495. dropdownClassName="certain-category-search-dropdown"
  496. dropdownMatchSelectWidth={false}
  497. style={{ width: 160 }}
  498. dataSource={options}
  499. placeholder="输入姓名"
  500. value={this.state.aname}
  501. onChange={this.httpChange}
  502. filterOption={true}
  503. onBlur={this.blurChange}
  504. onSelect={this.selectAuto}
  505. >
  506. <Input />
  507. </AutoComplete>
  508. </span>
  509. <span style={{ marginRight: "10px" }}>
  510. <Select
  511. style={{ width: 160 }}
  512. placeholder="签单金额"
  513. value={searchInfor.amountType}
  514. onChange={(e) => {
  515. this.setState({
  516. searchInfor: Object.assign(searchInfor, {
  517. amountType: e,
  518. }),
  519. });
  520. }}
  521. >
  522. <Option value="0">全部</Option>
  523. <Option value="1">小于10万</Option>
  524. <Option value="2">10-20万</Option>
  525. <Option value="3">20-30万</Option>
  526. <Option value="4">30万以上</Option>
  527. </Select>
  528. </span>
  529. <span style={{ marginRight: "10px" }}>
  530. <span style={{ marginRight: "5px", marginBottom: "10px" }}>
  531. 签单时间 :
  532. </span>
  533. <RangePicker
  534. style={{ marginRight: "10px", marginBottom: "10px" }}
  535. value={[
  536. searchInfor.signTimeStart
  537. ? moment(searchInfor.signTimeStart)
  538. : null,
  539. searchInfor.signTimeEnd ? moment(searchInfor.signTimeEnd) : null,
  540. ]}
  541. onChange={(data, dataString) => {
  542. this.setState({
  543. searchInfor: Object.assign(searchInfor, {
  544. signTimeStart: dataString[0],
  545. signTimeEnd: dataString[1],
  546. }),
  547. });
  548. }}
  549. />
  550. </span>
  551. <Button
  552. type="primary"
  553. onClick={() => {
  554. this.submit()
  555. }}
  556. style={{ marginRight: "10px", marginBottom: "10px" }}
  557. >
  558. 搜索
  559. </Button>
  560. <Button
  561. onClick={this.resetAll}
  562. style={{ marginRight: "10px", marginBottom: "10px" }}
  563. >
  564. 重置
  565. </Button>
  566. </div>
  567. </TabPane>
  568. <TabPane tab="更改表格显示数据" key="2">
  569. <div style={{ marginLeft: 10 }}>
  570. <Spin spinning={this.state.loading}>
  571. <ChooseList
  572. columns={this.state.columns}
  573. changeFn={this.changeList}
  574. changeList={this.state.changeList}
  575. top={55}
  576. margin={11}
  577. />
  578. </Spin>
  579. </div>
  580. </TabPane>
  581. <TabPane tab="导出" key="3">
  582. <div style={{ float: "left" }}>
  583. <Button
  584. type="primary"
  585. loading={this.state.exportPendingLoading}
  586. onClick={this.exportExec}
  587. style={{ margin: 10 }}
  588. >
  589. 导出Excel
  590. </Button>
  591. </div>
  592. </TabPane>
  593. </Tabs>
  594. <div className="patent-table">
  595. <div style={{ padding: "0px 10px 10px" }}>
  596. 总计:签单客户&nbsp;
  597. <span style={{ color: "#58A3FF" }}>{dataSource.reduce((prev, next) => { return prev + next.userCount }, 0)}</span>&nbsp;个,签单个数&nbsp;
  598. <span style={{ color: "#58A3FF" }}>{dataSource.reduce((prev, next) => { return prev + next.orderCount }, 0)}</span>&nbsp;个
  599. </div>
  600. <Spin spinning={this.state.loading}>
  601. <Table
  602. bordered
  603. size="middle"
  604. columns={
  605. this.state.changeList
  606. ? this.state.changeList
  607. : this.state.columns
  608. }
  609. dataSource={dataSource}
  610. pagination={false}
  611. />
  612. </Spin>
  613. </div>
  614. </div>
  615. {
  616. showDetail &&
  617. <Modal
  618. visible={showDetail}
  619. title={<div>
  620. <span style={{ marginRight: 20 }}>客户订单列表</span>
  621. <span style={{ color: "red" }}>归属人:{rowData["aname"]}</span>
  622. </div>}
  623. width="90%"
  624. footer={null}
  625. onCancel={this.closeDesc.bind(this)}
  626. >
  627. <DetailList
  628. data={Object.assign(rowData, params)}
  629. />
  630. </Modal>
  631. }
  632. </div>
  633. );
  634. }
  635. }
  636. export default SignStatistics;