wuqun 2 years ago
parent
commit
7317638c69
  1. 27
      api/app/lib/controllers/salePerformance/index.js
  2. 14
      api/app/lib/models/customerContactsFollup.js
  3. 50
      api/app/lib/models/sale.js
  4. 63
      api/app/lib/models/salePerformance.js
  5. 8
      api/app/lib/routes/salePerformance/index.js
  6. 3
      api/config.js
  7. 196
      web/client/src/layout/components/header/index.jsx
  8. 7
      web/client/src/sections/auth/containers/login.jsx
  9. 4
      web/client/src/sections/business/actions/index.js
  10. 16
      web/client/src/sections/business/actions/performanceSummary.js
  11. 52
      web/client/src/sections/business/constants/index.js
  12. 4
      web/client/src/sections/business/containers/customer/customerContactFollowup.jsx
  13. 6
      web/client/src/sections/business/containers/index.js
  14. 225
      web/client/src/sections/business/containers/performanceReport/importPerformanceSummaryModal.jsx
  15. 487
      web/client/src/sections/business/containers/performanceReport/performanceSummary.jsx
  16. 3
      web/client/src/sections/business/nav-item.jsx
  17. 7
      web/client/src/sections/business/routes.js
  18. 12
      web/client/src/sections/business/style.less
  19. 3
      web/client/src/utils/webapi.js
  20. 2
      web/config.js
  21. 2
      web/package.json

27
api/app/lib/controllers/salePerformance/index.js

@ -0,0 +1,27 @@
'use strict';
// 查询业绩汇总表-->关联sale表
async function getSalePerformance(ctx, next) {
const { type } = ctx.params;
const models = ctx.fs.dc.models;
let rslt = null;
try {
rslt = await models.sale.findAll({
order: [['id', 'DESC']],
include:[{
model:models.salePerformance
}]
// where: { type: type }
})
ctx.status = 200
ctx.body = rslt
} catch (error) {
ctx.fs.logger.error(`path:${ctx.path},error:${error}`)
ctx.status = 400;
ctx.body = { name: 'FindAllError', message: '获取失败' }
}
}
module.exports = {
getSalePerformance,
}

14
api/app/lib/models/customerContactsFollup.js

@ -7,7 +7,7 @@ module.exports = dc => {
const CustomerContactsFollowup = sequelize.define("customerContactsFollowup", { const CustomerContactsFollowup = sequelize.define("customerContactsFollowup", {
id: { id: {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
allowNull: false, allowNull: true,
primaryKey: true, primaryKey: true,
field: "id", field: "id",
autoIncrement: true, autoIncrement: true,
@ -19,7 +19,7 @@ module.exports = dc => {
}, },
items: { items: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: false,
field: "items", field: "items",
}, },
department: { department: {
@ -29,7 +29,7 @@ module.exports = dc => {
}, },
sale: { sale: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: false,
field: "sale", field: "sale",
}, },
updatetime: { updatetime: {
@ -39,22 +39,22 @@ module.exports = dc => {
}, },
customerContacts: { customerContacts: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: false,
field: "customer_contacts", field: "customer_contacts",
}, },
phone: { phone: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: false,
field: "phone", field: "phone",
}, },
visitStyle: { visitStyle: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: false,
field: "visit_style", field: "visit_style",
}, },
itemText: { itemText: {
type: DataTypes.STRING, type: DataTypes.STRING,
allowNull: true, allowNull: false,
field: "item_text", field: "item_text",
} }
}, { }, {

50
api/app/lib/models/sale.js

@ -0,0 +1,50 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const sale = sequelize.define("sale", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
field: "id",
autoIncrement: true,
},
department: {
type: DataTypes.STRING,
allowNull: true,
field: "department",
},
business: {
type: DataTypes.STRING,
allowNull: true,
field: "business",
},
sale: {
type: DataTypes.STRING,
allowNull: true,
field: "sale",
},
hiredate: {
type: DataTypes.DATE,
allowNull: false,
field: "hiredate",
},
regularDate: {
type: DataTypes.DATE,
allowNull: false,
field: "regular_date",
},
pepId: {
type: DataTypes.INTEGER,
allowNull: true,
field: "pep_id",
}
}, {
tableName: "sale",
});
dc.models.sale = sale;
return sale;
};

63
api/app/lib/models/salePerformance.js

@ -0,0 +1,63 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const salePerformance = sequelize.define("salePerformance", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
field: "id",
autoIncrement: true,
},
amount: {
type: DataTypes.STRING,
allowNull: false,
field: "amount",
},
actualPerformance: {
type: DataTypes.STRING,
allowNull: false,
field: "actual_performance",
},
assessmentPerformance: {
type: DataTypes.STRING,
allowNull: false,
field: "assessment_performance",
},
saleName : {
type: DataTypes.STRING,
allowNull: false,
field: "sale_name",
},
month: {
type: DataTypes.INTEGER,
allowNull: false,
field: "month",
},
year: {
type: DataTypes.INTEGER,
allowNull: false,
field: "year",
},
saleId: {
type: DataTypes.INTEGER,
allowNull: false,
field: "sale_id",
},
task: {
type: DataTypes.INTEGER,
allowNull: false,
field: "task",
}
}, {
tableName: "sale_performance",
});
const { sale } = dc.models;
sale.hasMany(salePerformance, { foreighKey: 'saleName', targetKey: "sale" })
dc.models.salePerformance = salePerformance;
return salePerformance;
};

8
api/app/lib/routes/salePerformance/index.js

@ -0,0 +1,8 @@
'use strict';
const report = require('../../controllers/salePerformance');
module.exports = function (app, router, opts) {
app.fs.api.logAttr['GET/salePerformance'] = { content: '业绩汇总表', visible: false };
router.get('/salePerformance', report.getSalePerformance);
};

3
api/config.js

