泛微薪资核算iframe表格
|
|
@ -23,11 +23,14 @@
|
|||
"@ant-design/charts": "^1.4.2",
|
||||
"@ant-design/pro-layout": "6.32.1",
|
||||
"@formily/antd": "^2.0.6",
|
||||
"@jiaminghi/data-view-react": "^1.2.5",
|
||||
"@types/lodash": "^4.14.172",
|
||||
"@ztree/ztree_v3": "^3.5.42",
|
||||
"ahooks": "^3.1.3",
|
||||
"antd": "^4.17.3",
|
||||
"axios": "^0.22.0",
|
||||
"echarts": "^4.9.0",
|
||||
"echarts-gl": "^1.1.2",
|
||||
"fbemitter": "^3.0.0",
|
||||
"js-base64": "^3.6.1",
|
||||
"js-cookie": "^2.2.1",
|
||||
|
|
@ -58,7 +61,6 @@
|
|||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.2",
|
||||
"@umijs/plugin-access": "2.4.2",
|
||||
"umi-plugin-authorize": "^2.8.12",
|
||||
"@umijs/plugin-dva": "^0.13.0",
|
||||
"@umijs/plugin-initial-state": "^2.4.0",
|
||||
"@umijs/plugin-locale": "^0.15.0",
|
||||
|
|
@ -71,6 +73,7 @@
|
|||
"prettier": "^2.2.0",
|
||||
"typescript": "^4.3.5",
|
||||
"umi": "^3.5.20",
|
||||
"umi-plugin-authorize": "^2.8.12",
|
||||
"yorkie": "^2.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 832 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 6.0 MiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -15,6 +15,7 @@ import { layoutConfig } from "@/layouts/config";
|
|||
import stores from "@/store";
|
||||
import "moment/locale/zh-cn";
|
||||
import "antd/dist/antd.variable.min.css";
|
||||
import "../utils/flexible";
|
||||
|
||||
moment.locale("zh-cn");
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,58 @@ import { G2, measureTextWidth } from "@ant-design/plots";
|
|||
|
||||
const G = G2.getEngine("canvas");
|
||||
|
||||
export const structureCardList = [
|
||||
{
|
||||
title: "当期总人数",
|
||||
active: 0,
|
||||
number: 489,
|
||||
unit: "人",
|
||||
color: "#FFBB31",
|
||||
tabList: ["本月", "上月"]
|
||||
},
|
||||
{
|
||||
title: "本月入职",
|
||||
active: 0,
|
||||
number: 100,
|
||||
unit: "人",
|
||||
color: "#3DA5F6",
|
||||
tabList: ["本月", "上月"]
|
||||
},
|
||||
{
|
||||
title: "本月离职",
|
||||
active: 0,
|
||||
number: 23,
|
||||
unit: "人",
|
||||
color: "#5FD5C7",
|
||||
tabList: ["本月", "上月"]
|
||||
},
|
||||
{
|
||||
title: "年度累计入职",
|
||||
active: 0,
|
||||
number: 238,
|
||||
unit: "人",
|
||||
color: "#2B6DF6",
|
||||
tabList: ["本年", "去年"]
|
||||
},
|
||||
{
|
||||
title: "年度累计离职",
|
||||
active: 0,
|
||||
number: 198,
|
||||
unit: "人",
|
||||
color: "#B571EA",
|
||||
tabList: ["本年", "去年"]
|
||||
},
|
||||
{
|
||||
title: "平均年龄",
|
||||
active: 0,
|
||||
number: 28.0,
|
||||
unit: "岁",
|
||||
color: "#5FCC7B",
|
||||
tabList: ["本年", "去年"]
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
//人员流动数据
|
||||
export const flowingData = [
|
||||
{
|
||||
|
|
@ -44,14 +96,10 @@ export const flowingConfig = {
|
|||
offset: "-2%",
|
||||
content: "{name}",
|
||||
style: {
|
||||
fontSize: 10
|
||||
fontSize: 15
|
||||
}
|
||||
},
|
||||
interactions: [
|
||||
{
|
||||
type: "element-active"
|
||||
}
|
||||
],
|
||||
interactions: [],
|
||||
pieStyle: {
|
||||
lineWidth: 0
|
||||
}
|
||||
|
|
@ -179,11 +227,7 @@ export const seniorityConfig = {
|
|||
textAlign: "center"
|
||||
}
|
||||
},
|
||||
interactions: [
|
||||
{
|
||||
type: "element-active"
|
||||
}
|
||||
]
|
||||
interactions: []
|
||||
};
|
||||
|
||||
//人员流动情况趋势图数据信息
|
||||
|
|
@ -279,8 +323,8 @@ export const institutionConfig = {
|
|||
data: institutionData,
|
||||
xField: "stage",
|
||||
yField: "number",
|
||||
dynamicHeight: true,
|
||||
legend: false
|
||||
dynamicHeight: true
|
||||
// legend: false
|
||||
};
|
||||
|
||||
//人员异动分析数数据
|
||||
|
|
@ -309,104 +353,46 @@ export const personChangeConfig = {
|
|||
labelHeight: 28,
|
||||
content: "{name}\n{percentage}"
|
||||
},
|
||||
interactions: [
|
||||
{
|
||||
type: "element-active"
|
||||
}
|
||||
]
|
||||
interactions: []
|
||||
};
|
||||
|
||||
//详细图表柱状图
|
||||
export const multiplData = [
|
||||
{
|
||||
"name": "全员离职率",
|
||||
"年份": "2022",
|
||||
"年份流动信息": 18.9
|
||||
},
|
||||
{
|
||||
"name": "全员离职率",
|
||||
"年份": "2023",
|
||||
"年份流动信息": 28.8
|
||||
},
|
||||
{
|
||||
"name": "主动离职率",
|
||||
"年份": "2022",
|
||||
"年份流动信息": 12.4
|
||||
},
|
||||
{
|
||||
"name": "主动离职率",
|
||||
"年份": "2023",
|
||||
"年份流动信息": 23.2
|
||||
},
|
||||
{
|
||||
"name": "被动离职率",
|
||||
"年份": "2022",
|
||||
"年份流动信息": 12.4
|
||||
},
|
||||
{
|
||||
"name": "被动离职率",
|
||||
"年份": "2023",
|
||||
"年份流动信息": 23.2
|
||||
},
|
||||
{
|
||||
"name": "关键岗位离职率",
|
||||
"年份": "2022",
|
||||
"年份流动信息": 12.4
|
||||
},
|
||||
{
|
||||
"name": "关键岗位离职率",
|
||||
"年份": "2023",
|
||||
"年份流动信息": 23.2
|
||||
},
|
||||
{
|
||||
"name": "绩优员工离职率",
|
||||
"年份": "2022",
|
||||
"年份流动信息": 12.4
|
||||
},
|
||||
{
|
||||
"name": "绩优员工离职率",
|
||||
"年份": "2023",
|
||||
"年份流动信息": 23.2
|
||||
},
|
||||
{
|
||||
"name": "转正率",
|
||||
"年份": "2022",
|
||||
"年份流动信息": 12.4
|
||||
},
|
||||
{
|
||||
"name": "转正率",
|
||||
"年份": "2023",
|
||||
"年份流动信息": 23.2
|
||||
},
|
||||
{ "city": "12", "type": "今年", "value": 18000 },
|
||||
{ "city": "12", "type": "去年", "value": 11000 },
|
||||
{ "city": "11", "type": "今年", "value": 18000 },
|
||||
{ "city": "11", "type": "去年", "value": 11000 },
|
||||
{ "city": "10", "type": "今年", "value": 18000 },
|
||||
{ "city": "10", "type": "去年", "value": 11000 },
|
||||
{ "city": "09", "type": "今年", "value": 18000 },
|
||||
{ "city": "09", "type": "去年", "value": 11000 },
|
||||
{ "city": "08", "type": "今年", "value": 18000 },
|
||||
{ "city": "08", "type": "去年", "value": 11000 },
|
||||
{ "city": "07", "type": "今年", "value": 17000 },
|
||||
{ "city": "07", "type": "去年", "value": 6000 },
|
||||
{ "city": "06", "type": "今年", "value": 9000 },
|
||||
{ "city": "06", "type": "去年", "value": 8500 },
|
||||
{ "city": "05", "type": "今年", "value": 14000 },
|
||||
{ "city": "05", "type": "去年", "value": 9000 },
|
||||
{ "city": "04", "type": "今年", "value": 14000 },
|
||||
{ "city": "04", "type": "去年", "value": 9000 },
|
||||
{ "city": "03", "type": "今年", "value": 16000 },
|
||||
{ "city": "03", "type": "去年", "value": 5000 },
|
||||
{ "city": "02", "type": "今年", "value": 9000 },
|
||||
{ "city": "02", "type": "去年", "value": 8500 },
|
||||
{ "city": "01", "type": "今年", "value": 14500 },
|
||||
{ "city": "01", "type": "去年", "value": 8500 }
|
||||
];
|
||||
|
||||
//人员流动情况趋势图配置信息
|
||||
export const multipleConfig = {
|
||||
data: [],
|
||||
xField: "city",
|
||||
yField: "value",
|
||||
seriesField: "type",
|
||||
isGroup: true,
|
||||
xField: "年份",
|
||||
yField: "年份流动信息",
|
||||
seriesField: "name",
|
||||
// 分组柱状图 组内柱子间的间距 (像素级别)
|
||||
dodgePadding: 2,
|
||||
// 分组柱状图 组间的间距 (像素级别)
|
||||
intervalPadding: 50,
|
||||
label: {
|
||||
// 可手动配置 label 数据标签位置
|
||||
position: "middle",
|
||||
// 'top', 'middle', 'bottom'
|
||||
// 可配置附加的布局方法
|
||||
layout: [
|
||||
// 柱形图数据标签位置自动调整
|
||||
{
|
||||
type: "interval-adjust-position"
|
||||
}, // 数据标签防遮挡
|
||||
{
|
||||
type: "interval-hide-overlap"
|
||||
}, // 数据标签文颜色自动调整
|
||||
{
|
||||
type: "adjust-color"
|
||||
}
|
||||
]
|
||||
columnStyle: {
|
||||
radius: [20, 20, 0, 0]
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -425,102 +411,67 @@ export const trainingData = [
|
|||
//培训排名
|
||||
export const rankingData = [
|
||||
{
|
||||
name: '机构一',
|
||||
month: 'Jan.',
|
||||
grades: 18.9,
|
||||
label: "财务部",
|
||||
type: "前五",
|
||||
value: 2800
|
||||
},
|
||||
{
|
||||
name: '机构一',
|
||||
month: 'Feb.',
|
||||
grades: 28.8,
|
||||
label: "销售部",
|
||||
type: "前五",
|
||||
value: 1800
|
||||
},
|
||||
{
|
||||
name: '机构一',
|
||||
month: 'Mar.',
|
||||
grades: 39.3,
|
||||
label: "策划部",
|
||||
type: "前五",
|
||||
value: 950
|
||||
},
|
||||
{
|
||||
name: '机构一',
|
||||
month: 'Apr.',
|
||||
grades: 81.4,
|
||||
label: "运营部",
|
||||
type: "前五",
|
||||
value: 500
|
||||
},
|
||||
{
|
||||
name: '机构一',
|
||||
month: 'May',
|
||||
grades: 47,
|
||||
label: "后勤部",
|
||||
type: "前五",
|
||||
value: 170
|
||||
},
|
||||
{
|
||||
name: '机构一',
|
||||
month: 'Jun.',
|
||||
grades: 20.3,
|
||||
label: "财务部1",
|
||||
type: "后五",
|
||||
value: 2260
|
||||
},
|
||||
{
|
||||
name: '机构一',
|
||||
month: 'Jul.',
|
||||
grades: 24,
|
||||
label: "销售部1",
|
||||
type: "后五",
|
||||
value: 1300
|
||||
},
|
||||
{
|
||||
name: '机构一',
|
||||
month: 'Aug.',
|
||||
grades: 35.6,
|
||||
label: "策划部1",
|
||||
type: "后五",
|
||||
value: 900
|
||||
},
|
||||
{
|
||||
name: '机构二',
|
||||
month: 'Jan.',
|
||||
grades: 12.4,
|
||||
label: "运营部1",
|
||||
type: "后五",
|
||||
value: 390
|
||||
},
|
||||
{
|
||||
name: '机构二',
|
||||
month: 'Feb.',
|
||||
grades: 23.2,
|
||||
},
|
||||
{
|
||||
name: '机构二',
|
||||
month: 'Mar.',
|
||||
grades: 34.5,
|
||||
},
|
||||
{
|
||||
name: '机构二',
|
||||
month: 'Apr.',
|
||||
grades: 99.7,
|
||||
},
|
||||
{
|
||||
name: '机构二',
|
||||
month: 'May',
|
||||
grades: 52.6,
|
||||
},
|
||||
{
|
||||
name: '机构二',
|
||||
month: 'Jun.',
|
||||
grades: 35.5,
|
||||
},
|
||||
{
|
||||
name: '机构二',
|
||||
month: 'Jul.',
|
||||
grades: 37.4,
|
||||
},
|
||||
{
|
||||
name: '机构二',
|
||||
month: 'Aug.',
|
||||
grades: 42.4,
|
||||
},
|
||||
]
|
||||
label: "后勤部1",
|
||||
type: "后五",
|
||||
value: 100
|
||||
}
|
||||
];
|
||||
export const rankingConfig = {
|
||||
data: [],
|
||||
isGroup: true,
|
||||
xField: "month",
|
||||
yField: "grades",
|
||||
seriesField: "name",
|
||||
// 分组柱状图 组内柱子间的间距 (像素级别)
|
||||
dodgePadding: 2,
|
||||
// 分组柱状图 组间的间距 (像素级别)
|
||||
intervalPadding: 10,
|
||||
columnStyle: {
|
||||
radius: [20, 20, 0],
|
||||
},
|
||||
xField: "value",
|
||||
yField: "label",
|
||||
seriesField: "type",
|
||||
dodgePadding: 4,
|
||||
label: {
|
||||
// 可手动配置 label 数据标签位置
|
||||
position: "middle",
|
||||
// 'top', 'middle', 'bottom'
|
||||
// 'left', 'middle', 'right'
|
||||
// 可配置附加的布局方法
|
||||
layout: [
|
||||
// 柱形图数据标签位置自动调整
|
||||
|
|
@ -602,257 +553,249 @@ export const humanAnalysisconfig = {
|
|||
//人效排名数据
|
||||
export const humanEfficiencyRankingData = [
|
||||
{
|
||||
title: '员工1',
|
||||
title: "员工1",
|
||||
description: "公司1",
|
||||
rank: 1
|
||||
},
|
||||
{
|
||||
title: '员工2',
|
||||
title: "员工2",
|
||||
description: "公司2",
|
||||
rank: 2
|
||||
},
|
||||
{
|
||||
title: '员工3',
|
||||
title: "员工3",
|
||||
description: "公司3",
|
||||
rank: 3
|
||||
},
|
||||
{
|
||||
title: '员工4',
|
||||
title: "员工4",
|
||||
description: "公司4",
|
||||
rank: 4
|
||||
},
|
||||
}
|
||||
];
|
||||
//销售新客户合同数量排名
|
||||
export const saleEfficiencyRankingData = [
|
||||
{
|
||||
title: '员工5',
|
||||
title: "员工5",
|
||||
description: "公司1",
|
||||
rank: 1
|
||||
},
|
||||
{
|
||||
title: '员工6',
|
||||
title: "员工6",
|
||||
description: "公司2",
|
||||
rank: 2
|
||||
},
|
||||
{
|
||||
title: '员工7',
|
||||
title: "员工7",
|
||||
description: "公司3",
|
||||
rank: 3
|
||||
},
|
||||
{
|
||||
title: '员工8',
|
||||
title: "员工8",
|
||||
description: "公司4",
|
||||
rank: 4
|
||||
},
|
||||
}
|
||||
];
|
||||
//新销售有效合同
|
||||
export const newSaleEfficiencyRankingData = [
|
||||
{
|
||||
title: '员工9',
|
||||
title: "员工9",
|
||||
description: "公司1",
|
||||
rank: 1
|
||||
},
|
||||
{
|
||||
title: '员工10',
|
||||
title: "员工10",
|
||||
description: "公司2",
|
||||
rank: 2
|
||||
},
|
||||
{
|
||||
title: '员工11',
|
||||
title: "员工11",
|
||||
description: "公司3",
|
||||
rank: 3
|
||||
},
|
||||
{
|
||||
title: '员工12',
|
||||
title: "员工12",
|
||||
description: "公司4",
|
||||
rank: 4
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
|
||||
//人工总成本数据
|
||||
export const totalLaborCostdata = [
|
||||
{
|
||||
time: 'Jan.',
|
||||
time: "Jan.",
|
||||
value: 350,
|
||||
count: 800,
|
||||
count: 800
|
||||
},
|
||||
{
|
||||
time: 'Feb.',
|
||||
time: "Feb.",
|
||||
value: 900,
|
||||
count: 600,
|
||||
count: 600
|
||||
},
|
||||
{
|
||||
time: 'Mar.',
|
||||
time: "Mar.",
|
||||
value: 300,
|
||||
count: 400,
|
||||
count: 400
|
||||
},
|
||||
{
|
||||
time: 'Apr.',
|
||||
time: "Apr.",
|
||||
value: 450,
|
||||
count: 380,
|
||||
count: 380
|
||||
},
|
||||
{
|
||||
time: 'May.',
|
||||
time: "May.",
|
||||
value: 470,
|
||||
count: 220,
|
||||
count: 220
|
||||
},
|
||||
{
|
||||
time: 'Jun.',
|
||||
time: "Jun.",
|
||||
value: 470,
|
||||
count: 220,
|
||||
count: 220
|
||||
},
|
||||
{
|
||||
time: 'Jul.',
|
||||
time: "Jul.",
|
||||
value: 470,
|
||||
count: 220,
|
||||
count: 220
|
||||
},
|
||||
{
|
||||
time: 'Aug.',
|
||||
time: "Aug.",
|
||||
value: 470,
|
||||
count: 220,
|
||||
},
|
||||
count: 220
|
||||
}
|
||||
];
|
||||
export const totalLaborCostConfig = {
|
||||
data: [totalLaborCostdata, totalLaborCostdata],
|
||||
xField: 'time',
|
||||
yField: ['value', 'count'],
|
||||
xField: "time",
|
||||
yField: ["value", "count"],
|
||||
yAxis: {
|
||||
// 格式化左坐标轴
|
||||
value: {
|
||||
min: 0,
|
||||
label: {
|
||||
formatter: (val) => `${val}W`,
|
||||
},
|
||||
formatter: (val) => `${val}W`
|
||||
}
|
||||
},
|
||||
// 隐藏右坐标轴
|
||||
count: false,
|
||||
count: false
|
||||
},
|
||||
geometryOptions: [
|
||||
{
|
||||
geometry: 'column',
|
||||
color: '#5B8FF9',
|
||||
geometry: "column",
|
||||
color: "#5B8FF9",
|
||||
columnWidthRatio: 0.4,
|
||||
label: {
|
||||
position: 'middle',
|
||||
},
|
||||
position: "middle"
|
||||
}
|
||||
},
|
||||
{
|
||||
geometry: 'line',
|
||||
geometry: "line",
|
||||
smooth: true,
|
||||
color: '#5AD8A6',
|
||||
},
|
||||
],
|
||||
interactions: [
|
||||
{
|
||||
type: 'element-highlight',
|
||||
},
|
||||
{
|
||||
type: 'active-region',
|
||||
},
|
||||
color: "#5AD8A6"
|
||||
}
|
||||
],
|
||||
interactions: [],
|
||||
annotations: {
|
||||
value: [
|
||||
{
|
||||
type: 'text',
|
||||
position: ['2019-06', 'max'],
|
||||
content: '柱线混合图',
|
||||
},
|
||||
type: "text",
|
||||
position: ["2019-06", "max"],
|
||||
content: "柱线混合图"
|
||||
}
|
||||
],
|
||||
count: [
|
||||
{
|
||||
type: 'dataMarker',
|
||||
type: "dataMarker",
|
||||
top: true,
|
||||
position: ['2019-05', 400],
|
||||
position: ["2019-05", 400],
|
||||
line: {
|
||||
length: 20,
|
||||
length: 20
|
||||
},
|
||||
text: {
|
||||
content: '2019-05, 发布新版本',
|
||||
content: "",
|
||||
style: {
|
||||
textAlign: 'left',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
textAlign: "left"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
//人均收入配置数据 1、人均固定收入 2、人均浮动收入
|
||||
export const fixedIncomePerData = [
|
||||
{
|
||||
type: '销售',
|
||||
value: 27,
|
||||
type: "销售",
|
||||
value: 27
|
||||
},
|
||||
{
|
||||
type: '项目',
|
||||
value: 25,
|
||||
type: "项目",
|
||||
value: 25
|
||||
},
|
||||
{
|
||||
type: '开发',
|
||||
value: 18,
|
||||
type: "开发",
|
||||
value: 18
|
||||
},
|
||||
{
|
||||
type: '客服',
|
||||
value: 15,
|
||||
type: "客服",
|
||||
value: 15
|
||||
},
|
||||
{
|
||||
type: '其他',
|
||||
value: 10,
|
||||
type: "其他",
|
||||
value: 10
|
||||
},
|
||||
{
|
||||
type: '其他',
|
||||
value: 5,
|
||||
},
|
||||
type: "其他",
|
||||
value: 5
|
||||
}
|
||||
];
|
||||
export const floatingIncomePerData = [
|
||||
{
|
||||
type: '销售',
|
||||
value: 18,
|
||||
type: "销售",
|
||||
value: 18
|
||||
},
|
||||
{
|
||||
type: '项目',
|
||||
value: 29,
|
||||
type: "项目",
|
||||
value: 29
|
||||
},
|
||||
{
|
||||
type: '开发',
|
||||
value: 23,
|
||||
type: "开发",
|
||||
value: 23
|
||||
},
|
||||
{
|
||||
type: '客服',
|
||||
value: 3,
|
||||
type: "客服",
|
||||
value: 3
|
||||
},
|
||||
{
|
||||
type: '其他',
|
||||
value: 22,
|
||||
type: "其他",
|
||||
value: 22
|
||||
},
|
||||
{
|
||||
type: '其他',
|
||||
value: 5,
|
||||
},
|
||||
type: "其他",
|
||||
value: 5
|
||||
}
|
||||
];
|
||||
//人均收入配置信息
|
||||
export const perCapitaIncomeConfig = {
|
||||
appendPadding: 10,
|
||||
data: [],
|
||||
angleField: 'value',
|
||||
colorField: 'type',
|
||||
angleField: "value",
|
||||
colorField: "type",
|
||||
radius: 1,
|
||||
innerRadius: 0.64,
|
||||
meta: {
|
||||
value: {
|
||||
formatter: (v) => `${v} ¥`,
|
||||
},
|
||||
formatter: (v) => `${v} ¥`
|
||||
}
|
||||
},
|
||||
label: {
|
||||
type: 'inner',
|
||||
offset: '-50%',
|
||||
type: "inner",
|
||||
offset: "-50%",
|
||||
style: {
|
||||
textAlign: 'center',
|
||||
textAlign: "center"
|
||||
},
|
||||
autoRotate: false,
|
||||
content: '{value}',
|
||||
content: "{value}"
|
||||
},
|
||||
statistic: {
|
||||
title: {
|
||||
|
|
@ -861,119 +804,117 @@ export const perCapitaIncomeConfig = {
|
|||
const { width, height } = container.getBoundingClientRect();
|
||||
const d = Math.sqrt(Math.pow(width / 2, 2) + Math.pow(height / 2, 2));
|
||||
//总计
|
||||
const text = datum ? datum.type : '';
|
||||
const text = datum ? datum.type : "";
|
||||
return renderStatistic(d, text, {
|
||||
fontSize: 28,
|
||||
fontSize: 28
|
||||
});
|
||||
},
|
||||
}
|
||||
},
|
||||
content: {
|
||||
offsetY: 4,
|
||||
style: {
|
||||
fontSize: '32px',
|
||||
fontSize: "32px"
|
||||
},
|
||||
customHtml: (container, view, datum, data) => {
|
||||
const { width } = container.getBoundingClientRect();
|
||||
const text = datum ? `¥ ${datum.value}` : `¥ ${data.reduce((r, d) => r + d.value, 0)}`;
|
||||
return renderStatistic(width, "", {
|
||||
fontSize: 32,
|
||||
fontSize: 32
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
// 添加 中心统计文本 交互
|
||||
interactions: [
|
||||
],
|
||||
interactions: []
|
||||
};
|
||||
|
||||
|
||||
|
||||
//人均人工成本数据
|
||||
export const laborCostsPerCapitaData = [
|
||||
{
|
||||
year: 'Jan.',
|
||||
year: "Jan.",
|
||||
value: 3,
|
||||
count: 10,
|
||||
count: 10
|
||||
},
|
||||
{
|
||||
year: 'Feb.',
|
||||
year: "Feb.",
|
||||
value: 4,
|
||||
count: 4,
|
||||
count: 4
|
||||
},
|
||||
{
|
||||
year: 'Mar.',
|
||||
year: "Mar.",
|
||||
value: 3.5,
|
||||
count: 5,
|
||||
count: 5
|
||||
},
|
||||
{
|
||||
year: 'Apr.',
|
||||
year: "Apr.",
|
||||
value: 5,
|
||||
count: 5,
|
||||
count: 5
|
||||
},
|
||||
{
|
||||
year: 'May.',
|
||||
year: "May.",
|
||||
value: 4.9,
|
||||
count: 4.9,
|
||||
count: 4.9
|
||||
},
|
||||
{
|
||||
year: 'Jun.',
|
||||
year: "Jun.",
|
||||
value: 6,
|
||||
count: 35,
|
||||
count: 35
|
||||
},
|
||||
{
|
||||
year: 'Jul.',
|
||||
year: "Jul.",
|
||||
value: 7,
|
||||
count: 7,
|
||||
count: 7
|
||||
},
|
||||
{
|
||||
year: 'Aug.',
|
||||
year: "Aug.",
|
||||
value: 9,
|
||||
count: 1,
|
||||
},
|
||||
count: 1
|
||||
}
|
||||
];
|
||||
export const laborCostsPerCapitaConfig = {
|
||||
data: [laborCostsPerCapitaData, laborCostsPerCapitaData],
|
||||
xField: 'year',
|
||||
yField: ['value', 'count'],
|
||||
xField: "year",
|
||||
yField: ["value", "count"],
|
||||
geometryOptions: [
|
||||
{
|
||||
geometry: 'line',
|
||||
geometry: "line",
|
||||
smooth: true,
|
||||
color: '#5B8FF9',
|
||||
color: "#5B8FF9",
|
||||
label: {
|
||||
formatter: (datum) => {
|
||||
return `${datum.value}w`;
|
||||
},
|
||||
}
|
||||
},
|
||||
lineStyle: {
|
||||
lineWidth: 3,
|
||||
lineDash: [5, 5],
|
||||
},
|
||||
lineDash: [5, 5]
|
||||
}
|
||||
},
|
||||
{
|
||||
geometry: 'line',
|
||||
geometry: "line",
|
||||
smooth: true,
|
||||
color: '#5AD8A6',
|
||||
color: "#5AD8A6",
|
||||
lineStyle: {
|
||||
lineWidth: 4,
|
||||
opacity: 0.5,
|
||||
opacity: 0.5
|
||||
},
|
||||
label: {
|
||||
formatter: (datum) => {
|
||||
return `${datum.count}w`;
|
||||
},
|
||||
}
|
||||
},
|
||||
point: {
|
||||
shape: 'circle',
|
||||
shape: "circle",
|
||||
size: 4,
|
||||
style: {
|
||||
opacity: 0.5,
|
||||
stroke: '#5AD8A6',
|
||||
fill: '#fff',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
stroke: "#5AD8A6",
|
||||
fill: "#fff"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
function renderStatistic(containerWidth, text, style) {
|
||||
|
|
@ -987,7 +928,7 @@ function renderStatistic(containerWidth, text, style) {
|
|||
}
|
||||
|
||||
const textStyleStr = `width:${containerWidth}px;`;
|
||||
return `<div style="${textStyleStr};font-size:${scale}em;line-height:${scale < 1 ? 1 : 'inherit'};">${text}</div>`;
|
||||
return `<div style="${textStyleStr};font-size:${scale}em;line-height:${scale < 1 ? 1 : "inherit"};">${text}</div>`;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
.chartWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #e5e5e5;
|
||||
padding: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
.talentTableBox, .flowRateTableBox, .profitTableBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
& > div:not(:last-child) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
& > div {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background: #fff;
|
||||
padding: 40px 10px 8px;
|
||||
overflow: hidden;
|
||||
|
||||
:global {
|
||||
.ant-tabs {
|
||||
height: 100%;
|
||||
line-height: normal;
|
||||
|
||||
.ant-tabs-content-holder {
|
||||
flex: 1;
|
||||
|
||||
.ant-tabs-content {
|
||||
height: 100%;
|
||||
|
||||
.ant-tabs-tabpane {
|
||||
overflow-y: auto;
|
||||
|
||||
& > div {
|
||||
height: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
position: absolute;
|
||||
left: 2%;
|
||||
top: 2%;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > div {
|
||||
margin-bottom: 8px;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
.peopleFlowWrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #e5e5e5;
|
||||
overflow: auto;
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Author: 黎永顺
|
||||
* name: 人员流量表
|
||||
* Description:
|
||||
* Date: 2023/4/27
|
||||
*/
|
||||
import React, { FunctionComponent } from "react";
|
||||
import styles from "./index.less";
|
||||
|
||||
interface OwnProps {
|
||||
}
|
||||
|
||||
type Props = OwnProps;
|
||||
|
||||
const index: FunctionComponent<Props> = (props) => {
|
||||
|
||||
return (
|
||||
<div className={styles.peopleFlowWrapper}>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default index;
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
const AgeStructureBar = (echarts: any) => {
|
||||
// 实例化对象
|
||||
const myChart = echarts.init(document.getElementById("ageStructure"));
|
||||
// 配置项
|
||||
const option = {
|
||||
legend: {
|
||||
icon: "circle",
|
||||
top: "-1%",
|
||||
right: "0%",
|
||||
itemGap: 20,
|
||||
textStyle:{
|
||||
fontSize: 12,//字体大小
|
||||
color: '#ffffff'//字体颜色
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
// 坐标轴指示器,坐标轴触发有效
|
||||
trigger: "axis",
|
||||
axisPointer: {
|
||||
// 默认为直线,可选为:'line' | 'shadow'
|
||||
type: "shadow"
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: "0%",
|
||||
top: 20,
|
||||
right: "0%",
|
||||
bottom: "0%",
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: "value",
|
||||
show: false,
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 不显示坐标轴刻度线
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: "category",
|
||||
data: ["30岁以下", "30~40岁", "40~50岁", "50~60岁", "60岁以上"],
|
||||
// 修改坐标值样式
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
color: "rgba(255, 255, 255, 1)",
|
||||
fontSize: 10,
|
||||
show: true
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 不显示坐标轴刻度线
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "男",
|
||||
type: "bar",
|
||||
stack: "total",
|
||||
barWidth: "30%",
|
||||
label: {
|
||||
show: true,
|
||||
color: "rgba(255, 255, 255, 1)",
|
||||
fontSize: 10
|
||||
},
|
||||
emphasis: {
|
||||
focus: "series"
|
||||
},
|
||||
data: [320, 302, 301, 334, 390],
|
||||
// bar 样式修改
|
||||
itemStyle: {
|
||||
barBorderRadius: [10, 0 , 0 , 10],
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: "#3EA1FF" },
|
||||
{ offset: 0.5, color: "#72CFFF" },
|
||||
{ offset: 1, color: "#72CFFF" }
|
||||
]),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "女",
|
||||
type: "bar",
|
||||
stack: "total",
|
||||
barWidth: "30%",
|
||||
label: {
|
||||
show: true,
|
||||
color: "rgba(255, 255, 255, 1)",
|
||||
fontSize: 10
|
||||
},
|
||||
emphasis: {
|
||||
focus: "series"
|
||||
},
|
||||
data: [120, 132, 101, 134, 90],
|
||||
// bar 样式修改
|
||||
itemStyle: {
|
||||
barBorderRadius: [0, 10 , 10 , 0],
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: "#B2FDB2" },
|
||||
{ offset: 0.5, color: "#00D5FF" },
|
||||
{ offset: 1, color: "#00D5FF" }
|
||||
]),
|
||||
},
|
||||
}
|
||||
]
|
||||
};
|
||||
// 配置项给实例对象
|
||||
myChart.setOption(option);
|
||||
return myChart;
|
||||
};
|
||||
|
||||
export default AgeStructureBar;
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
const PersonnelCategoryBar = (echarts: any) => {
|
||||
// 实例化对象
|
||||
const myChart = echarts.init(document.getElementById("changeStatistics"));
|
||||
// 配置项
|
||||
const option = {
|
||||
tooltip: {
|
||||
// 坐标轴指示器,坐标轴触发有效
|
||||
trigger: "axis",
|
||||
axisPointer: {
|
||||
// 默认为直线,可选为:'line' | 'shadow'
|
||||
type: "shadow"
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: "0%",
|
||||
top: 10,
|
||||
right: "0%",
|
||||
bottom: "4%",
|
||||
// true: 数值离DOM盒子的距离 false: 坐标轴离DOM盒子的距离
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: "category",
|
||||
data: ["公司本部", "公司1", "公司2", "公司3", "公司4", "公司5", "公司6", "公司7"],
|
||||
axisTick: {
|
||||
alignWithLabel: true
|
||||
},
|
||||
// 修改坐标值样式
|
||||
axisLabel: {
|
||||
color: "rgba(255, 255, 255, 1)",
|
||||
fontSize: 10,
|
||||
show: true
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: "value",
|
||||
// 修改坐标值样式
|
||||
axisLabel: {
|
||||
color: "rgba(255, 255, 255, 1)",
|
||||
fontSize: 12
|
||||
},
|
||||
// 修改坐标轴线样式
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 不显示坐标轴刻度线
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: "dashed",
|
||||
color: "rgba(93,126,158,1)"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: "入职",
|
||||
type: "bar",
|
||||
showBackground: false,
|
||||
barWidth: "10%",
|
||||
data: [250, 250, 320, 410, 280, 320, 405, 405],
|
||||
// bar 样式修改
|
||||
itemStyle: {
|
||||
barBorderRadius: 10,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: "#3EA1FF" },
|
||||
{ offset: 0.5, color: "#72CFFF" },
|
||||
{ offset: 1, color: "#72CFFF" }
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "离职",
|
||||
type: "bar",
|
||||
showBackground: false,
|
||||
barWidth: "10%",
|
||||
data: [370, 370, 260, 110, 390, 280, 110, 110],
|
||||
// bar 样式修改
|
||||
itemStyle: {
|
||||
barBorderRadius: 10,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: "#00D5FF" },
|
||||
{ offset: 0.5, color: "#B2FDB2" },
|
||||
{ offset: 1, color: "#B2FDB2" }
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "调职",
|
||||
type: "bar",
|
||||
showBackground: false,
|
||||
barWidth: "10%",
|
||||
data: [310, 310, 80, 280, 310, 70, 280, 280],
|
||||
// bar 样式修改
|
||||
itemStyle: {
|
||||
barBorderRadius: 10,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: "#1455FF" },
|
||||
{ offset: 0.5, color: "#5F9CFF" },
|
||||
{ offset: 1, color: "#5F9CFF" }
|
||||
])
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
// 配置项给实例对象
|
||||
myChart.setOption(option);
|
||||
return myChart;
|
||||
};
|
||||
|
||||
export default PersonnelCategoryBar;
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
import { getPie3D } from "@/utils/chart";
|
||||
|
||||
const color = ["#FE772D", "#FEDB4B", "#2A71FF", "#00EDFE"];
|
||||
|
||||
const DegreeStructurePie = (echarts: any) => {
|
||||
// 实例化对象
|
||||
const myChart = echarts.init(document.getElementById("degreeStructure"));
|
||||
// 配置项
|
||||
const option = {
|
||||
...getPie3D(setLabel([
|
||||
{
|
||||
name: "研究生(博士)",
|
||||
value: 176
|
||||
},
|
||||
{
|
||||
name: "研究生(硕士)",
|
||||
value: 288
|
||||
},
|
||||
{
|
||||
name: "本科",
|
||||
value: 588
|
||||
},
|
||||
{
|
||||
name: "大专及以下",
|
||||
value: 78
|
||||
}
|
||||
], color), 0.6, 240, 26, 18, 1, true)
|
||||
};
|
||||
// 配置项给实例对象
|
||||
myChart.setOption({
|
||||
...option,
|
||||
legend: {
|
||||
orient: "vertical",
|
||||
icon: "circle",
|
||||
top: "25%",
|
||||
left: "10%",
|
||||
itemGap: 28,
|
||||
textStyle: {
|
||||
fontSize: 12,//字体大小
|
||||
color: "#ffffff"//字体颜色
|
||||
}
|
||||
}
|
||||
});
|
||||
return myChart;
|
||||
};
|
||||
|
||||
export default DegreeStructurePie;
|
||||
|
||||
export const setLabel = (optionData: any, color: any) => {
|
||||
return optionData.map((item: any, index: number) => {
|
||||
return {
|
||||
...item,
|
||||
itemStyle: {
|
||||
color: color[index]
|
||||
},
|
||||
label: {
|
||||
normal: {
|
||||
show: false,
|
||||
color: color[index],
|
||||
position: "right",
|
||||
// distance:-10,
|
||||
offset: [0, 3],
|
||||
formatter: [
|
||||
"{d|{d}%}",
|
||||
"————",
|
||||
// '{c|{c}}{b|台}',
|
||||
"{b|{b}}"
|
||||
].join("\n"), // 用\n来换行
|
||||
rich: {
|
||||
b: {
|
||||
// color: '#fff',
|
||||
lineHeight: 25,
|
||||
align: "left",
|
||||
color: color[index]
|
||||
},
|
||||
c: {
|
||||
fontSize: 22,
|
||||
textShadowColor: "#1c90a6",
|
||||
textShadowOffsetX: 0,
|
||||
textShadowOffsetY: 2,
|
||||
textShadowBlur: 5,
|
||||
color: color[index]
|
||||
},
|
||||
d: {
|
||||
color: color[index],
|
||||
align: "left"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
normal: {
|
||||
show: false,
|
||||
length2: 30,
|
||||
lineStyle: {
|
||||
width: 1,
|
||||
color: color[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import { getPie3D } from "@/utils/chart";
|
||||
import { setLabel } from "./degreeStructurePie";
|
||||
|
||||
const color = ["#BD42EF", "#6D45F3", "#3358F3", "#6ECDE5"];
|
||||
const GradAnalysisPie = (echarts: any) => {
|
||||
// 实例化对象
|
||||
const myChart = echarts.init(document.getElementById("gradAnalysis"));
|
||||
// 配置项
|
||||
const option = {
|
||||
...getPie3D(setLabel([
|
||||
{
|
||||
name: "正高级",
|
||||
value: 500
|
||||
},
|
||||
{
|
||||
name: "副高级",
|
||||
value: 1120
|
||||
},
|
||||
{
|
||||
name: "中级",
|
||||
value: 4567
|
||||
},
|
||||
{
|
||||
name: "初级",
|
||||
value: 5278
|
||||
}
|
||||
], color), 0.6, 240, 26, 18, 1, false)
|
||||
};
|
||||
// 配置项给实例对象
|
||||
myChart.setOption({
|
||||
...option,
|
||||
legend: {
|
||||
orient: "vertical",
|
||||
icon: "circle",
|
||||
top: "25%",
|
||||
left: "10%",
|
||||
itemGap: 28,
|
||||
textStyle: {
|
||||
fontSize: 12,//字体大小
|
||||
color: "#ffffff"//字体颜色
|
||||
}
|
||||
}
|
||||
});
|
||||
return myChart;
|
||||
};
|
||||
|
||||
export default GradAnalysisPie;
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
const PersonnelCategoryBar = (echarts: any) => {
|
||||
// 实例化对象
|
||||
const myChart = echarts.init(document.getElementById("personnelCategory"));
|
||||
// 配置项
|
||||
const option = {
|
||||
tooltip: {
|
||||
// 坐标轴指示器,坐标轴触发有效
|
||||
trigger: "axis",
|
||||
axisPointer: {
|
||||
// 默认为直线,可选为:'line' | 'shadow'
|
||||
type: "shadow"
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: "0%",
|
||||
top: 10,
|
||||
right: "0%",
|
||||
bottom: "4%",
|
||||
// true: 数值离DOM盒子的距离 false: 坐标轴离DOM盒子的距离
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: "category",
|
||||
data: ["领导班子", "海外中国籍", "正式在岗", "海外外籍", "退休返聘", "其他临时", "劳务派遣", "内退"],
|
||||
axisTick: {
|
||||
alignWithLabel: true
|
||||
},
|
||||
// 修改坐标值样式
|
||||
axisLabel: {
|
||||
color: "rgba(255, 255, 255, 1)",
|
||||
fontSize: 10,
|
||||
rotate: 60,
|
||||
show: true
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: "value",
|
||||
// 修改坐标值样式
|
||||
axisLabel: {
|
||||
color: "rgba(255, 255, 255, 1)",
|
||||
fontSize: 12
|
||||
},
|
||||
// 修改坐标轴线样式
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 不显示坐标轴刻度线
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: "dashed",
|
||||
color: "rgba(93,126,158,1)"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: "直接访问",
|
||||
type: "bar",
|
||||
showBackground: true,
|
||||
barWidth: "30%",
|
||||
data: [10, 52, 200, 334, 390, 1030, 220, 210],
|
||||
// bar 样式修改
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: "#3EA1FF" },
|
||||
{ offset: 0.5, color: "#72CFFF" },
|
||||
{ offset: 1, color: "#72CFFF" }
|
||||
]),
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: "#AFD8FF" },
|
||||
{ offset: 0.7, color: "#AFD8FF" },
|
||||
{ offset: 1, color: "#AFD8FF" }
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
// 配置项给实例对象
|
||||
myChart.setOption(option);
|
||||
return myChart;
|
||||
};
|
||||
|
||||
export default PersonnelCategoryBar;
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Author: 黎永顺
|
||||
* name: 框架组件
|
||||
* Description:
|
||||
* Date: 2023/5/4
|
||||
*/
|
||||
import React, { FC, useState } from "react";
|
||||
import cs from "classnames";
|
||||
import styles from "./index.less";
|
||||
|
||||
interface OwnProps {
|
||||
position: string;
|
||||
tabList: Array<any>;
|
||||
children: any;
|
||||
titleImg: any;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
type Props = OwnProps;
|
||||
const FrameComp: FC<Props> = (props) => {
|
||||
const { position, children, tabList, titleImg } = props;
|
||||
const [active, setActive] = useState<number>(0);
|
||||
|
||||
return (
|
||||
<div className={styles.frameWrapper}>
|
||||
<div className={styles.headerTop}>
|
||||
<div className={cs(styles.headerLeft, { [styles["halfHeaderLeft"]]: position === "right" })}>
|
||||
<img src={titleImg} alt=""/>
|
||||
{
|
||||
position === "right" &&
|
||||
<ul className={styles.tabWrapper}>
|
||||
{
|
||||
_.map(tabList, (item, index) => {
|
||||
const classes = cs({
|
||||
[styles["liActive"]]: index === active
|
||||
});
|
||||
return <li key={index} onClick={() => setActive(index)} className={classes}>{item}</li>;
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
<span className={styles.moreWrapper}>More <img src={require("../../../../assets/images/more.png")}
|
||||
alt=""/></span>
|
||||
</div>
|
||||
{
|
||||
position === "bottom" &&
|
||||
<ul className={styles.tabWrapper}>
|
||||
{
|
||||
_.map(tabList, (item, index) => {
|
||||
const classes = cs({
|
||||
[styles["liActive"]]: index === active
|
||||
});
|
||||
return <li key={index} onClick={() => setActive(index)} className={classes}>{item}</li>;
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FrameComp;
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
.topPageWrapper {
|
||||
height: 1.25rem;
|
||||
position: relative;
|
||||
background: url("../../../../assets/images/head_bg1.png") no-repeat;
|
||||
background-size: 100% 100%;
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
line-height: 0.875rem;
|
||||
position: relative;
|
||||
|
||||
.updateTime {
|
||||
color: #fff;
|
||||
line-height: 0;
|
||||
margin-top: 0.0625rem;
|
||||
|
||||
img {
|
||||
width: 0.225rem;
|
||||
margin-right: .2rem;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 4.475rem;
|
||||
}
|
||||
}
|
||||
|
||||
.logoWrapper, .showTime {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.showTime {
|
||||
right: 2.375rem;
|
||||
top: 0.4rem;
|
||||
text-align: right;
|
||||
color: #fff;
|
||||
|
||||
.dateDay {
|
||||
padding: 0 .35rem;
|
||||
}
|
||||
}
|
||||
|
||||
.logoWrapper {
|
||||
top: 0.1rem;
|
||||
left: 2.375rem;
|
||||
line-height: 0.875rem;
|
||||
|
||||
img {
|
||||
width: 1.675rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mapWrapper {
|
||||
width: 10.625rem;
|
||||
height: 8.125rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.frameWrapper {
|
||||
background: url("../../../../assets/images/frame_bg.png") no-repeat;
|
||||
background-size: 100% 100%;
|
||||
margin-bottom: 0.2rem;
|
||||
|
||||
.headerTop {
|
||||
height: 0.475rem;
|
||||
position: relative;
|
||||
background: url("../../../../assets/images/frame_title_bg.png") no-repeat;
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.halfHeaderLeft {
|
||||
left: 1.45rem !important;
|
||||
}
|
||||
|
||||
.headerLeft {
|
||||
position: absolute;
|
||||
left: 0.7375rem;
|
||||
top: 0.0625rem;
|
||||
display: flex;
|
||||
|
||||
img {
|
||||
height: 0.3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.moreWrapper {
|
||||
color: #fff;
|
||||
font-size: 0.175rem;
|
||||
position: absolute;
|
||||
right: 0.3375rem;
|
||||
top: 0.1rem;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 0.2375rem;
|
||||
}
|
||||
}
|
||||
|
||||
.tabWrapper {
|
||||
margin-left: 0.775rem;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tabWrapper {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
padding: 0 0.3375rem;
|
||||
|
||||
li {
|
||||
padding: 0.1rem 0.175rem;
|
||||
font-size: 14px;
|
||||
color: #ACACAC;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
li.liActive {
|
||||
background: url("../../../../assets/images/tabSelected.png") no-repeat;
|
||||
background-size: 100% 100%;
|
||||
background-position-y: .125rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Author: 黎永顺
|
||||
* name: 地图组件
|
||||
* Description:
|
||||
* Date: 2023/5/4
|
||||
*/
|
||||
import React, { FC } from "react";
|
||||
import Chart from "@/utils/chart";
|
||||
import { mapOptions } from "./options";
|
||||
import styles from "./index.less";
|
||||
|
||||
interface OwnProps {
|
||||
}
|
||||
|
||||
type Props = OwnProps;
|
||||
const Map: FC<Props> = (props) => {
|
||||
|
||||
return (
|
||||
<div className={styles.mapWrapper}>
|
||||
<Chart renderer={"canvas"} option={mapOptions({})}/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Map;
|
||||
|
|
@ -0,0 +1,243 @@
|
|||
import "echarts/map/js/china";
|
||||
// 地图数据
|
||||
const mapData = {
|
||||
citys: [
|
||||
{
|
||||
name: "浙江",
|
||||
value: [120.15, 29.28, -2],
|
||||
symbolSize: 2,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "#FFF"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "四川",
|
||||
value: [103.36, 30.65, -5],
|
||||
symbolSize: 2,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "#FFF"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "内蒙古",
|
||||
value: [112.17, 42.81, -23],
|
||||
symbolSize: 2,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "#FFF"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "江苏",
|
||||
value: [120.26, 32.54, -1],
|
||||
symbolSize: 2,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "#FFF"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "西藏",
|
||||
value: [89.13, 30.66, -1],
|
||||
symbolSize: 2,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "#FFF"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
export const salaryData = {
|
||||
header: ["业务板块", "二级机构", "人员类型", "起始时间", "截止时间", "发薪人数", "固定薪酬", "浮动薪酬", "薪酬合计", "薪酬固浮化"],
|
||||
data: [
|
||||
["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"],
|
||||
["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"],
|
||||
["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"],
|
||||
["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"],
|
||||
["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"],
|
||||
["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"],
|
||||
["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"],
|
||||
["国内工程", "建设集团", "领导班子", "2023.1", "2023.1", "23", "20,000", "20,000", "20,000", "20,000"]
|
||||
]
|
||||
};
|
||||
|
||||
export const mapOptions = (params) => ({
|
||||
title: {
|
||||
show: false,
|
||||
text: "全国物流输送图",
|
||||
left: "center",
|
||||
textStyle: {
|
||||
color: "#fff"
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
geo: [
|
||||
{
|
||||
nameMap: {
|
||||
China: "中国"
|
||||
},
|
||||
map: "china",
|
||||
zlevel: 5,
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
formatter: function (params) {
|
||||
//圆环显示文字
|
||||
return params.name === "江苏" || params.name === "浙江" || params.name === "四川" || params.name === "内蒙古" || params.name === "西藏" ? params.name : "";
|
||||
},
|
||||
fontSize: 12,
|
||||
color: "#FFF"
|
||||
},
|
||||
emphasis: {
|
||||
show: true,
|
||||
color: "#FFF"
|
||||
}
|
||||
},
|
||||
zoom: 1.2,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
borderColor: "rgba(148,224,250, .5)", //区域边框颜色
|
||||
areaColor: "rgba(82,164,238,.1)", //区域颜色
|
||||
borderWidth: 1, //区域边框宽度
|
||||
shadowBlur: 5,
|
||||
shadowColor: "rgba(3,113,173,.7)"
|
||||
},
|
||||
emphasis: {
|
||||
borderColor: "rgba(148,224,250, .5)",
|
||||
areaColor: "rgba(6,89,176,.3)",
|
||||
borderWidth: 2.5,
|
||||
shadowBlur: 5,
|
||||
shadowColor: "rgba(3,113,173,.5)"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
nameMap: {
|
||||
China: "中国"
|
||||
},
|
||||
map: "china",
|
||||
zlevel: 4,
|
||||
top: "11%",
|
||||
label: {
|
||||
emphasis: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
zoom: 1.2,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
// borderColor: 'rgba(255,209,163, .5)', //区域边框颜色
|
||||
areaColor: "rgba(73,86,166,.1)", //区域颜色
|
||||
borderWidth: 0.5, //区域边框宽度
|
||||
shadowBlur: 5,
|
||||
shadowColor: "rgba(107,91,237,.7)"
|
||||
},
|
||||
emphasis: {
|
||||
borderColor: "transparent",
|
||||
areaColor: "transparent",
|
||||
borderWidth: 1,
|
||||
shadowBlur: 5,
|
||||
shadowColor: "transparent"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
nameMap: {
|
||||
China: "中国"
|
||||
},
|
||||
map: "china",
|
||||
zlevel: 3,
|
||||
top: "12%",
|
||||
label: {
|
||||
emphasis: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
zoom: 1.2,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
areaColor: "rgba(73,86,166,.1)", //区域颜色
|
||||
borderWidth: 0.5, //区域边框宽度
|
||||
shadowBlur: 5,
|
||||
shadowColor: "rgba(107,91,237,.7)"
|
||||
},
|
||||
emphasis: {
|
||||
borderColor: "transparent",
|
||||
areaColor: "transparent",
|
||||
borderWidth: 1,
|
||||
shadowBlur: 5,
|
||||
shadowColor: "transparent"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
nameMap: {
|
||||
China: "中国"
|
||||
},
|
||||
map: "china",
|
||||
zlevel: 2,
|
||||
top: "13%",
|
||||
label: {
|
||||
emphasis: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
zoom: 1.2,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
// borderColor: 'rgba(255,209,163, .5)', //区域边框颜色
|
||||
areaColor: "rgba(73,86,166,.1)", //区域颜色
|
||||
borderWidth: 0.5, //区域边框宽度
|
||||
shadowBlur: 5,
|
||||
shadowColor: "rgba(107,91,237,.7)"
|
||||
},
|
||||
emphasis: {
|
||||
borderColor: "transparent",
|
||||
areaColor: "transparent",
|
||||
borderWidth: 1,
|
||||
shadowBlur: 5,
|
||||
shadowColor: "transparent"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: "地点",
|
||||
type: "custom",
|
||||
coordinateSystem: "geo",
|
||||
zlevel: 12,
|
||||
renderItem(params, api) {
|
||||
//具体实现自定义图标的方法
|
||||
return {
|
||||
type: "image",
|
||||
style: {
|
||||
image: require("../../../../assets/images/zs.png"),
|
||||
x: api.coord([
|
||||
mapData.citys[params.dataIndex].value[0],
|
||||
mapData.citys[params.dataIndex].value[1]
|
||||
])[0],
|
||||
y: api.coord([
|
||||
mapData.citys[params.dataIndex].value[0],
|
||||
mapData.citys[params.dataIndex].value[1]
|
||||
])[1]
|
||||
}
|
||||
};
|
||||
},
|
||||
data: mapData.citys
|
||||
}
|
||||
]
|
||||
});
|
||||
export const userOptions = (params = {}) => ({
|
||||
header: params.header,
|
||||
data: params.data
|
||||
});
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Author: 黎永顺
|
||||
* name: 大屏头部组件
|
||||
* Description:
|
||||
* Date: 2023/5/4
|
||||
*/
|
||||
import React, { FC, useEffect, useRef, useState } from "react";
|
||||
import { formatTime } from "@/utils/common";
|
||||
import styles from "./index.less";
|
||||
|
||||
interface OwnProps {
|
||||
}
|
||||
|
||||
type Props = OwnProps;
|
||||
const weekday = ["星期天", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
|
||||
const TopPage: FC<Props> = (props) => {
|
||||
const [timeStr, setTimeStr] = useState<any>({});
|
||||
const timing = useRef<any>(null);
|
||||
useEffect(() => {
|
||||
setTimingFn();
|
||||
return () => {
|
||||
timing.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const setTimingFn = () => {
|
||||
timing.current = setInterval(() => {
|
||||
let dateYear = formatTime(new Date(), "yyyy-MM-dd");
|
||||
let dateDay = formatTime(new Date(), "HH:mm:ss");
|
||||
let dateWeek = weekday[new Date().getDay()];
|
||||
setTimeStr({ dateYear, dateDay, dateWeek });
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.topPageWrapper}>
|
||||
<span className={styles.logoWrapper}><img src={require("../../../../assets/images/weaverlogo.png")}
|
||||
alt=""/></span>
|
||||
<div className={styles.title}>
|
||||
<img src={require("../../../../assets/images/head_title.png")} alt=""/>
|
||||
<div className={styles.updateTime}>
|
||||
<img src={require("../../../../assets/images/refresh.png")} alt=""/>
|
||||
数据更新时间:16:02:09
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
className={styles.showTime}><span>{timeStr?.dateYear}</span><span
|
||||
className={styles.dateDay}>{timeStr?.dateDay}</span><span>{timeStr?.dateWeek}</span></span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TopPage;
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
.screenWrapperRoot {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
|
||||
.screenWrapper {
|
||||
line-height: 1.15;
|
||||
min-height: 100vh;
|
||||
background: url("../../../assets/images/pageBg.png") top center no-repeat;
|
||||
background-size: 100% 100%;
|
||||
|
||||
.screenMain {
|
||||
display: flex;
|
||||
min-width: 1024px;
|
||||
max-width: 1920px;
|
||||
margin: 0 auto;
|
||||
padding: 0 0.6875rem;
|
||||
|
||||
.screenColCenter {
|
||||
flex: 5 1;
|
||||
margin: 0 0.125rem;
|
||||
position: relative;
|
||||
|
||||
.cardWrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 99;
|
||||
padding: 0.25rem 0.125rem 0;
|
||||
|
||||
& > li:not(:last-child) {
|
||||
margin-right: 0.275rem;
|
||||
}
|
||||
|
||||
& > li {
|
||||
flex: 1;
|
||||
min-height: 1.1625rem;
|
||||
background: url("../../../assets/images/card_bg.png") no-repeat;
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
width: 0.725rem;
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
|
||||
.cardInner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #FFF;
|
||||
min-width: 1.125rem;
|
||||
text-align: center;
|
||||
|
||||
span:first-child {
|
||||
font-size: 0.275rem;
|
||||
margin-bottom: 0.1rem;
|
||||
}
|
||||
|
||||
span:last-child {
|
||||
font-size: 0.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.screenCol {
|
||||
flex: 3 1;
|
||||
|
||||
.personnelCate, .ageStruct, .degreeStructure, .gradAnalysis {
|
||||
height: 3.875rem;
|
||||
padding: 0 0.3375rem;
|
||||
}
|
||||
}
|
||||
|
||||
.screenFooter {
|
||||
display: flex;
|
||||
min-width: 1024px;
|
||||
max-width: 1920px;
|
||||
margin: 0 auto;
|
||||
padding: 0 0.6875rem;
|
||||
|
||||
& > div:first-child {
|
||||
margin-right: 0.125rem;
|
||||
}
|
||||
|
||||
.screenFooterCol {
|
||||
flex: 1;
|
||||
|
||||
.payrollStatistics {
|
||||
padding-bottom: 0.2375rem !important;
|
||||
|
||||
:global {
|
||||
.dv-scroll-board {
|
||||
.rows .row-item, .header {
|
||||
font-size: 0.15rem !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.payrollStatistics, .personnelChangeStatistics {
|
||||
height: 3.5rem;
|
||||
padding: 0 0.3375rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Author: 黎永顺
|
||||
* name: 报表大屏
|
||||
* Description:
|
||||
* Date: 2023/5/4
|
||||
*/
|
||||
import React, { FC, useEffect } from "react";
|
||||
// @ts-ignore
|
||||
import echarts from "echarts";
|
||||
import "echarts-gl";
|
||||
import TopPage from "./components/topPage";
|
||||
import Map from "./components/map";
|
||||
import FrameComp from "./components/frameComp";
|
||||
import PersonnelCategoryBar from "./components/echarts/personnelCategoryBar";
|
||||
import AgeStructureBar from "./components/echarts/ageStructureBar";
|
||||
import DegreeStructurePie from "./components/echarts/degreeStructurePie";
|
||||
import GradAnalysisPie from "./components/echarts/gradAnalysisPie";
|
||||
import ChangeStatisticsBar from "./components/echarts/changeStatisticsBar";
|
||||
import { salaryData, userOptions } from "./components/options";
|
||||
// @ts-ignore
|
||||
import { ScrollBoard } from "@jiaminghi/data-view-react";
|
||||
import styles from "./index.less";
|
||||
|
||||
interface OwnProps {
|
||||
}
|
||||
|
||||
type Props = OwnProps;
|
||||
const Index: FC<Props> = (props) => {
|
||||
useEffect(() => {
|
||||
const personnelCategoryBar = PersonnelCategoryBar(echarts);
|
||||
const ageStructureBar = AgeStructureBar(echarts);
|
||||
const degreeStructurePie = DegreeStructurePie(echarts);
|
||||
const gradAnalysisPie = GradAnalysisPie(echarts);
|
||||
const changeStatisticsBar = ChangeStatisticsBar(echarts);
|
||||
// 屏幕缩放对chart图表进行自适应处理,调用实例的resize方法
|
||||
window.onresize = () => {
|
||||
personnelCategoryBar.resize();
|
||||
ageStructureBar.resize();
|
||||
degreeStructurePie.resize();
|
||||
gradAnalysisPie.resize();
|
||||
changeStatisticsBar.resize();
|
||||
};
|
||||
return () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.screenWrapperRoot}>
|
||||
<div className={styles.screenWrapper}>
|
||||
<TopPage/>
|
||||
<div className={styles.screenMain}>
|
||||
<div className={styles.screenCol}>
|
||||
<FrameComp position="bottom" tabList={["全部", "总部", "苏皖大区", "山东大区"]}
|
||||
titleImg={require("../../../assets/images/rylbylb.png")}>
|
||||
<div className={styles.personnelCate} id="personnelCategory"/>
|
||||
</FrameComp>
|
||||
<FrameComp position="bottom" tabList={["全部", "总部", "苏皖大区"]}
|
||||
titleImg={require("../../../assets/images/hxzgzgnljg.png")}>
|
||||
<div className={styles.ageStruct} id="ageStructure"/>
|
||||
</FrameComp>
|
||||
</div>
|
||||
<div className={styles.screenColCenter}>
|
||||
<ul className={styles.cardWrapper}>
|
||||
<li>
|
||||
<img src={require("../../../assets/images/xsje.png")} alt=""/>
|
||||
<div className={styles.cardInner}>
|
||||
<span>8,29,000</span>
|
||||
<span>销售金额</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<img src={require("../../../assets/images/xmys.png")} alt=""/>
|
||||
<div className={styles.cardInner}>
|
||||
<span>3,29,000</span>
|
||||
<span>项目验收</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<img src={require("../../../assets/images/yjgz.png")} alt=""/>
|
||||
<div className={styles.cardInner}>
|
||||
<span>12,000</span>
|
||||
<span>月均工资</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<img src={require("../../../assets/images/rwdcl.png")} alt=""/>
|
||||
<div className={styles.cardInner}>
|
||||
<span>98%</span>
|
||||
<span>任务达成率</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<Map/>
|
||||
</div>
|
||||
<div className={styles.screenCol}>
|
||||
<FrameComp position="bottom" tabList={["全部", "总部", "苏皖大区"]}
|
||||
titleImg={require("../../../assets/images/hxzgzgxljg.png")}>
|
||||
<div className={styles.degreeStructure} id="degreeStructure"></div>
|
||||
</FrameComp>
|
||||
<FrameComp position="bottom" tabList={["全部", "总部", "苏皖大区"]}
|
||||
titleImg={require("../../../assets/images/hxzgzgdjfx.png")}>
|
||||
<div className={styles.gradAnalysis} id="gradAnalysis"></div>
|
||||
</FrameComp>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.screenFooter}>
|
||||
<div className={styles.screenFooterCol}>
|
||||
<FrameComp position="right" tabList={["全部", "总部", "苏皖大区"]}
|
||||
titleImg={require("../../../assets/images/zgxcfftj.png")}>
|
||||
<div className={styles.payrollStatistics}>
|
||||
<ScrollBoard
|
||||
config={{
|
||||
// 表头背景色
|
||||
headerBGC: "#012B56",
|
||||
// 奇数行背景色
|
||||
oddRowBGC: "#042043",
|
||||
// 偶数行背景色
|
||||
evenRowBGC: "#00376D",
|
||||
// 行号
|
||||
index: false,
|
||||
// 行号表头
|
||||
// indexHeader: "序号",
|
||||
// 宽度
|
||||
// columnWidth: [],
|
||||
// 对其方式
|
||||
align: ["center"],
|
||||
// 表行数
|
||||
rowNum: 4,
|
||||
...userOptions(salaryData)
|
||||
}}
|
||||
></ScrollBoard>
|
||||
</div>
|
||||
</FrameComp>
|
||||
</div>
|
||||
<div className={styles.screenFooterCol}>
|
||||
<FrameComp position="right" tabList={["全部", "总部", "苏皖大区"]}
|
||||
titleImg={require("../../../assets/images/hxzgzgrybdtj.png")}>
|
||||
<div className={styles.personnelChangeStatistics} id="changeStatistics"/>
|
||||
</FrameComp>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Index;
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
.profitWrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #e5e5e5;
|
||||
padding: 8px;
|
||||
overflow: hidden;
|
||||
|
||||
.profitLeft {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 580px;
|
||||
overflow: hidden;
|
||||
|
||||
& > :not(:last-child) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
& > div:first-child {
|
||||
& > div {
|
||||
height: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
& > div {
|
||||
flex: 1;
|
||||
background: #FFF;
|
||||
position: relative;
|
||||
padding: 40px 10px 8px;
|
||||
overflow: hidden;
|
||||
|
||||
.title {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 1%;
|
||||
top: 2%;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
:global {
|
||||
.ant-tabs {
|
||||
height: 100%;
|
||||
line-height: normal;
|
||||
|
||||
.ant-tabs-content-holder {
|
||||
flex: 1;
|
||||
|
||||
.ant-tabs-content {
|
||||
height: 100%;
|
||||
|
||||
.ant-tabs-tabpane {
|
||||
overflow-y: auto;
|
||||
|
||||
& > div {
|
||||
height: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.profitRight {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
margin-left: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
& > div:first-child {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
& > div {
|
||||
background: #fff;
|
||||
flex: 1;
|
||||
padding: 40px 10px 8px;
|
||||
position: relative;
|
||||
height: 100px;
|
||||
|
||||
:global {
|
||||
.ant-tabs {
|
||||
height: 100%;
|
||||
line-height: normal;
|
||||
|
||||
.ant-tabs-content-holder {
|
||||
flex: 1;
|
||||
|
||||
.ant-tabs-content {
|
||||
height: 100%;
|
||||
|
||||
.ant-tabs-tabpane {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 1%;
|
||||
top: 2%;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +1,24 @@
|
|||
/*
|
||||
* Author: 黎永顺
|
||||
* name:人才利润报表
|
||||
* Description:
|
||||
* Date: 2023/4/27
|
||||
*/
|
||||
import React, { FC } from "react";
|
||||
import styles from "./index.less";
|
||||
import { Avatar, List, Tabs } from "antd";
|
||||
import { Column, Funnel, Pie, DualAxes } from "@ant-design/plots";
|
||||
import {
|
||||
degreeData,
|
||||
flowingConfig,
|
||||
flowingTrendconfig,
|
||||
genderData,
|
||||
fixedIncomePerData,
|
||||
floatingIncomePerData,
|
||||
humanAnalysisconfig,
|
||||
humanEfficiencyRankingData,
|
||||
institutionConfig,
|
||||
internData,
|
||||
linesData,
|
||||
multiplData,
|
||||
multipleConfig, newSaleEfficiencyRankingData,
|
||||
personChangeConfig,
|
||||
personChangeData,
|
||||
rankingConfig,
|
||||
rankingData, saleEfficiencyRankingData,
|
||||
seniorityConfig,
|
||||
seniorityData,
|
||||
trainingData,totalLaborCostConfig,
|
||||
laborCostsPerCapitaConfig,
|
||||
perCapitaIncomeConfig, fixedIncomePerData, floatingIncomePerData
|
||||
} from "./constants";
|
||||
import styles from "./index.less";
|
||||
newSaleEfficiencyRankingData,
|
||||
perCapitaIncomeConfig,
|
||||
saleEfficiencyRankingData,
|
||||
totalLaborCostConfig
|
||||
} from "@/pages/personnelReport/constants";
|
||||
import { Column, DualAxes, Pie } from "@ant-design/plots";
|
||||
|
||||
interface OwnProps {
|
||||
}
|
||||
|
|
@ -31,51 +26,42 @@ interface OwnProps {
|
|||
type Props = OwnProps;
|
||||
|
||||
const index: FC<Props> = (props) => {
|
||||
// @ts-ignore
|
||||
|
||||
return (
|
||||
<div className={styles.chartWrapper}>
|
||||
{/*人才结构表*/}
|
||||
<div className={styles.talentTableBox}>
|
||||
<div>
|
||||
<div className={styles.title}>人员流动情况</div>
|
||||
<Pie {...flowingConfig} />
|
||||
<div className={styles.profitWrapper}>
|
||||
<div className={styles.profitLeft}>
|
||||
<div className={styles.profitLeftItem1}>
|
||||
<span className={styles.title}>人效分析</span>
|
||||
{/*@ts-ignore*/}
|
||||
<Column {...humanAnalysisconfig} />
|
||||
</div>
|
||||
<div style={{ paddingTop: 0 }}>
|
||||
<div className={styles.profitLeftItem2}>
|
||||
<span className={styles.title}>人均收入</span>
|
||||
<Tabs defaultActiveKey="1">
|
||||
<Tabs.TabPane tab="司龄占比" key="1">
|
||||
<Pie {...seniorityConfig} data={seniorityData}/>
|
||||
<Tabs.TabPane tab="人均固定收入" key="1">
|
||||
<Pie {...perCapitaIncomeConfig} data={fixedIncomePerData}/>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="性别占比" key="2">
|
||||
<Pie {...seniorityConfig} data={genderData}/>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="学历分布" key="3">
|
||||
<Pie {...seniorityConfig} data={degreeData}/>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="条线占比" key="4">
|
||||
<Pie {...seniorityConfig} data={linesData}/>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="实习生占比" key="5">
|
||||
<Pie {...seniorityConfig} data={internData}/>
|
||||
<Tabs.TabPane tab="人均浮动收入" key="2">
|
||||
<Pie {...perCapitaIncomeConfig} data={floatingIncomePerData}/>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.title}>人员流动情况趋势图</div>
|
||||
<Column {...flowingTrendconfig} />
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.title}>机构人员排名数</div>
|
||||
<Funnel {...institutionConfig} />
|
||||
<div className={styles.profitLeftItem3}>
|
||||
<span className={styles.title}>人工成本</span>
|
||||
<Tabs defaultActiveKey="1">
|
||||
<Tabs.TabPane tab="人工总成本" key="1">
|
||||
{/*@ts-ignore*/}
|
||||
<DualAxes {...totalLaborCostConfig} />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="人均人工成本" key="2">
|
||||
<DualAxes {...laborCostsPerCapitaConfig} />
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
{/*人才利润报表*/}
|
||||
<div className={styles.profitTableBox}>
|
||||
<div className={styles.profitRight}>
|
||||
<div>
|
||||
<div className={styles.title}>人效分析</div>
|
||||
<Column {...humanAnalysisconfig} />
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.title}>人效分析排名</div>
|
||||
<span className={styles.title}>人效分析排名</span>
|
||||
<Tabs defaultActiveKey="1">
|
||||
<Tabs.TabPane tab="有效金额排名" key="1">
|
||||
<List
|
||||
|
|
@ -128,7 +114,7 @@ const index: FC<Props> = (props) => {
|
|||
</Tabs>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.title}>团队效能排名</div>
|
||||
<span className={styles.title}>团队效能排名</span>
|
||||
<Tabs defaultActiveKey="1">
|
||||
<Tabs.TabPane tab="指标<400万" key="1">
|
||||
<List
|
||||
|
|
@ -244,47 +230,6 @@ const index: FC<Props> = (props) => {
|
|||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.title}>人均收入</div>
|
||||
<Tabs defaultActiveKey="1">
|
||||
<Tabs.TabPane tab="人均固定收入" key="1">
|
||||
<Pie {...perCapitaIncomeConfig} data={fixedIncomePerData}/>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="人均浮动收入" key="2">
|
||||
<Pie {...perCapitaIncomeConfig} data={floatingIncomePerData}/>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.title}>人工成本</div>
|
||||
<Tabs defaultActiveKey="1">
|
||||
<Tabs.TabPane tab="人工总成本" key="1">
|
||||
<DualAxes {...totalLaborCostConfig} />
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab="人均人工成本" key="2">
|
||||
<DualAxes {...laborCostsPerCapitaConfig} />
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
{/*人员流量表*/}
|
||||
<div className={styles.flowRateTableBox}>
|
||||
<div>
|
||||
<div className={styles.title}>人员异动分析</div>
|
||||
<Pie {...personChangeConfig} data={personChangeData}/>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.title}>详细图表</div>
|
||||
<Column {...multipleConfig} data={multiplData}/>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.title}>培训管理分析</div>
|
||||
<Pie {...seniorityConfig} data={trainingData}/>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.title}>培训排名</div>
|
||||
<Column {...rankingConfig} data={rankingData}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
const AgeStaticsPie = (echarts: any) => {
|
||||
// 实例化对象
|
||||
const myChart = echarts.init(document.getElementById("ageStatics"));
|
||||
// 配置项
|
||||
const option = {
|
||||
legend: {
|
||||
icon: "circle",
|
||||
bottom: "0%",
|
||||
left: "center",
|
||||
itemGap: 20,
|
||||
textStyle: {
|
||||
fontSize: 12,//字体大小
|
||||
color: "#787E95"//字体颜色
|
||||
}
|
||||
},
|
||||
color: ["#5FCD7B", "#F5CB49", "#7599F5", "#64BFDB"],
|
||||
series: [
|
||||
{
|
||||
name: "司龄统计",
|
||||
type: "pie",
|
||||
radius: ["45%", "60%"],
|
||||
avoidLabelOverlap: false,
|
||||
animation: false,
|
||||
label: {
|
||||
normal: {
|
||||
formatter: "{d}%",
|
||||
padding: [0, -25],
|
||||
position: "outer" //文字显示在内部,如果在外边把这个去掉就好
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{ value: 40, name: "1-3年" },
|
||||
{ value: 35, name: "3-5年" },
|
||||
{ value: 20, name: "5-8年" },
|
||||
{ value: 5, name: "8年以上" }
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
// 配置项给实例对象
|
||||
myChart.setOption(option);
|
||||
return myChart;
|
||||
};
|
||||
|
||||
export default AgeStaticsPie;
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
const EducationStaticsRedar = (echarts: any) => {
|
||||
// 实例化对象
|
||||
const myChart = echarts.init(document.getElementById("educationStatics"));
|
||||
// 配置项
|
||||
const option = {
|
||||
tooltip: {
|
||||
show: true
|
||||
},
|
||||
radar: {
|
||||
// shape: 'circle',
|
||||
indicator: [
|
||||
{ name: "博士及以上", max: 800, axisLabel: { show: true } },
|
||||
{ name: "硕士", max: 800 },
|
||||
{ name: "本科", max: 800 },
|
||||
{ name: "大专", max: 800 },
|
||||
{ name: "大专一下", max: 800 }
|
||||
],
|
||||
splitArea: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "学历统计",
|
||||
type: "radar",
|
||||
data: [
|
||||
{
|
||||
value: [210, 400, 800, 230, 180],
|
||||
name: "学历统计"
|
||||
}
|
||||
],
|
||||
label: {
|
||||
show: false,
|
||||
formatter: function (params: any) {
|
||||
return params.value;
|
||||
}
|
||||
},
|
||||
itemStyle: { //此属性的颜色和下面areaStyle属性的颜色都设置成相同色即可实现
|
||||
color: "#4D95E9",
|
||||
borderColor: "#4D95E9"
|
||||
},
|
||||
areaStyle: {
|
||||
color: "rgba(77,149,233,0.26)"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
// 配置项给实例对象
|
||||
myChart.setOption(option);
|
||||
return myChart;
|
||||
};
|
||||
|
||||
export default EducationStaticsRedar;
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
const GenderStaticsPie = (echarts: any) => {
|
||||
// 实例化对象
|
||||
const myChart = echarts.init(document.getElementById("genderStatics"));
|
||||
// 配置项
|
||||
const option = {
|
||||
legend: {
|
||||
icon: "circle",
|
||||
bottom: "0%",
|
||||
left: "center",
|
||||
itemGap: 20,
|
||||
textStyle: {
|
||||
fontSize: 12,//字体大小
|
||||
color: "#787E95"//字体颜色
|
||||
}
|
||||
},
|
||||
color: ["#A6BEF3", "#205CE1"],
|
||||
grid: {
|
||||
bottom: "10%",
|
||||
containLabel: true
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "性别统计",
|
||||
type: "pie",
|
||||
startAngle:-180,
|
||||
radius: [40, 70],
|
||||
roseType: "radius",
|
||||
animation: false,
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{ value: 70, name: "男" },
|
||||
{ value: 30, name: "女" }
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
// 配置项给实例对象
|
||||
myChart.setOption(option);
|
||||
return myChart;
|
||||
};
|
||||
|
||||
export default GenderStaticsPie;
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
.structureCardWrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.top {
|
||||
display: flex;
|
||||
|
||||
& > span {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
|
||||
li:last-child {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
li.active {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
li {
|
||||
font-size: 12px;
|
||||
color: #D4D4D4;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
|
||||
& > div:first-child {
|
||||
& > span:first-child {
|
||||
font-size: 24px;
|
||||
letter-spacing: 0;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
& > span:last-child {
|
||||
font-size: 14px;
|
||||
color: #D4D4D4;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
& > div {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.charts {
|
||||
height: 62px;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top: 0.5px solid #e5e5e5;
|
||||
padding-top: 10px;
|
||||
|
||||
& > span {
|
||||
& > span:first-child {
|
||||
font-size: 12px;
|
||||
color: #D4D4D4;
|
||||
font-weight: 400;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
& > span:last-child {
|
||||
font-size: 12px;
|
||||
color: #333333;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.structureFrameWrapper {
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
padding: 16px;
|
||||
border-radius: 5px;
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: .5px solid #e5e5e5;
|
||||
padding-bottom: 14px;
|
||||
|
||||
img {
|
||||
width: 28px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
font-size: 18px;
|
||||
color: #333333;
|
||||
letter-spacing: 0;
|
||||
font-weight: 700;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.tabBox {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 20px 32px 0 0;
|
||||
|
||||
li:last-child {
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
li.active {
|
||||
color: #1E66F6;
|
||||
}
|
||||
|
||||
li {
|
||||
font-size: 14px;
|
||||
color: #999999;
|
||||
cursor: pointer;
|
||||
font-weight: 550;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
const InternStaticsBar = (echarts: any) => {
|
||||
// 实例化对象
|
||||
const myChart = echarts.init(document.getElementById("internStatics"));
|
||||
// 配置项
|
||||
const option = {
|
||||
xAxis: [
|
||||
{
|
||||
type: "value",
|
||||
axisLabel: {
|
||||
formatter: "{value} "
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
type: "dashed",
|
||||
color: "#1D3039"
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
lineStyle: {
|
||||
fontSize: 12,
|
||||
color: "#D4D4D4"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
grid: {
|
||||
top: "10%",
|
||||
left: "5%",
|
||||
right: "5%",
|
||||
bottom: "3%",
|
||||
containLabel: true
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: "category",
|
||||
offset: 0,
|
||||
data: ["项目实习生", "销售实习生", "技术实习生", "大区实习生", "客服实习生"],
|
||||
axisPointer: {
|
||||
type: "shadow"
|
||||
},
|
||||
axisLine: {
|
||||
//这是x轴文字颜色
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
color: "#787E95",
|
||||
fontSize: 10
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
data: [34, 19, 15, 32, 11],
|
||||
type: "bar",
|
||||
barWidth: "25%",
|
||||
realtimeSort: true,
|
||||
yAxisIndex: 0,
|
||||
itemStyle: {
|
||||
color: "#4CAEFF" //设定单个柱子颜色
|
||||
},
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
position: "right",
|
||||
fontWeight: "bold",
|
||||
fontSize: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
// 配置项给实例对象
|
||||
myChart.setOption(option);
|
||||
return myChart;
|
||||
};
|
||||
|
||||
export default InternStaticsBar;
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
const LineStaticsPie = (echarts: any) => {
|
||||
// 实例化对象
|
||||
const myChart = echarts.init(document.getElementById("lineStatics"));
|
||||
// 配置项
|
||||
const option = {
|
||||
legend: {
|
||||
icon: "circle",
|
||||
bottom: "0%",
|
||||
left: "center",
|
||||
itemGap: 20,
|
||||
textStyle: {
|
||||
fontSize: 12,//字体大小
|
||||
color: "#787E95"//字体颜色
|
||||
}
|
||||
},
|
||||
color: ["#61D485", "#F4CB48", "#7499F5", "#65BFDC", "#ED9C5F"],
|
||||
series: [
|
||||
{
|
||||
name: "司龄统计",
|
||||
type: "pie",
|
||||
radius: ["45%", "60%"],
|
||||
center: ['50%', '41%'],
|
||||
avoidLabelOverlap: false,
|
||||
animation: false,
|
||||
label: {
|
||||
normal: {
|
||||
formatter: "{d}%",
|
||||
padding: [0, -25],
|
||||
position: "outer" //文字显示在内部,如果在外边把这个去掉就好
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: [
|
||||
{ value: 40, name: "项目" },
|
||||
{ value: 15, name: "市场" },
|
||||
{ value: 20, name: "销售" },
|
||||
{ value: 15, name: "支撑" },
|
||||
{ value: 10, name: "销售1" }
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
// 配置项给实例对象
|
||||
myChart.setOption(option);
|
||||
return myChart;
|
||||
};
|
||||
|
||||
export default LineStaticsPie;
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
const MonthlyTrendsBar = (echarts: any) => {
|
||||
// 实例化对象
|
||||
const myChart = echarts.init(document.getElementById("monthlyTrendsCharts"));
|
||||
// 配置项
|
||||
const option = {
|
||||
tooltip: {
|
||||
// 坐标轴指示器,坐标轴触发有效
|
||||
trigger: "axis",
|
||||
axisPointer: {
|
||||
// 默认为直线,可选为:'line' | 'shadow'
|
||||
type: "shadow"
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: "10%",
|
||||
left: "2%",
|
||||
right: "2%",
|
||||
bottom: "3%",
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: "category",
|
||||
data: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
|
||||
axisTick: {
|
||||
alignWithLabel: true,
|
||||
show: false
|
||||
},
|
||||
// 修改坐标值样式
|
||||
axisLabel: {
|
||||
color: "#B8B8B8",
|
||||
fontSize: 16,
|
||||
show: true
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: "value",
|
||||
name: "人",
|
||||
// 修改坐标值样式
|
||||
axisLabel: {
|
||||
color: "#787E95",
|
||||
fontSize: 14
|
||||
// formatter: '{value} 人'
|
||||
},
|
||||
nameTextStyle: {
|
||||
color: "#787E95",
|
||||
fontSize: 16
|
||||
},
|
||||
// 修改坐标轴线样式
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: "transparent"
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 不显示坐标轴刻度线
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
type: "dashed",
|
||||
color: "rgba(93,126,158,1)"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: "入职",
|
||||
type: "bar",
|
||||
showBackground: false,
|
||||
barWidth: "8%",
|
||||
barGap: "100%",
|
||||
data: [250, 250, 320, 410, 280, 320, 405, 405, 320, 250, 280, 405],
|
||||
// bar 样式修改
|
||||
itemStyle: {
|
||||
barBorderRadius: 5,
|
||||
color: "#0FC482"
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "离职",
|
||||
type: "bar",
|
||||
showBackground: false,
|
||||
barWidth: "8%",
|
||||
data: [370, 370, 260, 110, 390, 280, 110, 110, 370, 260, 390, 110],
|
||||
// bar 样式修改
|
||||
itemStyle: {
|
||||
barBorderRadius: 5,
|
||||
color: "#FFA500"
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "在职",
|
||||
type: "bar",
|
||||
showBackground: false,
|
||||
barWidth: "8%",
|
||||
data: [310, 310, 80, 280, 310, 70, 280, 280, 80, 280, 310, 280],
|
||||
// bar 样式修改
|
||||
itemStyle: {
|
||||
barBorderRadius: 5,
|
||||
color: "#FFD600"
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "离职",
|
||||
type: "line",
|
||||
yAxisIndex: 0,
|
||||
data: [370, 370, 260, 110, 390, 280, 110, 110, 370, 260, 390, 110],
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: "#FFD700",
|
||||
lineStyle: {
|
||||
color: "#FFD700"
|
||||
}
|
||||
}
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: {//分隔区域的颜色
|
||||
x0: 0,
|
||||
y0: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [{
|
||||
offset: 0,
|
||||
color: "#FFD700" // 0% 处的颜色
|
||||
}, {
|
||||
offset: 1,
|
||||
color: "rgba(255,215,0,0)" // 100% 处的颜色;中间还可以设置50%、20%、70%时的颜色
|
||||
}],
|
||||
globalCoord: false // 缺省为 false,以确保上面的x,y,x2,y2表示的是百分比
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
// 配置项给实例对象
|
||||
myChart.setOption(option);
|
||||
return myChart;
|
||||
};
|
||||
|
||||
export default MonthlyTrendsBar;
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
const RankingBar = (echarts: any) => {
|
||||
// 实例化对象
|
||||
const colorList = ["#1E66F6", "#70CDFF", "#00C48B", "#FFD700", "#1E66F6", "#70CDFF", "#00C48B", "#FFD700"];
|
||||
const myChart = echarts.init(document.getElementById("rankingCharts"));
|
||||
// 配置项
|
||||
const option = {
|
||||
xAxis: [
|
||||
{
|
||||
type: "value",
|
||||
axisLabel: {
|
||||
formatter: "{value} "
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
type: "dashed",
|
||||
color: "#1D3039"
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
show: false,
|
||||
lineStyle: {
|
||||
fontSize: 12,
|
||||
color: "#D4D4D4"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
grid: {
|
||||
top: "10%",
|
||||
left: "5%",
|
||||
right: "5%",
|
||||
bottom: "3%",
|
||||
containLabel: true
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: "category",
|
||||
offset: 0,
|
||||
inverse: true, //升序
|
||||
// axisLabel: {
|
||||
// color: "#787E95",
|
||||
// fontSize: 16
|
||||
// },
|
||||
data: ["契约锁", "泛微北京", "泛微上海", "产品研发中心", "泛微华南一部", "泛微广州一部", "泛微杭州", "EBU事业一部"],
|
||||
axisPointer: {
|
||||
type: "shadow"
|
||||
},
|
||||
axisLine: {
|
||||
//这是x轴文字颜色
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
color: "#787E95",
|
||||
fontSize: 14,
|
||||
formatter: function (value: any, index: number) {
|
||||
if (index == 0) {
|
||||
return "{one|" + "} {a| " + value + "}";
|
||||
} else if (index == 1) {
|
||||
return "{two|" + "} {a|" + value + "}";
|
||||
} else if (index == 2) {
|
||||
return "{three|" + "} {a|" + value + "}";
|
||||
}
|
||||
return value;
|
||||
},
|
||||
rich: {
|
||||
a: {
|
||||
color: "#787E95",
|
||||
fontSize: 14
|
||||
},
|
||||
// 第一名
|
||||
one: {
|
||||
width: 40,
|
||||
height: 20,
|
||||
margin: 10,
|
||||
align: "center",
|
||||
backgroundColor: {
|
||||
image: require("../../../../assets/images/one.png")
|
||||
}
|
||||
},
|
||||
two: {
|
||||
width: 40,
|
||||
height: 20,
|
||||
margin: 10,
|
||||
align: "center",
|
||||
backgroundColor: {
|
||||
image: require("../../../../assets/images/two.png")
|
||||
}
|
||||
},
|
||||
three: {
|
||||
width: 40,
|
||||
height: 20,
|
||||
marginRight: 20,
|
||||
align: "center",
|
||||
backgroundColor: {
|
||||
image: require("../../../../assets/images/three.png")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
data: [4096, 1412, 1337, 788, 788, 347, 332, 291],
|
||||
inverse: true,//数组翻转显示
|
||||
axisTick: {
|
||||
alignWithLabel: true,
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
show: false//不显示y轴的线
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
fontSize: 16,
|
||||
color: function (params: any, index: any) {
|
||||
return colorList[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
data: _.map([4096, 1412, 1337, 788, 788, 347, 332, 291], (it, idx) => ({
|
||||
value: it,
|
||||
itemStyle: {
|
||||
color: colorList[idx], //设定单个柱子颜色
|
||||
borderColor: colorList[idx], //设定单个柱子边框颜色
|
||||
barBorderRadius: [10, 10, 10, 10]
|
||||
}
|
||||
})),
|
||||
type: "bar",
|
||||
barWidth: "20%",
|
||||
realtimeSort: true,
|
||||
yAxisIndex: 0,
|
||||
label: {
|
||||
normal: {
|
||||
show: false,
|
||||
position: "right",
|
||||
fontWeight: "bold",
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
showBackground: true,
|
||||
backgroundStyle: {
|
||||
color: "rgba(220, 220, 220, 0.8)",
|
||||
barBorderRadius: [10, 10, 10, 10]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
type: "bar",
|
||||
yAxisIndex: 1,//使两个柱状图重合的效果
|
||||
barWidth: "25%",
|
||||
data: _.map([5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000], (it, idx) => ({
|
||||
value: it,
|
||||
itemStyle: {
|
||||
color: "none",
|
||||
borderColor: colorList[idx],
|
||||
borderWidth: 2,
|
||||
barBorderRadius: [10, 10, 10, 10]
|
||||
}
|
||||
})),
|
||||
label: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
// 配置项给实例对象
|
||||
myChart.setOption(option);
|
||||
return myChart;
|
||||
};
|
||||
|
||||
export default RankingBar;
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Author: 黎永顺
|
||||
* name: 卡片组件
|
||||
* Description:
|
||||
* Date: 2023/5/5
|
||||
*/
|
||||
import React, { FC } from "react";
|
||||
import { TinyArea } from "@ant-design/plots";
|
||||
import styles from "./index.less";
|
||||
import cs from "classnames";
|
||||
|
||||
interface OwnProps {
|
||||
title: string;
|
||||
active: number;
|
||||
number: string;
|
||||
unit: string;
|
||||
color: string;
|
||||
tabList: Array<any>;
|
||||
}
|
||||
|
||||
type Props = OwnProps;
|
||||
const config = {
|
||||
height: 60,
|
||||
autoFit: false,
|
||||
data: [264, 417, 438, 887, 309, 397, 550],
|
||||
smooth: true,
|
||||
areaStyle: {
|
||||
fill: "#d6e3fd"
|
||||
},
|
||||
tooltip: {
|
||||
showCrosshairs: false,
|
||||
showContent: false
|
||||
}
|
||||
};
|
||||
const StructureCard: FC<Props> = (props) => {
|
||||
const { active, tabList, title, number, unit, color } = props;
|
||||
|
||||
return (
|
||||
<div className={styles.structureCardWrapper}>
|
||||
<div className={styles.top}>
|
||||
<span>{title}</span>
|
||||
<ul>
|
||||
{
|
||||
_.map(tabList, (it, idx) => {
|
||||
const classes = cs({
|
||||
[styles["active"]]: idx === active
|
||||
});
|
||||
return <li key={it} className={classes}>{it}</li>;
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<div className={styles.center}>
|
||||
<div><span style={{ color }}>{number}</span><span>{unit}</span></div>
|
||||
<div className={styles.charts}><TinyArea {..._.assign(config, {
|
||||
areaStyle: { fill: `l(270) 0:#fff 0.5:${color} 1:${color}` },
|
||||
line: { color }
|
||||
})} /></div>
|
||||
</div>
|
||||
<div className={styles.bottom}>
|
||||
<span><span>同比:</span><span>4.75%</span></span>
|
||||
<span><span>环比:</span><span>1.75%</span></span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StructureCard;
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Author: 黎永顺
|
||||
* name: 人才结构表框组件
|
||||
* Description:
|
||||
* Date: 2023/5/5
|
||||
*/
|
||||
import React, { FC, useState } from "react";
|
||||
import styles from "./index.less";
|
||||
import cs from "classnames";
|
||||
|
||||
interface OwnProps {
|
||||
title: string;
|
||||
children: any;
|
||||
tabList?: Array<any>;
|
||||
}
|
||||
|
||||
type Props = OwnProps;
|
||||
|
||||
const StructureFrame: FC<Props> = (props) => {
|
||||
const { children, tabList, title } = props;
|
||||
const [active, setActive] = useState<number>(0);
|
||||
|
||||
return (
|
||||
<div className={styles.structureFrameWrapper}>
|
||||
<div className={styles.header}>
|
||||
<img src={require("../../../../assets/images/frame_icon.png")} alt=""/>
|
||||
<span>{title}</span>
|
||||
</div>
|
||||
{
|
||||
!_.isEmpty(tabList) &&
|
||||
<ul className={styles.tabBox}>
|
||||
{
|
||||
_.map(tabList, (it, idx) => {
|
||||
const classes = cs({
|
||||
[styles["active"]]: idx === active
|
||||
});
|
||||
return <li key={it} className={classes} onClick={() => setActive(idx)}>{it}</li>;
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StructureFrame
|
||||
;
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
.structureWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #e5e5e5;
|
||||
overflow: auto;
|
||||
|
||||
.structureColumn_1 {
|
||||
display: flex;
|
||||
margin: 0 0 10px;
|
||||
padding: 0;
|
||||
|
||||
& > li:not(:last-child) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
& > li {
|
||||
flex: 1;
|
||||
border-radius: 5px;
|
||||
background: #FFF;
|
||||
max-height: 160px;
|
||||
min-height: 100px;
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.structureColumn_2 {
|
||||
margin-bottom: 10px;
|
||||
|
||||
.multipleCharts {
|
||||
height: 330px;
|
||||
display: flex;
|
||||
|
||||
& > div:not(:last-child) {
|
||||
div {
|
||||
border-right: .5px solid #e5e5e5;
|
||||
}
|
||||
}
|
||||
|
||||
& > div {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
font-weight: 700;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
div {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.structureColumn_3 {
|
||||
display: flex;
|
||||
|
||||
& > div:first-child {
|
||||
flex: 4 1;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
& > div:last-child {
|
||||
flex: 2 1;
|
||||
}
|
||||
|
||||
.monthlyTrendsChartsBox {
|
||||
width: 100%;
|
||||
min-height: 438px;
|
||||
max-height: 438px;
|
||||
}
|
||||
|
||||
.rankingChartsBox {
|
||||
width: 100%;
|
||||
min-height: 480px;
|
||||
max-height: 480px;
|
||||
height: 480px;
|
||||
|
||||
:global {
|
||||
.dv-capsule-chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.capsule-container {
|
||||
.unit-label {
|
||||
font-size: 12px;
|
||||
color: #D4D4D4;
|
||||
text-align: right;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.label-column {
|
||||
font-size: 16px;
|
||||
color: #787E95;
|
||||
text-align: right;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Author: 黎永顺
|
||||
* name: 人才结构表
|
||||
* Description:
|
||||
* Date: 2023/4/27
|
||||
*/
|
||||
import React, { FC, useEffect, useState } from "react";
|
||||
// @ts-ignore
|
||||
import echarts from "echarts";
|
||||
// @ts-ignore
|
||||
import { CapsuleChart } from "@jiaminghi/data-view-react";
|
||||
import StructureCard from "./components/structureCard";
|
||||
import StructureFrame from "./components/structureFrame";
|
||||
import MonthlyTrendsBar from "./components/monthlyTrendsCharts";
|
||||
import RankingBar from "./components/rankingCharts";
|
||||
import AgeStaticsPie from "./components/ageStaticsCharts";
|
||||
import GenderStaticsPie from "./components/genderStaticsCharts";
|
||||
import LineStaticsPie from "./components/lineStaticsCharts";
|
||||
import InternStaticsBar from "./components/internStaticsCharts";
|
||||
import EducationStaticsRedar from "./components/educationStaticsCharts";
|
||||
import { structureCardList } from "../constants";
|
||||
import styles from "./index.less";
|
||||
|
||||
interface OwnProps {
|
||||
}
|
||||
|
||||
type Props = OwnProps;
|
||||
|
||||
const index: FC<Props> = (props) => {
|
||||
const [card, setCard] = useState<any>(structureCardList);
|
||||
useEffect(() => {
|
||||
const monthlyTrendsBar = MonthlyTrendsBar(echarts);
|
||||
const rankingBar = RankingBar(echarts);
|
||||
const ageStaticsPie = AgeStaticsPie(echarts);
|
||||
const genderStaticsPie = GenderStaticsPie(echarts);
|
||||
const lineStaticsPie = LineStaticsPie(echarts);
|
||||
const internStaticsBar = InternStaticsBar(echarts);
|
||||
const educationStaticsRedar = EducationStaticsRedar(echarts);
|
||||
// 屏幕缩放对chart图表进行自适应处理,调用实例的resize方法
|
||||
window.onresize = () => {
|
||||
monthlyTrendsBar.resize();
|
||||
rankingBar.resize();
|
||||
ageStaticsPie.resize();
|
||||
genderStaticsPie.resize();
|
||||
lineStaticsPie.resize();
|
||||
internStaticsBar.resize();
|
||||
educationStaticsRedar.resize();
|
||||
};
|
||||
return () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.structureWrapper}>
|
||||
<ul className={styles.structureColumn_1}>
|
||||
{
|
||||
_.map(card, (it, idx) => {
|
||||
return <li key={idx}><StructureCard {...it}/></li>;
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
<div className={styles.structureColumn_2}>
|
||||
<StructureFrame title="人员结构统计">
|
||||
<div className={styles.multipleCharts}>
|
||||
<div><span>司龄统计</span>
|
||||
<div id="ageStatics"/>
|
||||
</div>
|
||||
<div><span>性别统计</span>
|
||||
<div id="genderStatics"/>
|
||||
</div>
|
||||
<div><span>学历统计</span>
|
||||
<div id="educationStatics"/>
|
||||
</div>
|
||||
<div><span>条线统计</span>
|
||||
<div id="lineStatics"/>
|
||||
</div>
|
||||
<div><span>实习生统计</span>
|
||||
<div id="internStatics"/>
|
||||
</div>
|
||||
</div>
|
||||
</StructureFrame>
|
||||
</div>
|
||||
<div className={styles.structureColumn_3}>
|
||||
<div>
|
||||
<StructureFrame title="人员流动情况月度趋势" tabList={["今年", "去年"]}>
|
||||
<div className={styles.monthlyTrendsChartsBox} id="monthlyTrendsCharts"/>
|
||||
</StructureFrame>
|
||||
</div>
|
||||
<div>
|
||||
<StructureFrame title="机构人员排名数">
|
||||
<div className={styles.rankingChartsBox} id="rankingCharts"/>
|
||||
</StructureFrame>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default index;
|
||||
|
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
* ECharts 组件基础部分
|
||||
* 传入 option 和渲染方式 renderer
|
||||
* */
|
||||
|
||||
import React, { PureComponent } from "react";
|
||||
import * as echarts from "echarts";
|
||||
import "zrender/lib/svg/svg";
|
||||
import { debounce } from "./common"; // 一个节流函数
|
||||
|
||||
export default class Chart extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
width: "100%",
|
||||
height: "100%"
|
||||
};
|
||||
this.chart = null;
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
// 初始化图表
|
||||
await this.initChart(this.el);
|
||||
// 将传入的配置(包含数据)注入
|
||||
this.setOption(this.props.option);
|
||||
// 监听屏幕缩放,重新绘制 echart 图表
|
||||
window.addEventListener("resize", debounce(this.resize, 100));
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
// 每次更新组件都重置
|
||||
this.setOption(this.props.option);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// 组件卸载前卸载图表
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { width, height } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="default-chart"
|
||||
ref={el => (this.el = el)}
|
||||
style={{ width, height }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
initChart = el => {
|
||||
// renderer 用于配置渲染方式 可以是 svg 或者 canvas
|
||||
const renderer = this.props.renderer || "canvas";
|
||||
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
this.chart = echarts.init(el, null, {
|
||||
renderer,
|
||||
width: "auto",
|
||||
height: "auto"
|
||||
});
|
||||
resolve();
|
||||
}, 0);
|
||||
});
|
||||
};
|
||||
setOption = option => {
|
||||
if (!this.chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
const notMerge = this.props.notMerge;
|
||||
const lazyUpdate = this.props.lazyUpdate;
|
||||
|
||||
this.chart.setOption(option, notMerge, lazyUpdate);
|
||||
};
|
||||
dispose = () => {
|
||||
if (!this.chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.chart.dispose();
|
||||
this.chart = null;
|
||||
};
|
||||
resize = () => {
|
||||
this.chart && this.chart.resize();
|
||||
};
|
||||
getInstance = () => {
|
||||
return this.chart;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制3d图
|
||||
* @param pieData 总数据
|
||||
* @param internalDiameterRatio:透明的空心占比
|
||||
* @param distance 视角到主体的距离
|
||||
* @param alpha 旋转角度
|
||||
* @param pieHeight 立体的高度
|
||||
* @param opacity 饼或者环的透明度
|
||||
* @param isSameHeight 饼或者环的透明度
|
||||
*/
|
||||
const getPie3D = (
|
||||
pieData,
|
||||
internalDiameterRatio,
|
||||
distance,
|
||||
alpha,
|
||||
pieHeight,
|
||||
opacity = 1,
|
||||
isSameHeight
|
||||
) => {
|
||||
const series = [];
|
||||
let sumValue = 0;
|
||||
let startValue = 0;
|
||||
let endValue = 0;
|
||||
let legendData = [];
|
||||
let legendBfb = [];
|
||||
const k = 1 - internalDiameterRatio;
|
||||
pieData.sort((a, b) => {
|
||||
return b.value - a.value;
|
||||
});
|
||||
// 为每一个饼图数据,生成一个 series-surface 配置
|
||||
for (let i = 0; i < pieData.length; i++) {
|
||||
sumValue += pieData[i].value;
|
||||
const seriesItem = {
|
||||
name:
|
||||
typeof pieData[i].name === "undefined" ? `series${i}` : pieData[i].name,
|
||||
type: "surface",
|
||||
parametric: true,
|
||||
wireframe: {
|
||||
show: false
|
||||
},
|
||||
pieData: pieData[i],
|
||||
pieStatus: {
|
||||
selected: false,
|
||||
hovered: false,
|
||||
k: k
|
||||
},
|
||||
center: ["10%", "50%"]
|
||||
};
|
||||
if (typeof pieData[i].itemStyle !== "undefined") {
|
||||
const itemStyle = {};
|
||||
itemStyle.color =
|
||||
typeof pieData[i].itemStyle.color !== "undefined"
|
||||
? pieData[i].itemStyle.color
|
||||
: opacity;
|
||||
itemStyle.opacity =
|
||||
typeof pieData[i].itemStyle.opacity !== "undefined"
|
||||
? pieData[i].itemStyle.opacity
|
||||
: opacity;
|
||||
seriesItem.itemStyle = itemStyle;
|
||||
}
|
||||
series.push(seriesItem);
|
||||
}
|
||||
|
||||
// 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,
|
||||
// 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。
|
||||
legendData = [];
|
||||
legendBfb = [];
|
||||
for (let i = 0; i < series.length; i++) {
|
||||
endValue = startValue + series[i].pieData.value;
|
||||
series[i].pieData.startRatio = startValue / sumValue;
|
||||
series[i].pieData.endRatio = endValue / sumValue;
|
||||
series[i].parametricEquation = getParametricEquation(
|
||||
series[i].pieData.startRatio,
|
||||
series[i].pieData.endRatio,
|
||||
false,
|
||||
false,
|
||||
k,
|
||||
!isSameHeight && series[i].pieData.value
|
||||
);
|
||||
startValue = endValue;
|
||||
const bfb = fomatFloat(series[i].pieData.value / sumValue, 4);
|
||||
legendData.push({
|
||||
name: series[i].name,
|
||||
value: bfb
|
||||
});
|
||||
legendBfb.push({
|
||||
name: series[i].name,
|
||||
value: bfb
|
||||
});
|
||||
}
|
||||
const boxHeight = getHeight3D(series, pieHeight); // 通过pieHeight设定3d饼/环的高度,单位是px
|
||||
// 准备待返回的配置项,把准备好的 legendData、series 传入。
|
||||
const option = {
|
||||
legend: {
|
||||
show: false,
|
||||
data: legendData,
|
||||
orient: "vertical",
|
||||
left: 10,
|
||||
top: 10,
|
||||
itemGap: 10,
|
||||
textStyle: {
|
||||
color: "#A1E2FF"
|
||||
},
|
||||
icon: "circle",
|
||||
formatter: function (param) {
|
||||
const item = legendBfb.filter((item) => item.name === param)[0];
|
||||
const bfs = fomatFloat(item.value * 100, 2) + "%";
|
||||
return `${item.name} ${bfs}`;
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: "#fff"
|
||||
}
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: "outside",
|
||||
formatter: "{b} \n{c} {d}%"
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: "#033b77",
|
||||
borderColor: "#21f2c4",
|
||||
textStyle: {
|
||||
color: "#fff",
|
||||
fontSize: 13
|
||||
},
|
||||
formatter: (params) => {
|
||||
if (
|
||||
params.seriesName !== "mouseoutSeries" &&
|
||||
params.seriesName !== "信用评价"
|
||||
) {
|
||||
// console.log(option.series, params.seriesName, "option.series[params.seriesIndex].pieData");
|
||||
const bfb = (
|
||||
(option.series[params.seriesIndex].pieData.endRatio -
|
||||
option.series[params.seriesIndex].pieData.startRatio) *
|
||||
100
|
||||
).toFixed(2);
|
||||
return (
|
||||
// ${params.seriesName}
|
||||
`<span style="display:inline-block; width: 100%;text-align: center; color: #fff">数量</span><br/>` +
|
||||
`<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>` +
|
||||
`<span style="color: #3EA1FF">${option.series[params.seriesIndex].pieData.value}(${bfb}%)</span>`
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
xAxis3D: {
|
||||
min: -1,
|
||||
max: 1
|
||||
},
|
||||
yAxis3D: {
|
||||
min: -1,
|
||||
max: 1
|
||||
},
|
||||
zAxis3D: {
|
||||
min: -1,
|
||||
max: 1
|
||||
},
|
||||
grid3D: {
|
||||
show: false,
|
||||
left: "18%",
|
||||
boxHeight: !isSameHeight ? boxHeight : 15, // 圆环的高度
|
||||
viewControl: {
|
||||
// 3d效果可以放大、旋转等,请自己去查看官方配置
|
||||
alpha, // 角度
|
||||
distance, // 调整视角到主体的距离,类似调整zoom
|
||||
rotateSensitivity: 0, // 设置为0无法旋转
|
||||
zoomSensitivity: 0, // 设置为0无法缩放
|
||||
panSensitivity: 0, // 设置为0无法平移
|
||||
autoRotate: false // 自动旋转
|
||||
}
|
||||
},
|
||||
series: series
|
||||
};
|
||||
return option;
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成扇形的曲面参数方程,用于 series-surface.parametricEquation
|
||||
*/
|
||||
const getParametricEquation = (
|
||||
startRatio,
|
||||
endRatio,
|
||||
isSelected,
|
||||
isHovered,
|
||||
k,
|
||||
h = 1
|
||||
) => {
|
||||
// 计算
|
||||
const midRatio = (startRatio + endRatio) / 2;
|
||||
const startRadian = startRatio * Math.PI * 2;
|
||||
const endRadian = endRatio * Math.PI * 2;
|
||||
const midRadian = midRatio * Math.PI * 2;
|
||||
// 如果只有一个扇形,则不实现选中效果。
|
||||
if (startRatio === 0 && endRatio === 1) {
|
||||
isSelected = false;
|
||||
}
|
||||
// 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
|
||||
k = typeof k !== "undefined" ? k : 1 / 3;
|
||||
// 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
|
||||
const offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0;
|
||||
const offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0;
|
||||
// 计算高亮效果的放大比例(未高亮,则比例为 1)
|
||||
const hoverRate = isHovered ? 1.05 : 1;
|
||||
// 返回曲面参数方程
|
||||
return {
|
||||
u: {
|
||||
min: -Math.PI,
|
||||
max: Math.PI * 3,
|
||||
step: Math.PI / 32
|
||||
},
|
||||
v: {
|
||||
min: 0,
|
||||
max: Math.PI * 2,
|
||||
step: Math.PI / 20
|
||||
},
|
||||
x: function (u, v) {
|
||||
if (u < startRadian) {
|
||||
return (
|
||||
offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate
|
||||
);
|
||||
}
|
||||
if (u > endRadian) {
|
||||
return (
|
||||
offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate
|
||||
);
|
||||
}
|
||||
return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;
|
||||
},
|
||||
y: function (u, v) {
|
||||
if (u < startRadian) {
|
||||
return (
|
||||
offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate
|
||||
);
|
||||
}
|
||||
if (u > endRadian) {
|
||||
return (
|
||||
offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate
|
||||
);
|
||||
}
|
||||
return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;
|
||||
},
|
||||
z: function (u, v) {
|
||||
if (u < -Math.PI * 0.5) {
|
||||
return Math.sin(u);
|
||||
}
|
||||
if (u > Math.PI * 2.5) {
|
||||
return Math.sin(u) * h * 0.1;
|
||||
}
|
||||
return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取3d丙图的最高扇区的高度
|
||||
*/
|
||||
const getHeight3D = (series, height) => {
|
||||
series.sort((a, b) => {
|
||||
return b.pieData.value - a.pieData.value;
|
||||
});
|
||||
return (height * 25) / series[0].pieData.value;
|
||||
};
|
||||
|
||||
/**
|
||||
* 格式化浮点数
|
||||
*/
|
||||
const fomatFloat = (num, n) => {
|
||||
let f = parseFloat(num);
|
||||
if (isNaN(f)) {
|
||||
return false;
|
||||
}
|
||||
f = Math.round(num * Math.pow(10, n)) / Math.pow(10, n); // n 幂
|
||||
let s = f.toString();
|
||||
let rs = s.indexOf(".");
|
||||
// 判定如果是整数,增加小数点再补0
|
||||
if (rs < 0) {
|
||||
rs = s.length;
|
||||
s += ".";
|
||||
}
|
||||
while (s.length <= rs + n) {
|
||||
s += "0";
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
export { getPie3D, getParametricEquation };
|
||||
|
|
@ -60,6 +60,75 @@ export const exceptStr = (str) => {
|
|||
return {};
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @param {date} time 需要转换的时间
|
||||
* @param {String} fmt 需要转换的格式 如 yyyy-MM-dd、yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
export const formatTime = (time, fmt) => {
|
||||
if (!time) return "";
|
||||
else {
|
||||
const date = new Date(time);
|
||||
const o = {
|
||||
"M+": date.getMonth() + 1,
|
||||
"d+": date.getDate(),
|
||||
"H+": date.getHours(),
|
||||
"m+": date.getMinutes(),
|
||||
"s+": date.getSeconds(),
|
||||
"q+": Math.floor((date.getMonth() + 3) / 3),
|
||||
S: date.getMilliseconds()
|
||||
};
|
||||
if (/(y+)/.test(fmt))
|
||||
fmt = fmt.replace(
|
||||
RegExp.$1,
|
||||
(date.getFullYear() + "").substr(4 - RegExp.$1.length)
|
||||
);
|
||||
for (const k in o) {
|
||||
if (new RegExp("(" + k + ")").test(fmt)) {
|
||||
fmt = fmt.replace(
|
||||
RegExp.$1,
|
||||
RegExp.$1.length === 1
|
||||
? o[k]
|
||||
: ("00" + o[k]).substr(("" + o[k]).length)
|
||||
);
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
};
|
||||
export const debounce = (func, wait, immediate) => {
|
||||
let timeout, args, context, timestamp, result;
|
||||
|
||||
const later = function () {
|
||||
// 据上一次触发时间间隔
|
||||
const last = +new Date() - timestamp;
|
||||
|
||||
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
|
||||
if (last < wait && last > 0) {
|
||||
timeout = setTimeout(later, wait - last);
|
||||
} else {
|
||||
timeout = null;
|
||||
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
|
||||
if (!immediate) {
|
||||
result = func.apply(context, args);
|
||||
if (!timeout) context = args = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return function (...args) {
|
||||
context = this;
|
||||
timestamp = +new Date();
|
||||
const callNow = immediate && !timeout;
|
||||
// 如果延时不存在,重新设定延时
|
||||
if (!timeout) timeout = setTimeout(later, wait);
|
||||
if (callNow) {
|
||||
result = func.apply(context, args);
|
||||
context = args = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
||||
export const paginationFun = (tableListPageObj, sizeChange, onChange) => {
|
||||
return {
|
||||
|
|
@ -76,8 +145,8 @@ export const paginationFun = (tableListPageObj, sizeChange, onChange) => {
|
|||
onChange({
|
||||
pageNum: page,
|
||||
size,
|
||||
total,
|
||||
total
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,147 @@
|
|||
(function(win, lib) {
|
||||
const doc = win.document;
|
||||
const docEl = doc.documentElement;
|
||||
let metaEl = doc.querySelector("meta[name=\"viewport\"]");
|
||||
const flexibleEl = doc.querySelector("meta[name=\"flexible\"]");
|
||||
let dpr = 0;
|
||||
let scale = 0;
|
||||
let tid;
|
||||
const flexible = lib.flexible || (lib.flexible = {});
|
||||
|
||||
if (metaEl) {
|
||||
console.warn("将根据已有的meta标签来设置缩放比例");
|
||||
const match = metaEl
|
||||
.getAttribute("content")
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
.match(/initial\-scale=([\d\.]+)/);
|
||||
if (match) {
|
||||
scale = parseFloat(match[1]);
|
||||
dpr = parseInt(1 / scale);
|
||||
}
|
||||
} else if (flexibleEl) {
|
||||
const content = flexibleEl.getAttribute("content");
|
||||
if (content) {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
|
||||
if (initialDpr) {
|
||||
dpr = parseFloat(initialDpr[1]);
|
||||
scale = parseFloat((1 / dpr).toFixed(2));
|
||||
}
|
||||
if (maximumDpr) {
|
||||
dpr = parseFloat(maximumDpr[1]);
|
||||
scale = parseFloat((1 / dpr).toFixed(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dpr && !scale) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const isAndroid = win.navigator.appVersion.match(/android/gi);
|
||||
const isIPhone = win.navigator.appVersion.match(/iphone/gi);
|
||||
const devicePixelRatio = win.devicePixelRatio;
|
||||
if (isIPhone) {
|
||||
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
|
||||
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
|
||||
dpr = 3;
|
||||
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) {
|
||||
dpr = 2;
|
||||
} else {
|
||||
dpr = 1;
|
||||
}
|
||||
} else {
|
||||
// 其他设备下,仍旧使用1倍的方案
|
||||
dpr = 1;
|
||||
}
|
||||
scale = 1 / dpr;
|
||||
}
|
||||
|
||||
docEl.setAttribute("data-dpr", dpr);
|
||||
if (!metaEl) {
|
||||
metaEl = doc.createElement("meta");
|
||||
metaEl.setAttribute("name", "viewport");
|
||||
metaEl.setAttribute(
|
||||
"content",
|
||||
"initial-scale=" +
|
||||
scale +
|
||||
", maximum-scale=" +
|
||||
scale +
|
||||
", minimum-scale=" +
|
||||
scale +
|
||||
", user-scalable=no"
|
||||
);
|
||||
if (docEl.firstElementChild) {
|
||||
docEl.firstElementChild.appendChild(metaEl);
|
||||
} else {
|
||||
const wrap = doc.createElement("div");
|
||||
wrap.appendChild(metaEl);
|
||||
doc.write(wrap.innerHTML);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshRem() {
|
||||
let width = docEl.getBoundingClientRect().width;
|
||||
// 最小1366px,最大适配2560px
|
||||
if (width / dpr < 1366) {
|
||||
width = 1366 * dpr;
|
||||
} else if (width / dpr > 2560) {
|
||||
width = 2560 * dpr;
|
||||
}
|
||||
// 设置成24等份,设计稿时1920px的,这样1rem就是80px
|
||||
const rem = width / 24;
|
||||
docEl.style.fontSize = rem + "px";
|
||||
flexible.rem = win.rem = rem;
|
||||
}
|
||||
|
||||
win.addEventListener(
|
||||
"resize",
|
||||
function() {
|
||||
clearTimeout(tid);
|
||||
tid = setTimeout(refreshRem, 300);
|
||||
},
|
||||
false
|
||||
);
|
||||
win.addEventListener(
|
||||
"pageshow",
|
||||
function(e) {
|
||||
if (e.persisted) {
|
||||
clearTimeout(tid);
|
||||
tid = setTimeout(refreshRem, 300);
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
if (doc.readyState === "complete") {
|
||||
doc.body.style.fontSize = 12 * dpr + "px";
|
||||
} else {
|
||||
doc.addEventListener(
|
||||
"DOMContentLoaded",
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function(e) {
|
||||
doc.body.style.fontSize = 12 * dpr + "px";
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
refreshRem();
|
||||
|
||||
flexible.dpr = win.dpr = dpr;
|
||||
flexible.refreshRem = refreshRem;
|
||||
flexible.rem2px = function(d) {
|
||||
let val = parseFloat(d) * this.rem;
|
||||
if (typeof d === "string" && d.match(/rem$/)) {
|
||||
val += "px";
|
||||
}
|
||||
return val;
|
||||
};
|
||||
flexible.px2rem = function(d) {
|
||||
let val = parseFloat(d) / this.rem;
|
||||
if (typeof d === "string" && d.match(/px$/)) {
|
||||
val += "rem";
|
||||
}
|
||||
return val;
|
||||
};
|
||||
})(window, window["lib"] || (window["lib"] = {}));
|
||||