statistics.jsx 18 KB

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