@ -55,6 +55,9 @@ const CLICKHOUST_PASSWORD = process.env.CLICKHOUST_PASSWORD || flags.clickHouseP
const CLICKHOUST_PEP_EMIS = process.env.CLICKHOUST_PEP_EMIS || flags.clickHousePepEmis const CLICKHOUST_PEP_EMIS = process.env.CLICKHOUST_PEP_EMIS || flags.clickHousePepEmis
const CLICKHOUST_HR = process.env.CLICKHOUST_HR || flags.clickHouseHr const CLICKHOUST_HR = process.env.CLICKHOUST_HR || flags.clickHouseHr
if ( if (
!RC_DB !RC_DB
|| !IOTA_REDIS_SERVER_HOST || !IOTA_REDIS_SERVER_PORT || !IOTA_REDIS_SERVER_HOST || !IOTA_REDIS_SERVER_PORT

196
web/client/src/layout/components/header/index.jsx

@ -7,99 +7,91 @@ import { headerItems } from './contant';
import "./index.less"; import "./index.less";
const Header = (props) => { const Header = (props) => {
const { dispatch, history, user, actions, socket, tochange } = props; const { dispatch, history, user, actions, socket, tochange } = props;
return ( return (
<> <>
<div id="top-slider"> <div id="top-slider">
<Nav <Nav
mode={"horizontal"} mode={"horizontal"}
onClick={({ itemKey }) => { onClick={({ itemKey }) => {
if (itemKey == "logout") { if (itemKey == "logout") {
dispatch(actions.auth.logout(user)); dispatch(actions.auth.logout(user));
if (socket) { if (socket) {
socket.disconnect(); socket.disconnect();
} }
history.push(`/signin`); history.push(`/signin`);
} }
}} }}
style={{ style={{
height: 48, height: 48,
minWidth: 520, minWidth: 520,
background: '#1D2343', background: '#1D2343',
backgroundSize: "100% 100%", backgroundSize: "100% 100%",
color: "white", color: "white",
}} }}
header={{ header={{
logo: ( logo: (
<div style={{ display: 'flex' }}> <div style={{ display: 'flex' }}>
<div style={{ marginLeft: 16, width: 24, height: 24 }}> <div style={{ marginLeft: 16, width: 24, height: 24 }}>
<img src="/assets/images/background/username.png" alt="" style={{ width: 24, marginRight: -10 }} /> <img src="/assets/images/background/username.png" alt="" style={{ width: 24, marginRight: -10 }} />
</div> </div>
<div style={{ fontFamily: 'YouSheBiaoTiHei', fontSize: 18, marginLeft: 12 }}> <div style={{ fontFamily: 'YouSheBiaoTiHei', fontSize: 18, marginLeft: 12 }}>
数据中心 数据中心
</div> </div>
</div> </div>
// <img // <img
// src="/assets/images/install/long_logo.png" // src="/assets/images/install/long_logo.png"
// style={{ display: "inline-block", width: 200, height: 40, marginLeft: -24 }} // style={{ display: "inline-block", width: 200, height: 40, marginLeft: -24 }}
// /> // />
), ),
// text: ( // text: (
// <> // <>
// {/* <SplitButtonGroup style={{ marginLeft: 10 }} aria-label=""> */} // {/* <SplitButtonGroup style={{ marginLeft: 10 }} aria-label=""> */}
// <Button theme="solid" type="primary" style={{ width: 52, height: 24, background: '#005ABD' }}></Button> // <Button theme="solid" type="primary" style={{ width: 52, height: 24, background: '#005ABD' }}></Button>
// {/* <Dropdown onVisibleChange={(v) => { }} menu={{}} trigger="click" position="bottomRight"> */} // {/* <Dropdown onVisibleChange={(v) => { }} menu={{}} trigger="click" position="bottomRight"> */}
// <Button style={{ width: 16, height: 24, background: '#005ABD' }} theme="solid" type="primary" icon={<IconTreeTriangleDown />}></Button> // <Button style={{ width: 16, height: 24, background: '#005ABD' }} theme="solid" type="primary" icon={<IconTreeTriangleDown />}></Button>
// {/* </Dropdown> */} // {/* </Dropdown> */}
// {/* </SplitButtonGroup> */} // {/* </SplitButtonGroup> */}
// </> // </>
// ), // ),
}} }}
footer={ footer={
<> <>
{headerItems.map((item, index) => { {headerItems.map((item, index) => {
if (item.hasOwnProperty('items')) { if (item.hasOwnProperty('items')) {
return ( return (
<Nav.Sub <Nav.Sub
key={index + 'a'} key={index + 'a'}
itemKey={item.itemKey} itemKey={item.itemKey}
text={item.text} text={item.text}
dropdownStyle={{ color: '#F2F3F5' }} dropdownStyle={{ color: '#F2F3F5' }}
> >
{item.hasOwnProperty('items') && item.items.map((ite, idx) => ( {item.hasOwnProperty('items') && item.items.map((ite, idx) => (
<Nav.Item key={idx + 'd'} itemKey={ite.itemKey} text={ite.text} onClick={() => { tochange(ite); }} /> <Nav.Item key={idx + 'd'} itemKey={ite.itemKey} text={ite.text} onClick={() => { tochange(ite); }} />
))} ))}
</Nav.Sub> </Nav.Sub>
) )
} }
else { else {
return ( return (
<div key='console' style={{ display: 'inline-flex' }}> <div key='console' style={{ display: 'inline-flex' }}>
<img src="/assets/images/background/console.png" style={{ width: 24, marginRight: -10 }} /> <img src="/assets/images/background/console.png" style={{ width: 24, marginRight: -10 }} />
<Nav.Item key={index + 'a'} itemKey={item.itemKey} text={item.text} onClick={() => { tochange(item) }} /> <Nav.Item key={index + 'a'} itemKey={item.itemKey} text={item.text} onClick={() => { tochange(item) }} />
</div> </div>
) )
} }
})} })}
<div style={{ display: 'flex', alignItems: 'center' }}> <div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ marginLeft: 16, width: 24, height: 24 }}> <div style={{ marginLeft: 16, width: 24, height: 24 }}>
<img src="/assets/images/background/username.png" alt="" style={{ width: '100%', height: '100%' }} /> <img src="/assets/images/background/username.png" alt="" style={{ width: '100%', height: '100%' }} />
</div> </div>
<div style={{ color: '#92CBFF', fontSize: 12, marginLeft: 10, cursor: "pointer" }} onClick={() => { <div style={{ color: '#92CBFF', fontSize: 12, marginLeft: 10, cursor: "pointer" }}>{user?.name}</div>
dispatch(actions.auth.logout(user)); </div>
if (socket) {
socket.disconnect();
}
history.push(`/signin`);
}}>
退出
</div>
</div>
{/* <Nav.Sub {/* <Nav.Sub
itemKey={"user"} itemKey={"user"}
text={ text={
<div <div
@ -145,21 +137,21 @@ const Header = (props) => {
退出 退出
</div> </div>
</Nav.Sub> */} </Nav.Sub> */}
</> </>
} }
/> />
</div> </div>
</> </>
); );
}; };
function mapStateToProps(state) { function mapStateToProps (state) {
const { global, auth, webSocket } = state; const { global, auth, webSocket } = state;
return { return {
actions: global.actions, actions: global.actions,
user: auth.user, user: auth.user,
socket: webSocket.socket, socket: webSocket.socket,
}; };
} }
export default connect(mapStateToProps)(Header); export default connect(mapStateToProps)(Header);

7
web/client/src/sections/auth/containers/login.jsx

@ -8,7 +8,7 @@ import { IconLock, IconUser } from '@douyinfe/semi-icons';
import '../style.less' import '../style.less'
const Login = props => { const Login = props => {
const { dispatch, user, error, actions, apiRoot, isRequesting } = props const { dispatch, user, error, actions, apiRoot, isRequesting,webPepUrl } = props
const form = useRef(); const form = useRef();
useEffect(() => { useEffect(() => {
@ -23,6 +23,8 @@ const Login = props => {
dispatch(push('/businessManagement/pmReport/reserveItemsReporting')); dispatch(push('/businessManagement/pmReport/reserveItemsReporting'));
localStorage.setItem('poms_open_sider', JSON.stringify(["pmReport"])) localStorage.setItem('poms_open_sider', JSON.stringify(["pmReport"]))
localStorage.setItem('poms_selected_sider', JSON.stringify(["businessManagement"])) localStorage.setItem('poms_selected_sider', JSON.stringify(["businessManagement"]))
}else{
window.location.href = `${webPepUrl}/signin`
} }
}, [user]) }, [user])
@ -110,7 +112,8 @@ function mapStateToProps(state) {
error: auth.error, error: auth.error,
actions: global.actions, actions: global.actions,
apiRoot: global.apiRoot, apiRoot: global.apiRoot,
isRequesting: auth.isRequesting isRequesting: auth.isRequesting,
webPepUrl: global.webPepUrl,
} }
} }

