Commit dc49be04 by zhaochengxiang

客户画像

parent 0c9c598a
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `yarn start`
Runs the app in the development mode.<br />
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br />
You will also see any lint errors in the console.
### `yarn test`
Launches the test runner in the interactive watch mode.<br />
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `yarn build`
Builds the app for production to the `build` folder.<br />
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br />
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `yarn eject`
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
### Analyzing the Bundle Size
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
### Making a Progressive Web App
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
### Advanced Configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
### Deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
### `yarn build` fails to minify
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
{
"name": "hnyc-home",
"version": "0.1.0",
"private": true,
"dependencies": {
"@antv/data-set": "^0.10.2",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"antd": "^3.26.0",
"axios": "^0.19.0",
"babel-polyfill": "^6.26.0",
"bizcharts": "^3.5.6",
"crypto-js": "^4.0.0",
"md5": "^2.2.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.0.1",
"react-scripts": "3.3.1",
"recharts": "^2.0.0-beta.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"proxy": "http://222.240.173.103:8012"
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
import React, { Component } from "react";
import {
BrowserRouter as Router,
Route, Switch
} from 'react-router-dom';
import Dashboard from "./view/Dashboard";
import mDashboard from "./view/mDashboard";
class App extends Component {
render() {
return (
<React.Fragment>
<Router>
<Switch>
<Route path='/dashboard/:id' component={Dashboard} />
<Route path='/m/dashboard/:id' component={mDashboard} />
</Switch>
</Router>
</React.Fragment>
);
}
}
export default App;
\ No newline at end of file
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: #eee;
}
html,
body,
#root {
height: 100%;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
import "babel-polyfill";
import React, { Suspense, lazy } from "react"
import ReactDOM from 'react-dom';
import { ConfigProvider } from 'antd';
import zh_CN from 'antd/es/locale-provider/zh_CN';
import 'antd/dist/antd.css';
import './index.css';
import './mixins.css';
import * as serviceWorker from './serviceWorker';
const App = lazy(() => import("./App"));
const app = (
<ConfigProvider locale={zh_CN}>
<Suspense fallback={<div className="text-center">正在加载界面...</div>}>
<App />
</Suspense>
</ConfigProvider>
);
ReactDOM.render(app, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>
.text-primary {
color: #4091b0 !important;
}
.border-bottom {
border-bottom-width: 1px;
border-bottom-style: solid;
}
.border-light {
border-color: #eeeeee;
}
.ant-layout {
background: #eee;
}
.ant-card {
border-radius: 10px;
}
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.0/8 are considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://bit.ly/CRA-PWA'
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl, {
headers: { 'Service-Worker': 'script' }
})
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect';
import axios from 'axios';
const baseURL = '';
const config = {
baseURL,
timeout: 3600000,
headers: {
'Cache-Control': 'no-cache,no-store,must-revalidate,max-age=-1,private'
},
responseType: 'json', // default
validateStatus: () => true
};
const callback = resp => {
if (resp.status === 401) {
console.log("session过期,请重新登录!");
return null;
}
else if (resp.status !== 200) {
throw resp;
}
return resp.data || resp;
}
const encode = config => {
let url = config.url||""
// get参数编码
if (config.method === 'get' && config.params) {
url += '?'
let keys = Object.keys(config.params)
for (let key of keys) {
url += `${key}=${encodeURIComponent(config.params[key])}&`
}
url = url.substring(0, url.length - 1)
config.params = {}
}
config.url = url
return config
};
export function GetJSON(url, params, cfg) {
const instance = axios.create({ ...config, ...cfg });
instance.interceptors.request.use(encode);
return instance.get(url, { params }).then(callback);
}
export function PostJSON(
url,
{ payload, params = {}, cfg }) {
const instance = axios.create({ ...config, ...cfg });
instance.interceptors.request.use(encode);
return instance.post(url, payload, { params }).then(callback);
}
\ No newline at end of file
.levelContent {
height: 40px;
display: flex;
align-items: center;
}
.levelContent span {
text-align: center;
width: 100%;
font-weight: bold;
font-size: 50px;
margin-bottom: 0;
}
.degreeContent {
height: 415px;
}
.degreeWrap {
max-height: 415px;
display: flex;
flex-wrap: wrap;
overflow: auto;
align-self: center;
}
.degreeWrapItem {
margin: 5px;
padding: 5px 10px;
border: 1px solid #d9d9d9;
border-radius: 4px;
}
.checked {
cursor: pointer;
}
.degreeWrapItem svg {
position: relative;
top: 2px;
color: #ff4d4f;
}
.checked svg {
color: #52c41a;
}
.degreeWrapItem .title {
margin-left: 10px;
font-size: .9rem;
}
.radarContent {
height: 326px;
}
.infoContentItem {
padding: 20px;
font-size:.9rem;
}
\ No newline at end of file
import React, { Component } from "react";
import md5 from "md5";
import CryptoJS from "crypto-js";
import { PostJSON } from "../util/axios";
import Basic from './Radar.recharts';
import './Dashboard.css';
import { Layout, Row, Col, Card, Tooltip, Spin, Divider } from 'antd';
const { Content } = Layout;
class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
"appid":"SJZT202001150001",
"appSecret":"09E9E259FF6BF30C5F93EC56DA634E59",
loading: false,
data: null,
cxdDimension: [
{key: "custActiveTimes",name: "累计参加活动的次数"},
{key: "custActivityExpecNums",name: "累计应到活动的次数"},
{key: "custTeamRank",name: "小组排名"},
{key: "custTeamScore",name: "组员评分"},
{key: "custTIsSDMAGHome",name: "是否小组之家"},
{key: "custTOSDMAG",name: "所属自律互助小组"},
{key: "custTSDMAG",name: "自律互助小组用户类型"},
],
gfdDimension:[
{key: "custAbnError",name: "库存异常"},
{key: "custBusinAddrInConformity",name: "与实际地址不符"},
{key: "custBusinLicInConformity",name: "与工商照不符"},
{key: "custBusinSuspend",name: "停业整顿"},
{key: "custCaseBehaviour",name: "有涉案行为"},
{key: "custCaseIllegalSource",name: "向无证户提供货源"},
{key: "custCaseManyTimes",name: "因涉烟违法行为被处罚两次以上"},
{key: "custCaseSaleFakeCigs",name: "销售假烟"},
{key: "custCaseSaleSmuggleCigs",name: "销售走私烟"},
{key: "custCentralizedSign",name: "集中签收预警"},
{key: "custDontOpen",name: "开店不开门"},
{key: "custIsSaleHigh",name: "销售天价烟"},
{key: "custNonAuthPerson",name: "非授权人取货预警"},
{key: "custNonAutoOrder",name: "非自主订货"},
{key: "custNonAutoPay",name: "非自主支付"},
{key: "custNotMatchLic",name: "人证不符"},
{key: "custRecoveryAd",name: "卷烟回收广告"},
{key: "custSaleOtherAreaCig",name: "销售未当卷烟"},
{key: "custSalePunish",name: "营销处罚类型"},
{key: "custSegBusinError",name: "档位与经营能力不符"},
{key: "custSusPectTransLic",name: "涉嫌出租转让许可证"},
{key: "custSuspectViolaPers",name: "疑似收购、运送、推销人员"},
{key: "custUnlicensed",name: "未亮证经营"},
],
czdDimension:[
{key: "custMonthOrderNum",name: "订货量(本月累计)"},
{key: "custMonthOrderSpecNum",name: "订购规格数(本月累计)"},
{key: "custMonthOrderStruct",name: "订单结构(本月累计)"},
{key: "custMonthOrderSum",name: "订货额(本月累计)"},
{key: "custMonthOutProProp",name: "省外烟比例"},
{key: "custMonthTotalRate",name: "月供总量使用率"},
{key: "custLastMonthOrderNum",name: "订货量(上月累计)"},
{key: "custLastMonthOrderSpecNum",name: "订购规格数(上月累计)"},
{key: "custLastMonthOrderStruct",name: "订单结构(上月月累计)"},
{key: "custLastMonthOrderSum",name: "订货额(上月累计)"},
{key: "custLastYearSameMonthOrderNum",name: "订货量(去年同期累计)"},
{key: "custLastYearSameMonthOrderStruct",name: "订单结构(去年同期累计)"},
{key: "custLastYearSameMonthOrderSum",name: "订货额(去年同期累计)"},
{key: "lastMonthOrderAmtWgt",name: "上月订货额月供系数"},
{key: "lastMonthOrderNumWgt",name: "上月订货量月供系数"},
{key: "lastYearSameMonthOrderAmtWgt",name: "去年同期订货额月供系数"},
{key: "lastYearSameMonthOrderNumWgt",name: "去年同期订货量月供系数"},
{key: "thisMonthOrderAmtWgt",name: "本月订货额月供系数"},
{key: "thisMonthOrderNumWgt",name: "本月订货量月供系数"},
],
phdDimension:[
{key: "custBrandCultInvest",name: "品牌培育投入"},
{key: "custDisplayStatusBad",name: "陈列状态差"},
{key: "custInfoCollectQuality",name: "信息采集质量"},
{key: "custIsReturn",name: "是否有退货行为"},
{key: "custLdmTempStorageRes",name: "暂存原因"},
{key: "custNonCooperation",name: "不配合检查"},
{key: "custNonEleSettleTimes",name: "非电子结算次数"},
{key: "custNoSignConfis",name: "未粘贴罚没烟标识"},
{key: "custNoTicketCounter",name: "未设罚没烟专柜"},
{key: "custPriceNotClear",name: "未明码标价"},
{key: "custSamplingRate",name: "出样率"},
{key: "custStandardizeSale",name: "罚没烟销售客户"},
{key: "custTerminalScanQuality",name: "终端扫码质量"},
{key: "custWaitingTimes",name: "接货等待次数"},
{key: "custWithoutSwipingCardTimes",name: "交接未刷卡次数"},
],
yjdDimension: [
{key: "custActivePayTimeError",name: "主动支付时间异常"},
{key: "custBaseInfoChange",name: "基础信息变更"},
{key: "custBrandSegBad",name: "紧俏品牌档位差"},
{key: "custBrandSourceNatureDivide",name: "品牌货源属性划分"},
{key: "custCaseManyTimes",name: "因涉烟违法行为被处罚两次以上"},
{key: "custCaseSupervision",name: "涉案户监管"},
{key: "custCaseSupervisionNotImplemented",name: "涉案户监管未落实"},
{key: "custCashPayRatio",name: "现金结算比例"},
{key: "custCrowdedBrandPutArea",name: "紧俏品牌投放面"},
{key: "custDayFocusPut",name: "集中时间(日)投放"},
{key: "custDayPay",name: "日清日结"},
{key: "custError",name: "异常客户"},
{key: "custErrorOrder",name: "异常订单"},
{key: "custExpensiveSingleSpecOrderError",name: "高价位卷烟单规格订购异常"},
{key: "custExpensiveSmokePutError",name: "高价位卷烟投放异常"},
{key: "custFocusPut",name: "集中客户投放"},
{key: "custInnerIPOrder",name: "内部IP订货"},
{key: "custLicInfoWarning",name: "客户办证信息"},
{key: "custManualImproveSeg",name: "手工提档"},
{key: "custMonthOrderBalanced",name: "客户月订货均衡性"},
{key: "custMonthSaleUnbalanced",name: "销售(月)不均衡"},
{key: "custOrderAdjust",name: "订单调整"},
{key: "custOrderFullRatio",name: "订单满足率"},
{key: "custOrderNum",name: "订货次数"},
{key: "custOrderWayChangeRatio",name: "订货方式变更率"},
{key: "custPayTypeChangeRatio",name: "结算方式变更率"},
{key: "custPayTypeError",name: "结算方式异常"},
{key: "custPutAreaLow",name: "投放面过低"},
{key: "custPutSpecRatio",name: "投放规格数比例"},
{key: "custReplaceCall",name: "疑似代调"},
{key: "custReplaceOrder",name: "疑似代订"},
{key: "custSaleNoteError",name: "销售记录异常"},
{key: "custSalePunish",name: "营销处罚类型"},
{key: "custSalesError",name: "销量异常"},
{key: "custSameBankCardPay",name: "一卡多结"},
{key: "custSameOrder",name: "五同订单"},
{key: "custSameOrderTel",name: "订货电话雷同"},
{key: "custSegChangeError",name: "档位变动异常"},
{key: "custSegRuleError",name: "客户分档规则异常"},
{key: "custSelectPointPut",name: "选点投放"},
{key: "custSelfCrowdedSourceProvide",name: "直营店紧俏货源供应"},
{key: "custSelfNature",name: "直营店属性"},
{key: "custSignPositionOffset",name: "客户签收位置偏移"},
{key: "custSimilarOrder",name: "雷同订单"},
{key: "custSingleSaleHigh",name: "是否存在单次销量过高"},
{key: "custSLHHSaleProportion",name: "大户销量占比"},
{key: "custSmokePriceError",name: "卷烟价格异常"},
{key: "custSmokeSaleError",name: "是否存在卷烟销售异常"},
{key: "custSpecialTypeOrderProportion",name: "特殊客户订购量占比"},
{key: "custStopAndReduceProvide",name: "停供减供"},
{key: "custStopProvideSituation",name: "货源停供情况"},
{key: "custWeekBrandFocusPut",name: "品牌集中(周)投放"},
{key: "custSegWarning",name: "客户分档"},
],
curDegree: '规范度',
curDimension: null
};
}
componentDidMount() {
const { appid, appSecret } = this.state;
const _decryptId = this.props.match.params.id;
var key = CryptoJS.enc.Utf8.parse("A930F2C3ACEA7B5B");
var iv = CryptoJS.enc.Utf8.parse('B54480C3A296C334');
var bytes = CryptoJS.AES.decrypt(CryptoJS.enc.Base64.stringify(CryptoJS.enc.Hex.parse(_decryptId)), key,{ iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
var _custId = bytes.toString(CryptoJS.enc.Utf8);
let time = this.getNowTime();
this.setState({ loading: true}, () => {
PostJSON("/api/tag/cust/getById",{ payload: {
"appid":appid,
"custId":_custId,
"time":time,
"sign":md5(`appid=${appid}&sign_type=MD5&custId=${_custId}&time=${time}&appSecret=${appSecret}`).toUpperCase(),
}}).then(function(data) {
if (data.return_code === '00000') {
const { gfdDimension } = this.state;
this.setState({ data: data.data, curDimension: this.curDimensionSort(data.data,gfdDimension), loading: false });
}
}.bind(this));
});
}
getNowTime = () => {
var date = new Date();
this.year = date.getFullYear();
this.month = date.getMonth() + 1;
this.date = date.getDate();
this.hour = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
this.minute = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
this.second = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
this.milliSeconds = date.getMilliseconds();
return `${this.year}${this.month}${this.hour}${this.minute}${this.second}${this.milliSeconds}`;
}
custCredit = data => {
var level = 0;
if (data === null) return '';
if (parseFloat(data.modelGFD) >= 1) level++;
if (parseFloat(data.modelYJD) >= 1) level++;
if (parseFloat(data.modelPHD) >= 1) level++;
if (parseFloat(data.modelCZD) > 0.9) level++;
if (parseFloat(data.modelCXD) > 0.9) level++;
return (level>2)?`${level}A`:'B';
}
custStatusMapper = status => {
if (status === null) return '';
if (status === '01') return '新增';
if (status === '02') return '有效';
if (status === '03') return '暂停';
if (status === '04') return '无效';
return '';
}
custAreaMapper = area => {
if (area === null) return '';
if (area === '01') return '工业区';
if (area === '02') return '居民区(村)';
if (area === '03') return '旅客中转区';
if (area === '04') return '学区';
if (area === '05') return '娱乐(旅游)区';
if (area === '06') return '政务(商务)区';
if (area === '11') return '商业(集贸)区';
if (area === '90') return '其他';
return '';
}
curDimensionSort = (data, dimension) => {
var checkedDimension = [];
var uncheckedDimension = [];
dimension.map(item=>{
(data[item.key]!=null)?checkedDimension.push(item):uncheckedDimension.push(item);
return item;
})
return checkedDimension.concat(uncheckedDimension);
}
onRadarSelected = (index) => {
const { gfdDimension, yjdDimension, phdDimension, czdDimension, cxdDimension, data} = this.state;
if (index === 0) {
this.setState({ curDegree: '规范度', curDimension: this.curDimensionSort(data,gfdDimension)});
} else if (index === 1) {
this.setState({ curDegree: '预警度', curDimension: this.curDimensionSort(data,yjdDimension)});
} else if (index === 2) {
this.setState({ curDegree: '配合度', curDimension: this.curDimensionSort(data,phdDimension)});
} else if (index === 3) {
this.setState({ curDegree: '成长度', curDimension: this.curDimensionSort(data,czdDimension)});
} else if (index === 4) {
this.setState({ curDegree: '诚信度', curDimension: this.curDimensionSort(data,cxdDimension)});
}
}
render() {
const { data, curDegree ,curDimension, loading } = this.state;
return (
<React.Fragment>
<Layout>
<Content style={{padding:'10px'}}>
<Row gutter={16}>
<Col md={8}>
<Card bordered={false}>
<h3 className="ant-typography text-primary">基本信息</h3>
<Spin spinning={loading}>
{data&&(
<>
<Row className="border-bottom border-light infoContentItem">
<Col md={12}>
<div>客户名称</div>
<div className="text-primary">{data.custName||''}</div>
</Col>
<Col md={12}>
<div>许可证号</div>
<div className="text-primary">{data.custLicNo||''}</div>
</Col>
</Row>
<Row className="border-bottom border-light infoContentItem">
<Col md={12}>
<div>营销部</div>
<div className="text-primary">{data.custSaleDeptName||''}</div>
</Col>
<Col md={12}>
<div>客户经理</div>
<div className="text-primary">{data.custSlsmanName||''}</div>
</Col>
</Row>
<Row className="border-bottom border-light infoContentItem">
<Col md={12}>
<div>档位</div>
<div className="text-primary">{data.custSeg||''}</div>
</Col>
<Col md={12}>
<div>批次</div>
<div className="text-primary">{data.custPeriodsName||''}</div>
</Col>
</Row>
<Row className="border-bottom border-light infoContentItem">
<Col md={12}>
<div>负责人姓名</div>
<div className="text-primary">{data.custManagerName||''}</div>
</Col>
<Col md={12}>
<div>许可证有效期止</div>
<div className="text-primary">{data.custLicValidateEnd||''}</div>
</Col>
</Row>
<Row className="border-bottom border-light infoContentItem">
<Col md={12}>
<div>市管员</div>
<div className="text-primary">{data.custGridPersonName||''}</div>
</Col>
<Col md={12}>
<div>市场类型</div>
<div className="text-primary">{data.custWorkPort||''}</div>
</Col>
</Row>
</>
)
}
</Spin>
</Card>
</Col>
<Col md={8}>
<Card bordered={false}>
<h3 className="ant-typography text-primary">客户评级</h3>
<Spin spinning={loading}>
<div className="levelContent">
{
data&&<span className="text-primary">{this.custCredit(data)}</span>
}
</div>
</Spin>
<Divider />
<Spin spinning={loading}>
<div className="radarContent">
{
data&&<Basic data={data} onSelect={this.onRadarSelected}></Basic>
}
</div>
</Spin>
</Card>
</Col>
<Col md={8}>
<Card bordered={false}>
<h3 className="ant-typography text-primary">{curDegree}</h3>
<Spin spinning={loading}>
<div className="degreeContent">
<div className="degreeWrap">
{
data&&curDimension.map((item,i) => {
if (data[item.key]!=null) {
return (
<div key={i} className="degreeWrapItem checked text-primary">
<Tooltip
placement="top"
overlayClassName="home"
title={data[item.key]}
>
<svg viewBox="64 64 896 896" focusable="false" data-icon="check-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true">
<path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 0 1-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"></path>
</svg>
<span className="title">{item.name}</span>
</Tooltip>
</div>
)
}
return (
<div key={i} className="degreeWrapItem">
<svg viewBox="64 64 896 896" focusable="false" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 0 1-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg>
<span className="title">{item.name}</span>
</div>
)
})
}
</div>
</div>
</Spin>
</Card>
</Col>
</Row>
</Content>
</Layout>
</React.Fragment>
);
}
}
export default Dashboard;
import React from "react";
import {
Chart,
Geom,
Axis,
Tooltip,
Coord,
//Legend
} from "bizcharts";
import DataSet from '@antv/data-set';
class Basic extends React.Component {
render() {
const _data = this.props.data;
const { DataView } = DataSet;
const data = [
{
item: "规范度",
value: parseFloat(_data.modelGFD) ,
b: 30
},
{
item: "预警度",
value: parseFloat(_data.modelYJD),
b: 70
},
{
item: "匹配度",
value: parseFloat(_data.modelPHD),
b: 60
},
{
item: "成熟度",
value: parseFloat(_data.modelCZD),
b: 50
},
{
item: "成长度",
value: parseFloat(_data.modelCXD),
b: 70
},
];
const dv = new DataView().source(data);
dv.transform({
type: "fold",
fields: ["value"],//, "b"
// 展开字段集
key: "user",
// key字段
value: "score" // value字段
});
const cols = {
score: {
min: 0,
max: 1
}
};
return (
<div>
<Chart onPlotClick={e=>{
e.data && console.log(e.data._origin)
}}
height={window.innerHeight}
data={dv}
padding={[20, 20, 95, 20]}
scale={cols}
forceFit
>
<Coord type="polar" radius={0.8} />
<Axis
name="item"
line={null}
tickLine={null}
grid={{
lineStyle: {
lineDash: null
},
hideFirstLine: false
}}
/>
<Tooltip />
<Axis
name="score"
line={null}
tickLine={null}
grid={{
type: "polygon",
lineStyle: {
lineDash: null
},
alternateColor: "rgba(0, 0, 0, 0.04)"
}}
/>
{/* <Legend name="user" marker="circle" offset={30} /> */}
<Geom type="area" position="item*score" color="user"/>
<Geom type="line" position="item*score" color="user" size={2} />
<Geom
type="point"
position="item*score"
color="user"
shape="circle"
size={4}
style={{
stroke: "#fff",
lineWidth: 1,
fillOpacity: 1
}}
select={[true, {
mode: 'single',
animate: true,
style: {
fill: 'red'
}
}]}
/>
</Chart>
</div>
);
}
}
export default Basic;
\ No newline at end of file
import React from "react";
import {
ResponsiveContainer, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Radar, Tooltip} from "recharts";
class Basic extends React.Component {
render() {
const _data = this.props.data;
var gfdLevel,yjdLevel,phdLevel,czdLevel,cxdLevel;
gfdLevel = (parseFloat(_data.modelGFD) >= 1)?'A':'B';
yjdLevel = (parseFloat(_data.modelYJD) >= 1)?'A':'B';
phdLevel = (parseFloat(_data.modelPHD) >= 1)?'A':'B';
czdLevel = (parseFloat(_data.modelCZD) > 0.9)?'A':'B';
cxdLevel = (parseFloat(_data.modelCXD) > 0.9)?'A':'B';
const data = [
{
item: `规范度:${gfdLevel}`,
value: parseFloat(_data.modelGFD).toFixed(2) ,
b: 30
},
{
item: `预警度:${yjdLevel}`,
value: parseFloat(_data.modelYJD).toFixed(2),
b: 70
},
{
item: `配合度:${phdLevel}`,
value: parseFloat(_data.modelPHD).toFixed(2),
b: 60
},
{
item: `成长度:${czdLevel}`,
value: parseFloat(_data.modelCZD).toFixed(2),
b: 50
},
{
item: `诚信度:${cxdLevel}`,
value: parseFloat(_data.modelCXD).toFixed(2),
b: 70
},
];
return (<ResponsiveContainer
height={this.props.mobile ? 300: '100%'}
>
<RadarChart data={data}
outerRadius={this.props.mobile ? "60%": "70%" }
>
<Tooltip isAnimationActive={false} />
<PolarGrid />
<PolarAngleAxis dataKey="item" onClick={e=>{
this.props.onSelect && this.props.onSelect(e.index)
}}/>
<PolarRadiusAxis
angle={90}
domain={[0, 1]} />
<Radar name="分数" dataKey="value" stroke="#38deee" fill="#51ceee" fillOpacity={0.6} />
{/* <Radar name="Lily" dataKey="B" stroke="#82ca9d" fill="#82ca9d" fillOpacity={0.6} /> */}
{/* <Legend /> */}
</RadarChart>
</ResponsiveContainer>
);
}
}
export default Basic;
\ No newline at end of file
import React, { Component } from "react";
import md5 from "md5";
import CryptoJS from "crypto-js";
import { PostJSON } from "../util/axios";
import Basic from './Radar.recharts';
import './Dashboard.css';
import { Layout, Row, Col, Card, Tooltip, Spin, Divider } from 'antd';
const { Content } = Layout;
class mDashboard extends Component {
constructor(props) {
super(props);
this.state = {
"appid":"SJZT202001150001",
"appSecret":"09E9E259FF6BF30C5F93EC56DA634E59",
loading: false,
data: null,
cxdDimension: [
{key: "custActiveTimes",name: "累计参加活动的次数"},
{key: "custActivityExpecNums",name: "累计应到活动的次数"},
{key: "custTeamRank",name: "小组排名"},
{key: "custTeamScore",name: "组员评分"},
{key: "custTIsSDMAGHome",name: "是否小组之家"},
{key: "custTOSDMAG",name: "所属自律互助小组"},
{key: "custTSDMAG",name: "自律互助小组用户类型"},
],
gfdDimension:[
{key: "custAbnError",name: "库存异常"},
{key: "custBusinAddrInConformity",name: "与实际地址不符"},
{key: "custBusinLicInConformity",name: "与工商照不符"},
{key: "custBusinSuspend",name: "停业整顿"},
{key: "custCaseBehaviour",name: "有涉案行为"},
{key: "custCaseIllegalSource",name: "向无证户提供货源"},
{key: "custCaseManyTimes",name: "因涉烟违法行为被处罚两次以上"},
{key: "custCaseSaleFakeCigs",name: "销售假烟"},
{key: "custCaseSaleSmuggleCigs",name: "销售走私烟"},
{key: "custCentralizedSign",name: "集中签收预警"},
{key: "custDontOpen",name: "开店不开门"},
{key: "custIsSaleHigh",name: "销售天价烟"},
{key: "custNonAuthPerson",name: "非授权人取货预警"},
{key: "custNonAutoOrder",name: "非自主订货"},
{key: "custNonAutoPay",name: "非自主支付"},
{key: "custNotMatchLic",name: "人证不符"},
{key: "custRecoveryAd",name: "卷烟回收广告"},
{key: "custSaleOtherAreaCig",name: "销售未当卷烟"},
{key: "custSalePunish",name: "营销处罚类型"},
{key: "custSegBusinError",name: "档位与经营能力不符"},
{key: "custSusPectTransLic",name: "涉嫌出租转让许可证"},
{key: "custSuspectViolaPers",name: "疑似收购、运送、推销人员"},
{key: "custUnlicensed",name: "未亮证经营"},
],
czdDimension:[
{key: "custMonthOrderNum",name: "订货量(本月累计)"},
{key: "custMonthOrderSpecNum",name: "订购规格数(本月累计)"},
{key: "custMonthOrderStruct",name: "订单结构(本月累计)"},
{key: "custMonthOrderSum",name: "订货额(本月累计)"},
{key: "custMonthOutProProp",name: "省外烟比例"},
{key: "custMonthTotalRate",name: "月供总量使用率"},
{key: "custLastMonthOrderNum",name: "订货量(上月累计)"},
{key: "custLastMonthOrderSpecNum",name: "订购规格数(上月累计)"},
{key: "custLastMonthOrderStruct",name: "订单结构(上月月累计)"},
{key: "custLastMonthOrderSum",name: "订货额(上月累计)"},
{key: "custLastYearSameMonthOrderNum",name: "订货量(去年同期累计)"},
{key: "custLastYearSameMonthOrderStruct",name: "订单结构(去年同期累计)"},
{key: "custLastYearSameMonthOrderSum",name: "订货额(去年同期累计)"},
{key: "lastMonthOrderAmtWgt",name: "上月订货额月供系数"},
{key: "lastMonthOrderNumWgt",name: "上月订货量月供系数"},
{key: "lastYearSameMonthOrderAmtWgt",name: "去年同期订货额月供系数"},
{key: "lastYearSameMonthOrderNumWgt",name: "去年同期订货量月供系数"},
{key: "thisMonthOrderAmtWgt",name: "本月订货额月供系数"},
{key: "thisMonthOrderNumWgt",name: "本月订货量月供系数"},
],
phdDimension:[
{key: "custBrandCultInvest",name: "品牌培育投入"},
{key: "custDisplayStatusBad",name: "陈列状态差"},
{key: "custInfoCollectQuality",name: "信息采集质量"},
{key: "custIsReturn",name: "是否有退货行为"},
{key: "custLdmTempStorageRes",name: "暂存原因"},
{key: "custNonCooperation",name: "不配合检查"},
{key: "custNonEleSettleTimes",name: "非电子结算次数"},
{key: "custNoSignConfis",name: "未粘贴罚没烟标识"},
{key: "custNoTicketCounter",name: "未设罚没烟专柜"},
{key: "custPriceNotClear",name: "未明码标价"},
{key: "custSamplingRate",name: "出样率"},
{key: "custStandardizeSale",name: "罚没烟销售客户"},
{key: "custTerminalScanQuality",name: "终端扫码质量"},
{key: "custWaitingTimes",name: "接货等待次数"},
{key: "custWithoutSwipingCardTimes",name: "交接未刷卡次数"},
],
yjdDimension: [
{key: "custActivePayTimeError",name: "主动支付时间异常"},
{key: "custBaseInfoChange",name: "基础信息变更"},
{key: "custBrandSegBad",name: "紧俏品牌档位差"},
{key: "custBrandSourceNatureDivide",name: "品牌货源属性划分"},
{key: "custCaseManyTimes",name: "因涉烟违法行为被处罚两次以上"},
{key: "custCaseSupervision",name: "涉案户监管"},
{key: "custCaseSupervisionNotImplemented",name: "涉案户监管未落实"},
{key: "custCashPayRatio",name: "现金结算比例"},
{key: "custCrowdedBrandPutArea",name: "紧俏品牌投放面"},
{key: "custDayFocusPut",name: "集中时间(日)投放"},
{key: "custDayPay",name: "日清日结"},
{key: "custError",name: "异常客户"},
{key: "custErrorOrder",name: "异常订单"},
{key: "custExpensiveSingleSpecOrderError",name: "高价位卷烟单规格订购异常"},
{key: "custExpensiveSmokePutError",name: "高价位卷烟投放异常"},
{key: "custFocusPut",name: "集中客户投放"},
{key: "custInnerIPOrder",name: "内部IP订货"},
{key: "custLicInfoWarning",name: "客户办证信息"},
{key: "custManualImproveSeg",name: "手工提档"},
{key: "custMonthOrderBalanced",name: "客户月订货均衡性"},
{key: "custMonthSaleUnbalanced",name: "销售(月)不均衡"},
{key: "custOrderAdjust",name: "订单调整"},
{key: "custOrderFullRatio",name: "订单满足率"},
{key: "custOrderNum",name: "订货次数"},
{key: "custOrderWayChangeRatio",name: "订货方式变更率"},
{key: "custPayTypeChangeRatio",name: "结算方式变更率"},
{key: "custPayTypeError",name: "结算方式异常"},
{key: "custPutAreaLow",name: "投放面过低"},
{key: "custPutSpecRatio",name: "投放规格数比例"},
{key: "custReplaceCall",name: "疑似代调"},
{key: "custReplaceOrder",name: "疑似代订"},
{key: "custSaleNoteError",name: "销售记录异常"},
{key: "custSalePunish",name: "营销处罚类型"},
{key: "custSalesError",name: "销量异常"},
{key: "custSameBankCardPay",name: "一卡多结"},
{key: "custSameOrder",name: "五同订单"},
{key: "custSameOrderTel",name: "订货电话雷同"},
{key: "custSegChangeError",name: "档位变动异常"},
{key: "custSegRuleError",name: "客户分档规则异常"},
{key: "custSelectPointPut",name: "选点投放"},
{key: "custSelfCrowdedSourceProvide",name: "直营店紧俏货源供应"},
{key: "custSelfNature",name: "直营店属性"},
{key: "custSignPositionOffset",name: "客户签收位置偏移"},
{key: "custSimilarOrder",name: "雷同订单"},
{key: "custSingleSaleHigh",name: "是否存在单次销量过高"},
{key: "custSLHHSaleProportion",name: "大户销量占比"},
{key: "custSmokePriceError",name: "卷烟价格异常"},
{key: "custSmokeSaleError",name: "是否存在卷烟销售异常"},
{key: "custSpecialTypeOrderProportion",name: "特殊客户订购量占比"},
{key: "custStopAndReduceProvide",name: "停供减供"},
{key: "custStopProvideSituation",name: "货源停供情况"},
{key: "custWeekBrandFocusPut",name: "品牌集中(周)投放"},
{key: "custSegWarning",name: "客户分档"},
],
curDegree: '规范度',
curDimension: null
};
}
componentDidMount() {
const { appid, appSecret } = this.state;
const _decryptId = this.props.match.params.id;
var key = CryptoJS.enc.Utf8.parse("A930F2C3ACEA7B5B");
var iv = CryptoJS.enc.Utf8.parse('B54480C3A296C334');
var bytes = CryptoJS.AES.decrypt(CryptoJS.enc.Base64.stringify(CryptoJS.enc.Hex.parse(_decryptId)), key,{ iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
var _custId = bytes.toString(CryptoJS.enc.Utf8);
let time = this.getNowTime();
this.setState({ loading: true}, () => {
PostJSON("/api/tag/cust/getById",{ payload: {
"appid":appid,
"custId":_custId,
"time":time,
"sign":md5(`appid=${appid}&sign_type=MD5&custId=${_custId}&time=${time}&appSecret=${appSecret}`).toUpperCase(),
}}).then(function(data) {
if (data.return_code === '00000') {
const { gfdDimension } = this.state;
this.setState({ data: data.data, curDimension: this.curDimensionSort(data.data,gfdDimension), loading: false });
}
}.bind(this));
});
}
getNowTime = () => {
var date = new Date();
this.year = date.getFullYear();
this.month = date.getMonth() + 1;
this.date = date.getDate();
this.hour = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
this.minute = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
this.second = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
this.milliSeconds = date.getMilliseconds();
return `${this.year}${this.month}${this.hour}${this.minute}${this.second}${this.milliSeconds}`;
}
custCredit = data => {
var level = 0;
if (data === null) return '';
if (parseFloat(data.modelGFD) >= 1) level++;
if (parseFloat(data.modelYJD) >= 1) level++;
if (parseFloat(data.modelPHD) >= 1) level++;
if (parseFloat(data.modelCZD) > 0.9) level++;
if (parseFloat(data.modelCXD) > 0.9) level++;
return (level>2)?`${level}A`:'B';
}
custStatusMapper = status => {
if (status === null) return '';
if (status === '01') return '新增';
if (status === '02') return '有效';
if (status === '03') return '暂停';
if (status === '04') return '无效';
return '';
}
custAreaMapper = area => {
if (area === null) return '';
if (area === '01') return '工业区';
if (area === '02') return '居民区(村)';
if (area === '03') return '旅客中转区';
if (area === '04') return '学区';
if (area === '05') return '娱乐(旅游)区';
if (area === '06') return '政务(商务)区';
if (area === '11') return '商业(集贸)区';
if (area === '90') return '其他';
return '';
}
curDimensionSort = (data, dimension) => {
var checkedDimension = [];
var uncheckedDimension = [];
dimension.map(item=>{
(data[item.key]!=null)?checkedDimension.push(item):uncheckedDimension.push(item);
return item;
})
return checkedDimension.concat(uncheckedDimension);
}
onRadarSelected = (index) => {
const { gfdDimension, yjdDimension, phdDimension, czdDimension, cxdDimension, data} = this.state;
if (index === 0) {
this.setState({ curDegree: '规范度', curDimension: this.curDimensionSort(data,gfdDimension)});
} else if (index === 1) {
this.setState({ curDegree: '预警度', curDimension: this.curDimensionSort(data,yjdDimension)});
} else if (index === 2) {
this.setState({ curDegree: '配合度', curDimension: this.curDimensionSort(data,phdDimension)});
} else if (index === 3) {
this.setState({ curDegree: '成长度', curDimension: this.curDimensionSort(data,czdDimension)});
} else if (index === 4) {
this.setState({ curDegree: '诚信度', curDimension: this.curDimensionSort(data,cxdDimension)});
}
}
render() {
const { data, curDegree ,curDimension, loading } = this.state;
return (
<React.Fragment>
<Layout>
<Content style={{padding:'10px'}}>
<Row style={{marginBottom: '10px'}}>
<Card bordered={false}>
<h3 className="ant-typography text-primary">基本信息</h3>
<Spin spinning={loading}>
{data&&(
<>
<Row className="border-bottom border-light infoContentItem">
<Col span={12}>
<div>客户名称</div>
<div className="text-primary">{data.custName||''}</div>
</Col>
<Col span={12}>
<div>许可证号</div>
<div className="text-primary">{data.custLicNo||''}</div>
</Col>
</Row>
<Row className="border-bottom border-light infoContentItem">
<Col span={12}>
<div>营销部</div>
<div className="text-primary">{data.custSaleDeptName||''}</div>
</Col>
<Col span={12}>
<div>客户经理</div>
<div className="text-primary">{data.custSlsmanName||''}</div>
</Col>
</Row>
<Row className="border-bottom border-light infoContentItem">
<Col span={12}>
<div>档位</div>
<div className="text-primary">{data.custSeg||''}</div>
</Col>
<Col span={12}>
<div>批次</div>
<div className="text-primary">{data.custPeriodsName||''}</div>
</Col>
</Row>
<Row className="border-bottom border-light infoContentItem">
<Col span={12}>
<div>负责人姓名</div>
<div className="text-primary">{data.custManagerName||''}</div>
</Col>
<Col span={12}>
<div>许可证有效期止</div>
<div className="text-primary">{data.custLicValidateEnd||''}</div>
</Col>
</Row>
<Row className="border-bottom border-light infoContentItem">
<Col span={12}>
<div>市管员</div>
<div className="text-primary">{data.custGridPersonName||''}</div>
</Col>
<Col md={12}>
<div>市场类型</div>
<div className="text-primary">{data.custWorkPort||''}</div>
</Col>
</Row>
</>
)
}
</Spin>
</Card>
</Row>
<Row style={{marginBottom: '10px'}}>
<Card bordered={false}>
<h3 className="ant-typography text-primary">客户评级</h3>
<Spin spinning={loading}>
<div className="levelContent">
{
data&&<span className="text-primary">{this.custCredit(data)}</span>
}
</div>
</Spin>
<Divider />
<Spin spinning={loading}>
<div className="radarContent" style={{height: '300px'}}>
{
data&&<Basic data={data} onSelect={this.onRadarSelected} mobile={true}></Basic>
}
</div>
</Spin>
</Card>
</Row>
<Row style={{marginBottom: '10px'}}>
<Card bordered={false}>
<h3 className="ant-typography text-primary">{curDegree}</h3>
<Spin spinning={loading}>
<div className="degreeContent" style={{height: 'auto'}}>
<div className="degreeWrap" style={{maxHeight: '100%'}}>
{
data&&curDimension.map((item,i) => {
if (data[item.key]!=null) {
console.log(data[item.key]);
return (
<div key={i} className="degreeWrapItem checked text-primary">
<Tooltip
placement="top"
overlayClassName="home"
title={data[item.key]}
>
<svg viewBox="64 64 896 896" focusable="false" data-icon="check-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true">
<path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 0 1-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"></path>
</svg>
<span className="title">{item.name}</span>
</Tooltip>
</div>
)
}
return (
<div key={i} className="degreeWrapItem">
<svg viewBox="64 64 896 896" focusable="false" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 0 1-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path></svg>
<span className="title">{item.name}</span>
</div>
)
})
}
</div>
</div>
</Spin>
</Card>
</Row>
</Content>
</Layout>
</React.Fragment>
);
}
}
export default mDashboard;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment