diff --git a/README.md b/README.md index ed55e83..b4a6edb 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,14 @@ # dashboard 海纳的数据化管理系统 + +### ant-design +https://ant-design.antgroup.com/components/select-cn + +### charts.ant.design +https://charts.ant.design/zh/examples/column/basic#basic + +### antv-g2plot +https://antv-g2plot.gitee.io/zh/examples/pie/basic/#legend-interaction + +### momentjs +http://momentjs.cn/docs/ \ No newline at end of file diff --git a/src/config.js b/src/config.js index b44be47..02b6d3c 100644 --- a/src/config.js +++ b/src/config.js @@ -2,5 +2,4 @@ import React from "react"; export const stores_Context = React.createContext(); export const DATE_FORMAT = "YYYY-MM-DD"; -//export const HT_HOST = 'https://p9axztuwd7x8a7.mycht.cn'; -export const HT_HOST = "http://202.103.68.100:890"; //889正式库 +export const HT_HOST = process.env.NODE_ENV == "production" ? "https://p9axztuwd7x8a7.mycht.cn" : "http://202.103.68.100:890"; \ No newline at end of file diff --git a/src/stores/AuthStore.js b/src/stores/AuthStore.js index 7e28d1b..0d1b910 100644 --- a/src/stores/AuthStore.js +++ b/src/stores/AuthStore.js @@ -1,61 +1,59 @@ -import {makeAutoObservable, runInAction} from "mobx"; -import * as dd from 'dingtalk-jsapi'; +import { makeAutoObservable, runInAction } from "mobx"; +import * as dd from "dingtalk-jsapi"; import * as config from "../config"; - //权限管理 class AuthStore { - - constructor(rootStore) { - this.rootStore = rootStore; - makeAutoObservable(this); - //this.get_auth(); //放到钉钉环境才能开启 - } - - auth = ['admin']; //开发时候用,正式环境留空 - user = {name:'loading',userid:'...'};//开发时候用,正式环境留空 - - has_permission(requireds) { - if (Object.keys(requireds).length == 0) { - return true; - } - let has_permission = requireds.filter(item => this.auth.includes(item)); - if (Object.keys(has_permission).length !== 0) { - return true; - } - return false; - } - - //请求权限 - get_auth() { - let _this = this; - const CORPID = 'ding48bce8fd3957c96b';//企业的id - dd.runtime.permission.requestAuthCode({ - corpId: CORPID, - onSuccess: function (res) { - console.log(res); - let code = res.code; - let url = '/dingtalk/dingtalkwork/Getusers_auth?code=' + code; - //请求获取HT接口获取用户权限和用户信息 - fetch(config.HT_HOST + url) - .then((response) => response.json()) - .then((json) => { - runInAction(() => { - _this.user = json.result; - _this.auth = json.result.authlist; - }) - }) - .catch((error) => { - console.log('fetch data failed', error); - }); - }, - onFail: function (err) { - console.log(err) - } - }); - } + constructor(rootStore) { + this.rootStore = rootStore; + makeAutoObservable(this); + if (process.env.NODE_ENV == "production") { + this.get_auth(); //放到钉钉环境才能开启 + } + } + + auth = ["admin"]; //开发时候用,正式环境留空 + user = { name: "loading", userid: "..." }; //开发时候用,正式环境留空 + + has_permission(requireds) { + if (Object.keys(requireds).length == 0) { + return true; + } + let has_permission = requireds.filter(item => this.auth.includes(item)); + if (Object.keys(has_permission).length !== 0) { + return true; + } + return false; + } + + //请求权限 + get_auth() { + let _this = this; + const CORPID = "ding48bce8fd3957c96b"; //企业的id + dd.runtime.permission.requestAuthCode({ + corpId: CORPID, + onSuccess: function (res) { + console.log(res); + let code = res.code; + let url = "/dingtalk/dingtalkwork/Getusers_auth?code=" + code; + //请求获取HT接口获取用户权限和用户信息 + fetch(config.HT_HOST + url) + .then(response => response.json()) + .then(json => { + runInAction(() => { + _this.user = json.result; + _this.auth = json.result.authlist; + }); + }) + .catch(error => { + console.log("fetch data failed", error); + }); + }, + onFail: function (err) { + console.log(err); + }, + }); + } } - export default AuthStore; - diff --git a/src/stores/OrdersStore.js b/src/stores/OrdersStore.js index b7bebb9..c197cc2 100644 --- a/src/stores/OrdersStore.js +++ b/src/stores/OrdersStore.js @@ -30,7 +30,7 @@ class OrdersStore { const date_picker_store = this.rootStore.date_picker_store; let _start_date = moment(date_picker_store.start_date.format(config.DATE_FORMAT)); let _end_date = moment(date_picker_store.end_date.format(config.DATE_FORMAT)); - return _end_date.diff(_start_date, "days"); + return _end_date.diff(_start_date, "days")+1; } handleChange_webcode = (value) => { diff --git a/src/stores/SaleStore.js b/src/stores/SaleStore.js index ce9c480..4530435 100644 --- a/src/stores/SaleStore.js +++ b/src/stores/SaleStore.js @@ -55,9 +55,9 @@ class SaleStore { this.date_title += ` ${date2_start}~${date2_end}`; } fetch(config.HT_HOST + url) - .then((response) => response.json()) - .then((json) => { - result=json.result1; + .then(response => response.json()) + .then(json => { + result = json.result1; // if (!comm.empty(json.result2) && !comm.empty(date_moment.start_date_cp)) { // let diff_days = date_moment.start_date.diff(date_moment.start_date_cp, "days"); // for (let item of json.result2) { @@ -74,7 +74,7 @@ class SaleStore { this.loading = false; }); }) - .catch((error) => { + .catch(error => { this.loading = false; console.log("fetch data failed", error); }); @@ -97,8 +97,8 @@ class SaleStore { this.date_title += ` ${date2_start}~${date2_end}`; } fetch(config.HT_HOST + url) - .then((response) => response.json()) - .then((json) => { + .then(response => response.json()) + .then(json => { if (!comm.empty(json.result2) && !comm.empty(date_moment.start_date_cp)) { } else { if (this.active_tab_key == "All") { @@ -155,11 +155,6 @@ class SaleStore { }, ], }, - { - title: "首次报价回复率", - children: [{ title: "", dataIndex: "FirstRspRate" }], - sorter: (a, b) => parseInt(b.FirstRspRate) - parseInt(a.FirstRspRate), - }, { title: "报价次数", children: [{ title: json.result1.reduce((a, b) => a + b.PriceTime, 0), dataIndex: "PriceTime" }], @@ -202,11 +197,69 @@ class SaleStore { }, ]; result.dataSource = json.result1; + } else if (this.active_tab_key == "ResponseRateWhatsApp") { + result.columns = [ + { + title: "", + children: [ + { + title: "", + dataIndex: "OPI_Name", + }, + ], + }, + { + title: "首次回复率", + children: [{ title: "", dataIndex: "firstReplayRate" }], + sorter: (a, b) => parseInt(b.firstReplayRate) - parseInt(a.firstReplayRate), + }, + { + title: "一次报价率", + children: [{ title: '', dataIndex: "FirstQuotationRate" }], + sorter: (a, b) => b.FirstQuotationRate - a.FirstQuotationRate, + }, + { + title: "一次报价回复率", + children: [{ title: '', dataIndex: "FirstQuotationReplayRate" }], + sorter: (a, b) => b.FirstQuotationReplayRate - a.FirstQuotationReplayRate, + }, + { + title: "一次报价回复周期", + children: [{ title: '', dataIndex: "FirstQuotationReplaytimeAVG" }], + sorter: (a, b) => b.FirstQuotationReplaytimeAVG - a.FirstQuotationReplaytimeAVG, + }, + { + title: "二次报价率", + children: [{ title: '', dataIndex: "SecondQuotationRate" }], + sorter: (a, b) => b.SecondQuotationRate - a.SecondQuotationRate, + }, + { + title: "二次报价回复率", + children: [{ title: '', dataIndex: "SecondQuotationReplayRate" }], + sorter: (a, b) => b.SecondQuotationReplayRate - a.SecondQuotationReplayRate, + }, + { + title: "二次报价回复周期", + children: [{ title: '', dataIndex: "SecondQuotationReplaytimeAVG" }], + sorter: (a, b) => b.SecondQuotationReplaytimeAVG - a.SecondQuotationReplaytimeAVG, + }, + { + title: "成团率", + children: [{ title: '', dataIndex: "COLI_SuccessRate" }], + sorter: (a, b) => b.COLI_SuccessRate - a.COLI_SuccessRate, + }, + { + title: "成团周期", + children: [{ title: '', dataIndex: "COLI_ConfirmTimeAVG" }], + sorter: (a, b) => b.COLI_ConfirmTimeAVG - a.COLI_ConfirmTimeAVG, + }, + ]; + result.dataSource = json.result1; } else { //if (this.active_tab_key == "Country") //获取类型的项目,去掉重复,作为列名 let type_name_arr = []; - json.result1.map((item) => { + json.result1.map(item => { type_name_arr[item.SubTypeSN] = { SubTypeSN: item.SubTypeSN, SubTypeName: item.SubTypeName }; }); @@ -215,17 +268,17 @@ class SaleStore { for (let item of json.result1) { let op_sn = "OP_" + item.OPI_SN; //顾问的SN - let items = json.result1.filter((d) => d.OPI_SN == item.OPI_SN); //筛选出有当前顾问的记录 + let items = json.result1.filter(d => d.OPI_SN == item.OPI_SN); //筛选出有当前顾问的记录 let total_data_value = items.length ? items.reduce((a, b) => a + b.COLI_YJLY, 0) : ""; //记录累加 if (comm.empty(type_data[op_sn])) { type_data[op_sn] = [{ key: item.OPI_SN }, { T_name: item.OPI_Name }, { T_total: total_data_value }]; } type_data[op_sn].push({ ["T_" + item.SubTypeSN]: item.COLI_YJLY }); } - Object.values(type_data).map((item) => { + Object.values(type_data).map(item => { type_data_arr.push( Object.assign( - ...item.map((it) => { + ...item.map(it => { return it; }) ) @@ -237,7 +290,7 @@ class SaleStore { ); Object.values(type_name_arr).map((item, index) => { let data_index = "T_" + item.SubTypeSN; - let items = type_data_arr.filter((d) => d[data_index]); //筛选出有对应类型的记录 + let items = type_data_arr.filter(d => d[data_index]); //筛选出有对应类型的记录 let total_data_value = items.length ? items.reduce((a, b) => a + b[data_index], 0) : ""; //记录累加 result.columns.push({ title: item.SubTypeName, @@ -253,7 +306,7 @@ class SaleStore { this.loading_table = false; }); }) - .catch((error) => { + .catch(error => { this.loading_table = false; console.log("fetch data failed", error); }); @@ -275,8 +328,8 @@ class SaleStore { this.date_title += ` ${date2_start}~${date2_end}`; } fetch(config.HT_HOST + url) - .then((response) => response.json()) - .then((json) => { + .then(response => response.json()) + .then(json => { if (!comm.empty(json.result2) && !comm.empty(date_moment.start_date_cp)) { } else { result.columns = [ @@ -315,7 +368,7 @@ class SaleStore { //数据处理,把相同类型放入同一个数组 let type_data = []; !comm.empty(json.result1) && - Object.values(json.result1).map((item) => { + Object.values(json.result1).map(item => { let subtype_sn = "type_" + item.subType; if (comm.empty(type_data[subtype_sn])) { type_data[subtype_sn] = { subType: item.subType, subType_name: item.subTypeVal, data: [item] }; @@ -331,7 +384,7 @@ class SaleStore { this.spinning = false; }); }) - .catch((error) => { + .catch(error => { this.spinning = false; console.log("fetch data failed", error); }); diff --git a/src/views/Sale.js b/src/views/Sale.js index 4b47cee..5964396 100644 --- a/src/views/Sale.js +++ b/src/views/Sale.js @@ -1,5 +1,5 @@ import React, { useContext, useEffect } from "react"; -import { Row, Col, Button, Tabs, Table, Space, Radio, Select } from "antd"; +import { Row, Col, Button, Tabs, Table, Divider, Radio, Select } from "antd"; import { ContainerOutlined, SearchOutlined,UserSwitchOutlined } from "@ant-design/icons"; import { stores_Context } from "../config"; import { Column, Pie } from "@ant-design/charts"; @@ -9,6 +9,7 @@ import { NavLink, useParams } from "react-router-dom"; import * as comm from "../utils/commons"; import * as config from "../config"; import GroupSelect from "../charts/GroupSelect"; +import { utils, writeFileXLSX } from "xlsx"; const Sale = () => { const { sale_store, date_picker_store } = useContext(stores_Context); @@ -140,6 +141,13 @@ const Sale = () => { 回复率 } + key="ResponseRateWhatsApp"> + + 沟通数据 + + } key="ResponseRateByWL"> { - record.key} loading={sale_store.loading_table} pagination={false} scroll={{ x: "100%" }} /> +
record.key} loading={sale_store.loading_table} pagination={false} scroll={{ x: "100%" }} /> + + { + const wb = utils.table_to_book(document.getElementById("table_to_xlsx_sale").getElementsByTagName("table")[0]); + writeFileXLSX(wb, "sale.xlsx"); + }}> + 导出excel + + {sale_store.active_tab_key == "All" ? : ""} diff --git a/src/views/Sale_sub.js b/src/views/Sale_sub.js index a2be3bd..ed7a97d 100644 --- a/src/views/Sale_sub.js +++ b/src/views/Sale_sub.js @@ -30,13 +30,13 @@ const Sale_sub = () => { {!comm.empty(type_data.dataSource) && - type_data.dataSource.map((item) => { + type_data.dataSource.map(item => { return ( - + {item.subType_name} -
record.key} loading={sale_store.loading_table} pagination={false} scroll={{ x: "100%" }} /> +
record.key} loading={sale_store.loading_table} pagination={false} scroll={{ x: "100%" }} /> ); })}