4
web/client/src/sections/business/actions/index.js

@ -3,9 +3,11 @@ import * as reserveItem from './reserve-item';
import * as salersReport from './salers-report'; import * as salersReport from './salers-report';
import * as achievementReport from './achievement-report'; import * as achievementReport from './achievement-report';
import * as getCustomerContactsFollowup from './customerContactFollowup' import * as getCustomerContactsFollowup from './customerContactFollowup'
import * as getPerformanceSummary from './performanceSummary'
export default { export default {
...reserveItem, ...reserveItem,
...salersReport, ...salersReport,
...achievementReport, ...achievementReport,
...getCustomerContactsFollowup ...getCustomerContactsFollowup,
...getPerformanceSummary
} }

16
web/client/src/sections/business/actions/performanceSummary.js

@ -0,0 +1,16 @@
'use strict';
import { ApiTable, basicAction } from '$utils'
export function getPerformanceSummary() {//查询
return (dispatch) => basicAction({
type: "get",
dispatch: dispatch,
actionType: "GET_PERFORMANCE_SUMMARY",
url: `${ApiTable.getPerformanceSummary}`,
msg: { option: "查询业绩汇总表" },
reducer: { name: "performanceSummaryList", params: { noClear: true } },
});
}

52
web/client/src/sections/business/constants/index.js

@ -119,4 +119,56 @@ export const achievementColumnKeys = {
outProvince: '省外业务', outProvince: '省外业务',
repurchase: '复购业务', repurchase: '复购业务',
isreproduce: '可复制的业务路径', isreproduce: '可复制的业务路径',
}
export const performanceSummaryKeys = {
oneAmount:'一月合同金额',
oneActualPerformance:'一月实际业绩',
oneAssessmentPerformance:'一月考核业绩',
oneTask:'一月销售任务',
twoAmount:'二月合同金额',
twoActualPerformance:'二月实际业绩',
twoAssessmentPerformance:'二月考核业绩',
twoTask:'二月销售任务',
threeAmount:'三月合同金额',
threeActualPerformance:'三月实际业绩',
threeAssessmentPerformance:'三月考核业绩',
threeTask:'三月销售任务',
fourAmount:'四月合同金额',
fourActualPerformance:'四月实际业绩',
fourAssessmentPerformance:'四月考核业绩',
fourTask:'四月销售任务',
fiveActualPerformance:'五月实际业绩',
fiveAssessmentPerformance:'五月考核业绩',
fiveTask:'五月销售任务',
fiveAmount:'五月合同金额',
sixAmount:'六月合同金额',
sixActualPerformance:'六月实际业绩',
sixAssessmentPerformance:'六月考核业绩',
sixTask:'六月销售任务',
sevenAmount:'七月合同金额',
sevenActualPerformance:'七月实际业绩',
sevenAssessmentPerformance:'七月考核业绩',
sevenTask:'七月销售任务',
eightAmount:'八月合同金额',
eightActualPerformance:'八月实际业绩',
eightAssessmentPerformance:'八月考核业绩',
eightTask:'八月销售任务',
nineAmount:'九月合同金额',
nineActualPerformance:'九月实际业绩',
nineAssessmentPerformance:'九月考核业绩',
nineTask:'九月销售任务',
tenAmount:'十月合同金额',
tenActualPerformance:'十月实际业绩',
tenAssessmentPerformance:'十月考核业绩',
tenTask:'十月销售任务',
elevenActualPerformance:'十一月实际业绩',
elevenAssessmentPerformance:'十一月考核业绩',
elevenTask:'十一月销售任务',
elevenAmount:'十一月合同金额',
twelveAmount:'十二月合同金额',
twelveActualPerformance:'十二月实际业绩',
twelveAssessmentPerformance:'十二月考核业绩',
twelveTask:'十二月销售任务',
name:'销售人员'
} }

4
web/client/src/sections/business/containers/customer/customerContactFollowup.jsx

@ -48,7 +48,7 @@ const CustomerContactFollowup = (props) => {
render: (text, record) => text == null ? '---' : moment(text).format('YYYY-MM-DD') render: (text, record) => text == null ? '---' : moment(text).format('YYYY-MM-DD')
}, },
{ {
title: '客户联系人', title: '联系人',
dataIndex: 'customerContacts', dataIndex: 'customerContacts',
render: (text, record) => text == null ? '---' : text render: (text, record) => text == null ? '---' : text
}, },
@ -63,7 +63,7 @@ const CustomerContactFollowup = (props) => {
render: (text, record) => text == null ? '---' : text render: (text, record) => text == null ? '---' : text
}, },
{ {
title: '项目进展', title: '跟进内容',
dataIndex: 'itemText', dataIndex: 'itemText',
render: (text, record) => text == null ? '---' : text render: (text, record) => text == null ? '---' : text
} }

6
web/client/src/sections/business/containers/index.js

