添加报价回复率统计

添加环境变量
feature/2.0-sales-trade
尹诚诚 3 years ago
parent 32867201e2
commit 2be0264d1e

@ -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/

@ -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";

@ -1,19 +1,19 @@
import { makeAutoObservable, runInAction } from "mobx";
import * as dd from 'dingtalk-jsapi';
import * as dd from "dingtalk-jsapi";
import * as config from "../config";
//权限管理
class AuthStore {
constructor(rootStore) {
this.rootStore = rootStore;
makeAutoObservable(this);
//this.get_auth(); //放到钉钉环境才能开启
if (process.env.NODE_ENV == "production") {
this.get_auth(); //放到钉钉环境才能开启
}
}
auth = ['admin']; //开发时候用,正式环境留空
user = {name:'loading',userid:'...'};//开发时候用,正式环境留空
auth = ["admin"]; //开发时候用,正式环境留空
user = { name: "loading", userid: "..." }; //开发时候用,正式环境留空
has_permission(requireds) {
if (Object.keys(requireds).length == 0) {
@ -29,33 +29,31 @@ class AuthStore {
//请求权限
get_auth() {
let _this = this;
const CORPID = 'ding48bce8fd3957c96b';//企业的id
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;
let url = "/dingtalk/dingtalkwork/Getusers_auth?code=" + code;
//请求获取HT接口获取用户权限和用户信息
fetch(config.HT_HOST + url)
.then((response) => response.json())
.then((json) => {
.then(response => response.json())
.then(json => {
runInAction(() => {
_this.user = json.result;
_this.auth = json.result.authlist;
});
})
})
.catch((error) => {
console.log('fetch data failed', error);
.catch(error => {
console.log("fetch data failed", error);
});
},
onFail: function (err) {
console.log(err)
}
console.log(err);
},
});
}
}
export default AuthStore;

@ -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) => {

@ -55,8 +55,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 => {
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");
@ -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);
});

@ -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 = () => {
<UserSwitchOutlined /> 回复率
</span>
}
key="ResponseRateWhatsApp"></Tabs.TabPane>
<Tabs.TabPane
tab={
<span>
<UserSwitchOutlined /> 沟通数据
</span>
}
key="ResponseRateByWL"></Tabs.TabPane>
<Tabs.TabPane
tab={
@ -175,7 +183,16 @@ const Sale = () => {
</Tabs>
<Row>
<Col span={24}>
<Table dataSource={type_data.dataSource} columns={type_data.columns} size="small" rowKey={(record) => record.key} loading={sale_store.loading_table} pagination={false} scroll={{ x: "100%" }} />
<Table id="table_to_xlsx_sale" dataSource={type_data.dataSource} columns={type_data.columns} size="small" rowKey={(record) => record.key} loading={sale_store.loading_table} pagination={false} scroll={{ x: "100%" }} />
<Divider orientation="right" plain>
<a
onClick={() => {
const wb = utils.table_to_book(document.getElementById("table_to_xlsx_sale").getElementsByTagName("table")[0]);
writeFileXLSX(wb, "sale.xlsx");
}}>
导出excel
</a>
</Divider>
</Col>
<Col sm={24} lg={12}>
{sale_store.active_tab_key == "All" ? <Pie {...pie_config} /> : ""}

@ -30,13 +30,13 @@ const Sale_sub = () => {
<Spin size="large" spinning={sale_store.spinning}>
<Row gutter={{ sm: 16, lg: 32 }}>
{!comm.empty(type_data.dataSource) &&
type_data.dataSource.map((item) => {
type_data.dataSource.map(item => {
return (
<Col md={24} xxl={12}>
<Col key={"col-" + item.subType_name} md={24} xxl={12}>
<Divider>
<Tag color="#87d068">{item.subType_name}</Tag>
</Divider>
<Table key={item.subType} dataSource={item.data} columns={type_data.columns} size="small" rowKey={(record) => record.key} loading={sale_store.loading_table} pagination={false} scroll={{ x: "100%" }} />
<Table key={item.subType} dataSource={item.data} columns={type_data.columns} size="small" rowKey={record => record.key} loading={sale_store.loading_table} pagination={false} scroll={{ x: "100%" }} />
</Col>
);
})}

Loading…
Cancel
Save