@ -9,7 +9,8 @@ import InvoicingDetails from './performanceReport/invoicingDetails';
import BackMoneyDetails from './performanceReport/backMoneyDetails'; import BackMoneyDetails from './performanceReport/backMoneyDetails';
import AchievementDetails from './performanceReport/achievementDetails'; import AchievementDetails from './performanceReport/achievementDetails';
import SalesDistributionDetails from './salesReport/salesDistributionDetails'; import SalesDistributionDetails from './salesReport/salesDistributionDetails';
import CustomerContactFollowup from './customer/customerContactFollowup' import CustomerContactFollowup from './customer/customerContactFollowup';
import PerformanceSummary from './performanceReport/performanceSummary'
export { export {
ReserveItemsReporting, ReserveItemsReporting,
@ -21,5 +22,6 @@ export {
BackMoneyDetails, BackMoneyDetails,
AchievementDetails, AchievementDetails,
SalesDistributionDetails, SalesDistributionDetails,
CustomerContactFollowup CustomerContactFollowup,
PerformanceSummary
}; };

225
web/client/src/sections/business/containers/performanceReport/importPerformanceSummaryModal.jsx

@ -0,0 +1,225 @@
'use strict';
import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import moment from 'moment';
import { Modal, Form, Button, Notification } from '@douyinfe/semi-ui';
import { IconUpload } from '@douyinfe/semi-icons';
import XLSX from 'xlsx';
import { performanceSummaryKeys } from '../../constants/index';
//
const ImportPerformanceSummaryModal = props => {
const { dispatch, actions, onCancel } = props;
const { businessManagement } = actions
const [msg, setMsg] = useState('');
const [loading, setLoading] = useState('');
const [postData, setPostData] = useState([]);
//
useEffect(() => {
}, []);
const confirm = () => {
if (postData.length) {
setLoading(true)
dispatch(businessManagement.importAchieveDetails(postData)).then(res => {
if (res.success) {
onCancel()
}
setLoading(false)
})
} else {
Notification.warning({ content: '没有数据可以提交,请上传数据文件', duration: 2 })
}
}
const dldCsvMb = () => {
//
let head = [];
Object.keys(performanceSummaryKeys).map(key => {
head.push(performanceSummaryKeys[key]);
})
head = head.join(',') + "\n";
//
//let data = 1 + ',' + 2 + ',' + 3 + ',' + 4 + ',' + 5
let templateCsv = "data:text/csv;charset=utf-8,\ufeff" + head;
//a
let link = document.createElement("a");
//a
link.setAttribute("href", templateCsv);
link.setAttribute("download", "业绩汇总表导入模板.csv");
//a
link.click();
}
const download = () => {
dldCsvMb();
}
const fileLimit = '.csv';
const getFileBlob = (url) => {
return new Promise((resolve, reject) => {
let request = new XMLHttpRequest()
request.open("GET", url, true)
request.responseType = "blob"
request.onreadystatechange = e => {
if (request.readyState == 4) {
if (request.status == 200) {
if (window.FileReader) {
let reader = new FileReader();
reader.readAsBinaryString(request.response);
reader.onload = event => {
try {
const { result } = event.target;
// excel
const workbook = XLSX.read(result, {
type: "binary",
cellDates: true,//true
codepage: 936//
});
let data = []; //
//
for (const sheet in workbook.Sheets) {
if (workbook.Sheets.hasOwnProperty(sheet)) {
data = data.concat(XLSX.utils.sheet_to_json(workbook.Sheets[sheet]));
}
}
resolve(data);//
} catch (e) {
reject("失败");
}
}
}
}
}
}
request.send();
})
}
// const judgeNull = (value) => {
// return value ? String(value).trim().replace(/\s*/g, "") : null;
// }
// const judgeNullTime = (v) => {
// //xlsxexcel
// //2020/8/1xlsx Fri Jul 31 2020 23:59:17 GMT+0800 43
// let a = new Date(v);
// a.setTime(a.getTime() + 43 * 1000);
// return v ? a : null;
// }
const judgeTimeValid = (time) => {
let valid = true;
if (!time) {
return valid;//
}
const ymd = /^((19|20)[0-9]{2})[\/\-]((0[1-9])|(1[0-2]))[\/\-]((0[1-9])|((1|2)[0-9])|(3[0-1]))$/;//
if (time instanceof Date) {
let timeStr = moment(time).format('YYYY/MM/DD');
if (!ymd.test(timeStr)) {
valid = false;
}
} else {
valid = false;
}
return valid;
}
return (
<Modal
title="导入" visible={true}
onOk={confirm} width={620}
confirmLoading={loading}
onCancel={() => {
setMsg('')
setLoading(false)
setPostData([])
onCancel()
}}
>
<div style={{ borderBottom: '1px solid #DCDEE0', margin: '0px -24px' }}></div>
<Form>
<Form.Upload
label={'业绩明细表'} labelPosition='left'
action={'/'} accept={fileLimit}
maxSize={200} limit={1}
onRemove={(currentFile, fileList, fileItem) => {
setMsg('');
setPostData([]);
}}
customRequest={(data) => {
const { file, onSuccess, onError } = data
getFileBlob(file.url).then(res => {
const error = (msg) => {
setMsg(msg)
onError({ message: msg })
}
if (res.length > 1000) {
error('一次性上传数据行数应小于1000行,请分批上传')
return
}
if (!res.length) {
error('请填写至少一行数据')
return
}
let postData = [];
let zzsPattern = /^[+]{0,1}(\d+)$/;//
for (let i = 0; i < res.length; i++) {
let d = res[i];
let obj = {};
Object.keys(performanceSummaryKeys).map(key => {
obj[key] = d[performanceSummaryKeys[key]] || null;
//}
})
let tValid = judgeTimeValid(obj.recConDate);
// if (!tValid) {
// error(`${i + 2}yyyy/mm/dd`)
// return
// }
// if (obj.isApproval && ['', ''].indexOf(obj.isApproval) == -1) {
// error(`${i + 2}`)
// return
// }
// //
// if (obj.repurchaseCount && !zzsPattern.test(obj.repurchaseCount)) {
// error(`${i + 2}`)
// return
// }
// if (obj.reproducible && ['', ''].indexOf(obj.reproducible) == -1) {
// error(`${i + 2}`)
// return
// }
postData.push(obj);
}
setPostData(postData)
console.log(postData,'=======================');
let msg = '文件解析完成,点击确定按钮上传保存!'
setMsg(msg)
onSuccess({ message: msg })
})
}}>
<Button icon={<IconUpload />} theme="light">
请选择文件
</Button>
</Form.Upload>
<span>{msg}</span>
<div style={{ color: '#ccc' }}>最大不超过200M导入文件需与
<span onClick={() => download()} style={{ cursor: 'pointer', color: '#0066FF' }}>导入模板</span>
一致</div>
</Form>
</Modal >
)
}
function mapStateToProps(state) {
const { auth, global } = state;
return {
user: auth.user,
actions: global.actions,
}
}
export default connect(mapStateToProps)(ImportPerformanceSummaryModal);

487
web/client/src/sections/business/containers/performanceReport/performanceSummary.jsx

@ -0,0 +1,487 @@
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import moment from 'moment'
import { Table, } from '@douyinfe/semi-ui';
import '../../style.less';
import ImportPerformanceSummaryModal from './importPerformanceSummaryModal'
import FileSaver from 'file-saver'
const AchievementDetails = (props) => {
const { dispatch, actions, performanceSummaryList } = props
const [importModalV, setImportModalV] = useState(false);
useEffect(() => {
dispatch(actions.businessManagement.getPerformanceSummary());
}, []);
let colums = [{ num: '1月', name: 'one' }, { num: '2月', name: 'two' }, { num: '3月', name: 'three' }, { num: '4月', name: 'four' }, { num: '5月', name: 'five' }, { num: '6月', name: 'six' }, { num: '7月', name: 'seven' }, { num: '8月', name: 'eight' }, { num: '9月', name: 'nine' }, { num: '10月', name: 'ten' }, { num: '11月', name: 'eleven' }, { num: '12月', name: 'twelve' }]
const columnsList = () => {
colums.forEach(e => {
columns.push({
title: e.num,
children: [
{
title: '合同金额',
dataIndex: e.name + '[amount]',
width: 130,
render: (text, record, index) => text
},
{
title: '实际业绩',
dataIndex: e.name + '[actualPerformance]',
width: 130,
render: (text, record, index) => text
},
{
title: '考核业绩',
dataIndex: e.name + '[assessmentPerformance]',
width: 130,
render: (text, record, index) => text
},
{
title: '销售任务',
dataIndex: e.name + '[task]',
width: 130,
render: (text, record, index) => text
},
{
title: '完成率%',
dataIndex: e.name + '[completion]',
width: 130,
render: (text, record, index) => {
let assessmentPerformance = record[e.name] ? record[e.name].assessmentPerformance : 0
let task = record[e.name] ? record[e.name].task : 0
return assessmentPerformance == 0 || task == 0 ? 0 : assessmentPerformance / task
}
}
]
}
)
})
}
let columns = [
{
title: '部门',
dataIndex: 'department',
width: 130,
// render: (text, record, index) => index + 1
},
{
title: '销售人员',
dataIndex: 'sale',
width: 130,
// render: (text, record, index) => index + 1
},
{
title: '业务线',
dataIndex: 'business',
width: 130,
// render: (text, record, index) => index + 1
},
{
title: '入职日期',
dataIndex: 'hiredate',
width: 130,
render: (text, record, index) => moment(text).format('YYYY-MM-DD')
},
{
title: '转正日期',
dataIndex: 'regularDate',
width: 130,
render: (text, record, index) => moment(text).format('YYYY-MM-DD')
},
{
title: '工龄',
dataIndex: 'workingYears',
width: 130,
render: (text, record, index) => {
let days = moment().diff(moment(record.hiredate).format('YYYY-MM-DD'), 'days')
let day = days / 365
return day.toFixed(1)
}
},
]
columnsList()
let arr = performanceSummaryList.map(e => {
e.salePerformances.forEach(i => {
if (i.month == 1) {
e.one = i
}
if (i.month == 2) {
e.two = i
}
if (i.month == 3) {
e.three = i
}
if (i.month == 4) {
e.four = i
}
if (i.month == 5) {
e.five = i
}
if (i.month == 6) {
e.six = i
}
if (i.month == 7) {
e.seven = i
}
if (i.month == 8) {
e.eight = i
}
if (i.month == 9) {
e.nine = i
}
if (i.month == 10) {
e.ten = i
}
if (i.month == 11) {
e.eleven = i
}
if (i.month == 12) {
e.twelve = i
}
})
return e
})
const exportDetail = () => {
let tableStyle = "text-align: center;font-size:21px"
let exportTable = `
<tr>
<th rowspan='2'><div style="${tableStyle}">部门</div></th>
<th rowspan='2'><div style="${tableStyle}">销售人员</div></th>
<th rowspan='2'><div style="${tableStyle}">业务线</div></th>
<th rowspan='2'><div style="${tableStyle}">入职日期</div></th>
<th rowspan='2'><div style="${tableStyle}">转正日期</div></th>
<th rowspan='2'><div style="${tableStyle}">工龄</div></th>
<th colspan="5"><div style="${tableStyle}">1</div></th>
<th colspan="5"><div style="${tableStyle}">2</div></th>
<th colspan="5"><div style="${tableStyle}">3</div></th>
<th colspan="2"><div style="${tableStyle}">一季度</div></th>
<th colspan="5"><div style="${tableStyle}">4</div></th>
<th colspan="5"><div style="${tableStyle}">5</div></th>
<th colspan="5"><div style="${tableStyle}">6</div></th>
<th colspan="2"><div style="${tableStyle}">二季度</div></th>
<th colspan="5"><div style="${tableStyle}">7</div></th>
<th colspan="5"><div style="${tableStyle}">8</div></th>
<th colspan="5"><div style="${tableStyle}">9</div></th>
<th colspan="2"><div style="${tableStyle}">三季度</div></th>
<th colspan="5"><div style="${tableStyle}">10</div></th>
<th colspan="5"><div style="${tableStyle}">11</div></th>
<th colspan="5"><div style="${tableStyle}">12</div></th>
<th colspan="2"><div style="${tableStyle}">四季度</div></th>
<th colspan="3"><div style="${tableStyle}">四季度</div></th>
<th rowspan='2'><div style="${tableStyle}">扣除转法务业绩金额</div></th>
<th rowspan='2'><div style="${tableStyle}">2022年最终业绩合计</div></th>
</tr>
<tr>
<th><div style="${tableStyle}">合同金额</div></th>
<th><div style="${tableStyle}">实际业绩</div></th>
<th><div style="${tableStyle}">考核业绩</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">合同金额</div></th>
<th><div style="${tableStyle}">实际业绩</div></th>
<th><div style="${tableStyle}">考核业绩</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">合同金额</div></th>
<th><div style="${tableStyle}">实际业绩</div></th>
<th><div style="${tableStyle}">考核业绩</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">合同金额</div></th>
<th><div style="${tableStyle}">实际业绩</div></th>
<th><div style="${tableStyle}">考核业绩</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">合同金额</div></th>
<th><div style="${tableStyle}">实际业绩</div></th>
<th><div style="${tableStyle}">考核业绩</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">合同金额</div></th>
<th><div style="${tableStyle}">实际业绩</div></th>
<th><div style="${tableStyle}">考核业绩</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">合同金额</div></th>
<th><div style="${tableStyle}">实际业绩</div></th>
<th><div style="${tableStyle}">考核业绩</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">合同金额</div></th>
<th><div style="${tableStyle}">实际业绩</div></th>
<th><div style="${tableStyle}">考核业绩</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">合同金额</div></th>
<th><div style="${tableStyle}">实际业绩</div></th>
<th><div style="${tableStyle}">考核业绩</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">合同金额</div></th>
<th><div style="${tableStyle}">实际业绩</div></th>
<th><div style="${tableStyle}">考核业绩</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">合同金额</div></th>
<th><div style="${tableStyle}">实际业绩</div></th>
<th><div style="${tableStyle}">考核业绩</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">合同金额</div></th>
<th><div style="${tableStyle}">实际业绩</div></th>
<th><div style="${tableStyle}">考核业绩</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">销售任务</div></th>
<th><div style="${tableStyle}">完成率</div></th>
<th><div style="${tableStyle}">合同金额合计</div></th>
<th><div style="${tableStyle}">实际业绩合计</div></th>
<th><div style="${tableStyle}">考核业绩合计</div></th>
</tr>
`;
//
if (JSON.stringify(arr) == '[]') {
message.warning('暂无导出的数据')
} else {
let allList = arr.map(item => {
const oneAssessmentPerformance = item.one && item.one.assessmentPerformance;
const oneTask = item.one && item.one.task;
const oneCompletion = oneAssessmentPerformance && oneTask && oneAssessmentPerformance / oneTask;
const twoAssessmentPerformance = item.two && item.two.assessmentPerformance;
const twoTask = item.two && item.two.task;
const twoCompletion = twoAssessmentPerformance && twoTask && twoAssessmentPerformance / twoTask;
const threeAssessmentPerformance = item.three && item.three.assessmentPerformance;
const threeTask = item.three && item.three.task;
const threeCompletion = threeAssessmentPerformance && threeTask && threeAssessmentPerformance / threeTask;
const fourAssessmentPerformance = item.four && item.four.assessmentPerformance;
const fourTask = item.four && item.four.task;
const fourCompletion = fourAssessmentPerformance && fourTask && fourAssessmentPerformance / fourTask;
const fiveAssessmentPerformance = item.five && item.five.assessmentPerformance;
const fiveTask = item.five && item.five.task;
const fiveCompletion = fiveAssessmentPerformance && fiveTask && fiveAssessmentPerformance / fiveTask;
const sixAssessmentPerformance = item.six && item.six.assessmentPerformance;
const sixTask = item.six && item.six.task;
const sixCompletion = sixAssessmentPerformance && sixTask && sixAssessmentPerformance / sixTask;
const sevenAssessmentPerformance = item.seven && item.seven.assessmentPerformance;
const sevenTask = item.seven && item.seven.task;
const sevenCompletion = sevenAssessmentPerformance && sevenTask && sevenAssessmentPerformance / sevenTask;
const eightAssessmentPerformance = item.eight && item.eight.assessmentPerformance;
const eightTask = item.eight && item.eight.task;
const eightCompletion = eightAssessmentPerformance && eightTask && eightAssessmentPerformance / eightTask;
const nineAssessmentPerformance = item.nine && item.nine.assessmentPerformance;
const nineTask = item.nine && item.nine.task;
const nineCompletion = nineAssessmentPerformance && nineTask && nineAssessmentPerformance / nineTask;
const tenAssessmentPerformance = item.ten && item.ten.assessmentPerformance;
const tenTask = item.ten && item.ten.task;
const tenCompletion = tenAssessmentPerformance && tenTask && tenAssessmentPerformance / tenTask;
const elevenAssessmentPerformance = item.eleven && item.eleven.assessmentPerformance;
const elevenTask = item.eleven && item.eleven.task;
const elevenCompletion = elevenAssessmentPerformance && elevenTask && elevenAssessmentPerformance / elevenTask;
const twelveAssessmentPerformance = item.twelve && item.twelve.assessmentPerformance;
const twelveTask = item.twelve && item.twelve.task;
const twelveCompletion = twelveAssessmentPerformance && twelveTask && twelveAssessmentPerformance / twelveTask;
let days = moment().diff(moment(item.hiredate).format('YYYY-MM-DD'), 'days')
let day = days / 365
const workingYears = day.toFixed(1)
return Object.assign({}, item, {
oneCompletion: oneCompletion,
twoCompletion: twoCompletion,
threeCompletion: threeCompletion,
oneQuarterTaskNnu: Number(oneTask ? oneTask : 0) + Number(twoTask ? twoTask : 0) + Number(threeTask ? threeTask : 0),
oneQuarterTask: (oneAssessmentPerformance || twoAssessmentPerformance || threeAssessmentPerformance) && (oneTask || twoTask || threeTask) && (oneAssessmentPerformance || 0 + twoAssessmentPerformance || 0 + threeAssessmentPerformance || 0) / (oneTask || 0 + twoTask || 0 + threeTask || 0),
fourCompletion: fourCompletion,
fiveCompletion: fiveCompletion,
sixCompletion: sixCompletion,
twoQuarterTaskNnu: Number(fourTask ? fourTask : 0) + Number(fiveTask ? fiveTask : 0) + Number(sixTask ? sixTask : 0),
twoQuarterTask: (fourAssessmentPerformance || fiveAssessmentPerformance || sixAssessmentPerformance) && (fourTask || fiveTask || sixTask) && (fourAssessmentPerformance || 0 + fiveAssessmentPerformance || 0 + sixAssessmentPerformance || 0) / (fourTask || 0 + fiveTask || 0 + sixTask || 0),
sevenCompletion: sevenCompletion,
eightCompletion: eightCompletion,
nineCompletion: nineCompletion,
threeQuarterTaskNnu: Number(sevenTask ? sevenTask : 0) + Number(eightTask ? eightTask : 0) + Number(nineTask ? nineTask : 0),
threeQuarterTask: (sevenAssessmentPerformance || eightAssessmentPerformance || nineAssessmentPerformance) && (sevenTask || eightTask || nineTask) && (sevenAssessmentPerformance || 0 + eightAssessmentPerformance || 0 + nineAssessmentPerformance || 0) / (sevenTask || 0 + eightTask || 0 + nineTask || 0),
tenCompletion: tenCompletion,
elevenCompletion: elevenCompletion,
twelveCompletion: twelveCompletion,
fourQuarterTaskNnu: Number(tenTask ? tenTask : 0) + Number(elevenTask ? elevenTask : 0) + Number(twelveTask ? twelveTask : 0),
fourQuarterTask: (tenAssessmentPerformance || elevenAssessmentPerformance || twelveAssessmentPerformance) && (tenTask || elevenTask || twelveTask) && (tenAssessmentPerformance || 0 + elevenAssessmentPerformance || 0 + twelveAssessmentPerformance || 0) / (tenTask || 0 + elevenTask || 0 + twelveTask || 0),
workingYears: workingYears,
allAmount: Number(item.one ? item.one.amount : 0) + Number(item.two ? item.two.amount : 0) + Number(item.three ? item.three.amount : 0) + Number(item.four ? item.four.amount : 0) + Number(item.five ? item.five.amount : 0) + Number(item.six ? item.six.amount : 0) + Number(item.seven ? item.seven.amount : 0) + Number(item.eight ? item.eight.amount : 0) + Number(item.nine ? item.nine.amount : 0) + Number(item.ten ? item.ten.amount : 0) + Number(item.eleven ? item.eleven.amount : 0) + Number(item.twelve ? item.twelve.amount : 0),
allactualPerformance: Number(item.one ? item.one.actualPerformance : 0) + Number(item.two ? item.two.actualPerformance : 0) + Number(item.three ? item.three.actualPerformance : 0) + Number(item.four ? item.four.actualPerformance : 0) + Number(item.five ? item.five.actualPerformance : 0) + Number(item.six ? item.six.actualPerformance : 0) + Number(item.seven ? item.seven.actualPerformance : 0) + Number(item.eight ? item.eight.actualPerformance : 0) + Number(item.nine ? item.nine.actualPerformance : 0) + Number(item.ten ? item.ten.actualPerformance : 0) + Number(item.eleven ? item.eleven.actualPerformance : 0) + Number(item.twelve ? item.twelve.actualPerformance : 0),
allassessmentPerformance: Number(item.one ? item.one.assessmentPerformance : 0) + Number(item.two ? item.two.assessmentPerformance : 0) + Number(item.three ? item.three.assessmentPerformance : 0) + Number(item.four ? item.four.assessmentPerformance : 0) + Number(item.five ? item.five.assessmentPerformance : 0) + Number(item.six ? item.six.assessmentPerformance : 0) + Number(item.seven ? item.seven.assessmentPerformance : 0) + Number(item.eight ? item.eight.assessmentPerformance : 0) + Number(item.nine ? item.nine.assessmentPerformance : 0) + Number(item.ten ? item.ten.assessmentPerformance : 0) + Number(item.eleven ? item.eleven.assessmentPerformance : 0) + Number(item.twelve ? item.twelve.assessmentPerformance : 0),
})
})
for (let d of allList) {
exportTable += `
<tr>
<td><div style="${tableStyle}">${d['department']}</div></td>
<td><div style="${tableStyle}">${d['sale']}</div></td>
<td><div style="${tableStyle}">${d['business']}</div></td>
<td><div style="${tableStyle}">${d['hiredate']}</div></td>
<td><div style="${tableStyle}">${d['regularDate']}</div></td>
<td><div style="${tableStyle}">${d['workingYears']}</div></td>
<td><div style="${tableStyle}">${d.one ? d.one.amount : ''}</div></td>
<td><div style="${tableStyle}">${d.one ? d.one.actualPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.one ? d.one.assessmentPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.one ? d.one.task : ''}</div></td>
<td><div style="${tableStyle}">${d.oneCompletion ? d.oneCompletion : ''}</div></td>
<td><div style="${tableStyle}">${d.two ? d.two.amount : ''}</div></td>
<td><div style="${tableStyle}">${d.two ? d.two.actualPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.two ? d.two.assessmentPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.two ? d.two.task : ''}</div></td>
<td><div style="${tableStyle}">${d.twoCompletion ? d.twoCompletion : ''}</div></td>
<td><div style="${tableStyle}">${d.three ? d.three.amount : ''}</div></td>
<td><div style="${tableStyle}">${d.three ? d.three.actualPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.three ? d.three.assessmentPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.three ? d.three.task : ''}</div></td>
<td><div style="${tableStyle}">${d.threeCompletion ? d.threeCompletion : ''}</div></td>
<td><div style="${tableStyle}">${d.oneQuarterTaskNnu == 0 ? '' : d.oneQuarterTaskNnu}</div></td>
<td><div style="${tableStyle}">${d.oneQuarterTask ? d.oneQuarterTask : ''}</div></td>
<td><div style="${tableStyle}">${d.four ? d.four.amount : ''}</div></td>
<td><div style="${tableStyle}">${d.four ? d.four.actualPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.four ? d.four.assessmentPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.four ? d.four.task : ''}</div></td>
<td><div style="${tableStyle}">${d.fourCompletion ? d.fourCompletion : ''}</div></td>
<td><div style="${tableStyle}">${d.five ? d.five.amount : ''}</div></td>
<td><div style="${tableStyle}">${d.five ? d.five.actualPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.five ? d.five.assessmentPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.five ? d.five.task : ''}</div></td>
<td><div style="${tableStyle}">${d.fiveCompletion ? d.fiveCompletion : ''}</div></td>
<td><div style="${tableStyle}">${d.six ? d.six.amount : ''}</div></td>
<td><div style="${tableStyle}">${d.six ? d.six.actualPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.six ? d.six.assessmentPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.six ? d.six.task : ''}</div></td>
<td><div style="${tableStyle}">${d.sixCompletion ? d.sixCompletion : ''}</div></td>
<td><div style="${tableStyle}">${d.twoQuarterTaskNnu == 0 ? '' : d.twoQuarterTaskNnu}</div></td>
<td><div style="${tableStyle}">${d.twoQuarterTask ? d.twoQuarterTask : ''}</div></td>
<td><div style="${tableStyle}">${d.seven ? d.seven.amount : ''}</div></td>
<td><div style="${tableStyle}">${d.seven ? d.seven.actualPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.seven ? d.seven.assessmentPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.seven ? d.seven.task : ''}</div></td>
<td><div style="${tableStyle}">${d.sevenCompletion ? d.sevenCompletion : ''}</div></td>
<td><div style="${tableStyle}">${d.eight ? d.eight.amount : ''}</div></td>
<td><div style="${tableStyle}">${d.eight ? d.eight.actualPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.eight ? d.eight.assessmentPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.eight ? d.eight.task : ''}</div></td>
<td><div style="${tableStyle}">${d.eightCompletion ? d.eightCompletion : ''}</div></td>
<td><div style="${tableStyle}">${d.nine ? d.nine.amount : ''}</div></td>
<td><div style="${tableStyle}">${d.nine ? d.nine.actualPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.nine ? d.nine.assessmentPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.nine ? d.nine.task : ''}</div></td>
<td><div style="${tableStyle}">${d.nineCompletion ? d.nineCompletion : ''}</div></td>
<td><div style="${tableStyle}">${d.threeQuarterTaskNnu == 0 ? '' : d.threeQuarterTaskNnu}</div></td>
<td><div style="${tableStyle}">${d.threeQuarterTask ? d.threeQuarterTask : ''}</div></td>
<td><div style="${tableStyle}">${d.ten ? d.ten.amount : ''}</div></td>
<td><div style="${tableStyle}">${d.ten ? d.ten.actualPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.ten ? d.ten.assessmentPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.ten ? d.ten.task : ''}</div></td>
<td><div style="${tableStyle}">${d.tenCompletion ? d.tenCompletion : ''}</div></td>
<td><div style="${tableStyle}">${d.eleven ? d.eleven.amount : ''}</div></td>
<td><div style="${tableStyle}">${d.eleven ? d.eleven.actualPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.eleven ? d.eleven.assessmentPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.eleven ? d.eleven.task : ''}</div></td>
<td><div style="${tableStyle}">${d.elevenCompletion ? d.elevenCompletion : ''}</div></td>
<td><div style="${tableStyle}">${d.twelve ? d.twelve.amount : ''}</div></td>
<td><div style="${tableStyle}">${d.twelve ? d.twelve.actualPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.twelve ? d.twelve.assessmentPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.twelve ? d.twelve.task : ''}</div></td>
<td><div style="${tableStyle}">${d.twelveCompletion ? d.twelveCompletion : ''}</div></td>
<td><div style="${tableStyle}">${d.fourQuarterTaskNnu == 0 ? '' : d.fourQuarterTaskNnu}</div></td>
<td><div style="${tableStyle}">${d.fourQuarterTask ? d.fourQuarterTask : ''}</div></td>
<td><div style="${tableStyle}">${d.allAmount ? d.allAmount : ''}</div></td>
<td><div style="${tableStyle}">${d.allactualPerformance ? d.allactualPerformance : ''}</div></td>
<td><div style="${tableStyle}">${d.allassessmentPerformance ? d.allassessmentPerformance : ''}</div></td>
<td><div style="${tableStyle}"></div></td>
<td><div style="${tableStyle}"></div></td>
</tr>
`
}
exportTable = `\uFEFF
<table style='text-alagin:center' border="1">
<tr>
<th colspan="30"><div style="${tableStyle}">储备项目明细表</div></th>
</tr>
${exportTable}
</table>
`;
let tempStrs = new Blob([exportTable], { type: 'text/xls' })
FileSaver.saveAs(tempStrs, `储备中项目明细${moment().format('YYYY-MM-DD')}.xls`)
}
}
return (
<>
<div style={{ padding: '0px 12px' }}>
<div style={{ display: 'flex' }}>
<div style={{ color: 'rgba(0,0,0,0.45)', fontSize: 14 }}>业务管理</div>
<div style={{ color: 'rgba(0,0,0,0.45)', fontSize: 14, margin: '0px 8px' }}>/</div>
<div style={{ color: 'rgba(0,0,0,0.45)', fontSize: 14 }}>业绩报表</div>
<div style={{ color: '#033C9A', fontSize: 14, margin: '0px 8px' }}>/</div>
<div style={{ color: '#033C9A', fontSize: 14 }}>业绩汇总表</div>
</div>
<div style={{ background: '#FFFFFF', boxShadow: '0px 0px 12px 2px rgba(220,222,224,0.2)', borderRadius: 2, padding: '20px ', marginTop: 9 }}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'baseline' }}>
<div style={{ width: 0, height: 20, borderLeft: '3px solid #0F7EFB', borderTop: '3px solid transparent', borderBottom: '3px solid transparent' }}></div>
<div style={{ fontFamily: "YouSheBiaoTiHei", fontSize: 24, color: '#033C9A', marginLeft: 8 }}>业绩汇总表</div>
<div style={{ marginLeft: 6, fontSize: 12, color: '#969799', fontFamily: "DINExp", }}>PERFORMANCE SUMMARY</div>
</div>
</div>
<div style={{ margin: '18px 0px' }}>
<div style={{ display: 'flex', margin: '16px 0px', justifyContent: 'space-between' }}>
<div style={{ display: 'flex' }}>
<div style={{ padding: '6px 20px', background: '#0F7EFB', color: '#FFFFFF', fontSize: 14, cursor: "pointer" }}
onClick={() => { setImportModalV(true); }}>
导入
</div>
<div style={{ padding: '6px 20px', background: '#00BA85', color: '#FFFFFF', fontSize: 14, cursor: "pointer", marginLeft: 18 }}
onClick={() => {
exportDetail()
}}>
导出全部
</div>
</div>
</div>
<div className='summaryList'>
<Table columns={columns} dataSource={arr} />
</div>
</div>
</div>
{
importModalV ? <ImportPerformanceSummaryModal
onCancel={() => {
setImportModalV(false);
}} /> : ''
}
</div>
</>
)
}
function mapStateToProps(state) {
const { auth, global, performanceSummaryList } = state;
return {
user: auth.user,
actions: global.actions,
performanceSummaryList: performanceSummaryList.data || []
};
}
export default connect(mapStateToProps)(AchievementDetails);

3
web/client/src/sections/business/nav-item.jsx

@ -43,6 +43,9 @@ export function getNavItem(user, dispatch) {
}, { }, {
itemKey: 'achievementDetails', itemKey: 'achievementDetails',
to: '/businessManagement/performanceReport/achievementDetails', text: '业绩明细表' to: '/businessManagement/performanceReport/achievementDetails', text: '业绩明细表'
}, {
itemKey: 'summary',
to: '/businessManagement/performanceReport/summary', text: '业绩汇总表'
}] }]
}, { }, {
itemKey: 'salesReport', itemKey: 'salesReport',

7
web/client/src/sections/business/routes.js

@ -1,6 +1,6 @@
import { import {
ReserveItemsReporting, ReserveItemsPeriodicStatistics, ReserveItemsDepSummary, ReserveItemsLostStatistics, ReserveItemsReporting, ReserveItemsPeriodicStatistics, ReserveItemsDepSummary, ReserveItemsLostStatistics,
ContractDetails, InvoicingDetails, BackMoneyDetails, AchievementDetails, SalesDistributionDetails, CustomerContactFollowup ContractDetails, InvoicingDetails, BackMoneyDetails, AchievementDetails, SalesDistributionDetails, CustomerContactFollowup,PerformanceSummary
} from './containers'; } from './containers';
export default [{ export default [{
@ -60,6 +60,11 @@ export default [{
key: 'achievementDetails', key: 'achievementDetails',
breadcrumb: '业绩明细表', breadcrumb: '业绩明细表',
component: AchievementDetails component: AchievementDetails
}, {
path: '/summary',
key: 'summary',
breadcrumb: '业绩汇总表',
component: PerformanceSummary
}] }]
}, { }, {
path: '/salesReport', path: '/salesReport',

12
web/client/src/sections/business/style.less

@ -7,3 +7,15 @@
} }
} }
} }
.summaryList{
margin-top: 20px;
.semi-table .semi-table-row:first-child .semi-table-row-head{
border-right: 1px solid #ccc;
}
.semi-table-thead > .semi-table-row > .semi-table-row-head{
border-right: 1px solid #ccc;
}
.semi-table-tbody > .semi-table-row > .semi-table-row-cell{
border-right: 1px solid #ccc;
}
}

3
web/client/src/utils/webapi.js

@ -31,7 +31,8 @@ export const ApiTable = {
getContractDetail: 'contract/detail', getContractDetail: 'contract/detail',
getInvoicingDetail: 'invoicing/detail', getInvoicingDetail: 'invoicing/detail',
//客户联系人对接跟进 //客户联系人对接跟进
getCustomerContactsFollowup:'customerContactsFollup' getCustomerContactsFollowup:'customerContactsFollup',
getPerformanceSummary:'salePerformance',
}; };
export const RouteTable = { export const RouteTable = {
apiRoot: "/api/root", apiRoot: "/api/root",

2
web/config.js

@ -28,7 +28,7 @@ const flags = args.parse(process.argv);
const API_URL = process.env.API_URL || flags.apiUrl; const API_URL = process.env.API_URL || flags.apiUrl;
const API_DC_URL = process.env.API_DC_URL || flags.apiHrUrl; const API_DC_URL = process.env.API_DC_URL || flags.apiHrUrl;
const FS_PEP_DOMAIN = process.FS_REPORT_DOMAIN || flags.domain; const FS_PEP_DOMAIN = process.env.FS_PEP_DOMAIN || flags.domain;
const WEB_PEP_URL = process.env.WEB_PEP_URL || flags.webPepUrl; const WEB_PEP_URL = process.env.WEB_PEP_URL || flags.webPepUrl;
// 七牛 // 七牛

2
web/package.json

@ -32,6 +32,7 @@
"css-loader": "^3.5.0", "css-loader": "^3.5.0",
"express": "^4.17.1", "express": "^4.17.1",
"file-loader": "^6.0.0", "file-loader": "^6.0.0",
"file-saver": "^2.0.5",
"html-webpack-plugin": "^4.5.0", "html-webpack-plugin": "^4.5.0",
"immutable": "^4.0.0-rc.12", "immutable": "^4.0.0-rc.12",
"less": "^3.12.2", "less": "^3.12.2",
@ -58,7 +59,6 @@
"echarts": "^5.3.3", "echarts": "^5.3.3",
"echarts-for-react": "^3.0.2", "echarts-for-react": "^3.0.2",
"ezuikit-js": "^0.6.1", "ezuikit-js": "^0.6.1",
"file-saver": "^2.0.5",
"fs-attachment": "^1.0.0", "fs-attachment": "^1.0.0",
"fs-web-server-scaffold": "^1.0.6", "fs-web-server-scaffold": "^1.0.6",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",

Loading…
Cancel
Save