泛微薪资核算iframe表格

custom-人事报表/V2-demo
黎永顺 2 years ago
parent c31ec3957a
commit 70d0508a9c

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1017 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

@ -0,0 +1,172 @@
@node-color: #1890ff;
@line-width: 1px;
@line-color: @node-color;
@expand-icon-size: 16px;
.text-overflow-ellipsis() {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.org-chart {
&-container {
display: flex;
justify-content: center;
margin-top: 26px;
}
&-table {
border-collapse: separate;
border-spacing: 0;
line-height: 1.5715;
.expand-icon {
display: inline-block;
width: @expand-icon-size;
height: @expand-icon-size;
border-radius: 50%;
background-color: @line-color;
position: absolute;
left: 50%;
bottom: 0;
margin-left: -@expand-icon-size / 2;
margin-bottom: -@expand-icon-size / 2;
z-index: 99;
cursor: pointer;
&-expanded,
&-collapsed {
&::before {
content: '';
width: 8px;
height: 2px;
background-color: #fff;
position: absolute;
top: 7px;
left: 4px;
}
}
&-collapsed {
&::after {
content: '';
width: 2px;
height: 8px;
background-color: #fff;
position: absolute;
top: 4px;
left: 7px;
}
}
&:hover {
background-color: darken(@line-color, 5%);
}
}
td {
text-align: center;
padding: 0;
vertical-align: top;
&.hidden {
display: none !important;
}
}
&-node {
display: inline-block;
//border: 1px solid @node-color;
//padding: 0.5rem;
margin: 0 5px;
cursor: pointer;
}
&-line {
height: 20px;
position: relative;
&:not(&-children) {
&:before {
content: '';
position: absolute;
top: 0;
width: @line-width;
height: 100%;
background-color: @line-color;
}
}
&-children {
&:nth-child(odd) {
border-right: @line-width solid @line-color;
}
&:not(:first-child, :last-child) {
border-top: @line-width solid @line-color;
}
}
}
// 水平
&&-horizontal {
.expand-icon {
top: 50%;
bottom: auto;
left: auto;
right: 0;
margin: -@expand-icon-size / 2 -@expand-icon-size / 2 auto auto;
}
td {
vertical-align: middle;
}
tr {
&:nth-child(odd) {
> .org-chart-table-line-children {
border-bottom: @line-width solid @line-color;
}
}
&:not(:first-child, :last-child) {
> .org-chart-table-line-children {
border-left: @line-width solid @line-color;
}
}
}
.org-chart-table {
&-node {
margin: 5px 0;
width: 120px;
&-content {
.text-overflow-ellipsis();
}
}
&-line {
height: auto;
width: 20px;
&:not(&-children) {
&:before {
top: auto;
left: 0;
width: 100%;
height: @line-width;
}
}
&-children {
&:nth-child(odd),
&:not(:first-child, :last-child) {
border: none;
}
}
}
}
}
}
}

@ -0,0 +1,31 @@
import classNames from "classnames";
import React from "react";
import { OrgChartProps } from "./interface";
import OrgChartNode from "./components/OrgChartNode";
import ChartWrapper from "./components/ChartWrapper";
import styles from "./OrgChart.less";
const OrgChart = (props: OrgChartProps) => {
const { data, className, style, pan, zoom, maxZoom,minZoom,zoomStep, ...otherProps } = props;
return !!data ? (
<ChartWrapper
pan={pan}
zoom={zoom}
maxZoom={maxZoom}
minZoom={minZoom}
zoomStep={zoomStep}
>
<div className={classNames(styles["org-chart-container"], className)} style={style}>
<OrgChartNode {...otherProps} data={data}/>
</div>
</ChartWrapper>
) : null;
};
OrgChart.defaultProps = {
direction: "vertical",
expandAll: true,
expandable: false
};
export default OrgChart;

@ -0,0 +1,126 @@
import React from "react";
import styles from "../OrgChart.less";
const clsPrefix = "cp-react-org-chart-chart-wrapper";
export default class ChartWrapper extends React.PureComponent {
static propTypes = {};
static defaultProps = {
maxZoom: 2,
minZoom: 0.5,
zoomStep: 0.03
};
state = {
scale: 1,
isMove: false,
translateX: 0,
translateY: 0,
originX: 0,
originY: 0
};
componentDidMount() {
document.addEventListener("mousemove", this.onMouseMove);
}
componentWillUnmount() {
document.removeEventListener("mousemove", this.onMouseMove);
}
onZoom = e => {
const { zoom, maxZoom, minZoom, zoomStep } = this.props;
if (!zoom) return;
const { deltaY, pageX, pageY } = e;
const { offsetWidth, offsetHeight } = this.wrapper;
let { scale } = this.state;
const { translateX, translateY } = this.state;
if (deltaY < 0 && scale < maxZoom) {
scale += zoomStep;
}
if (deltaY > 0 && scale > minZoom) {
scale -= zoomStep;
}
this.setState({
scale,
originX: pageX,
originY: pageY
});
};
toggleMove(isMove) {
this.setState({ isMove });
}
onMouseDown = e => {
this.toggleMove(true);
const { pageX, pageY } = e;
const { translateX, translateY } = this.state;
this.pageX = pageX;
this.pageY = pageY;
this.translateX = translateX;
this.translateY = translateY;
};
onMouseUp = e => {
this.toggleMove(false);
};
onMouseMove = e => {
const { isMove, scale } = this.state;
if (!isMove) return;
const { pageX, pageY } = e;
const x = (pageX - this.pageX) / scale + this.translateX;
const y = (pageY - this.pageY) / scale + this.translateY;
this.setState({
translateX: x,
translateY: y
});
};
getProps() {
const { zoom, pan } = this.props;
const props = {};
if (zoom) {
props.onWheel = this.onZoom;
}
if (pan) {
props.onMouseUp = this.onMouseUp;
props.onMouseDown = this.onMouseDown;
props.onMouseMove = this.onMouseMove;
}
return props;
}
render() {
const { children } = this.props;
const { scale, translateX, translateY, isMove, originX, originY } = this.state;
const cursor = isMove ? "move" : "default";
const props = this.getProps();
return (
<div className={styles[clsPrefix]} style={{ overflow: "hidden", flex: 1 }}>
<div
ref={node => {
this.wrapper = node;
}}
style={{
cursor,
transform: `scale(${scale}) translate(${translateX}px, ${translateY}px)`,
transformOrigin: `${originX}px ${originY}px`
}}
className={styles["chart-wrapper-main"]}
{...props}
>
{children}
</div>
</div>
);
}
}

@ -0,0 +1,180 @@
import classNames from "classnames";
import React from "react";
import { OrgChartNodeDataType, OrgChartNodeProps, RowProps } from "../interface";
import OrgChartNodeRow from "./OrgChartNodeRow";
import styles from "../OrgChart.less";
const OrgChartNode = (props: OrgChartNodeProps) => {
const {
data,
expandAll,
expandable,
direction,
renderNode,
onExpand,
onClick
} = props;
const [expanded, setExpanded] = React.useState<boolean>(false);
const handleExpandChange = () => {
const newExpanded = !expanded;
setExpanded(newExpanded);
onExpand?.(newExpanded, data);
};
const getOrgNodeRow = (
span: number,
data: OrgChartNodeDataType
): RowProps => {
const contentNode: React.ReactNode = (
<div className={styles["org-chart-table-node-content"]} title={data.label}>
{data.label}
</div>
);
return [
{
span,
content: (
<td>
<div
className={classNames(styles["org-chart-table-node"], data.className)}
style={data.style}
onClick={() => onClick?.(data)}>
{!!renderNode ? renderNode(data, contentNode) : contentNode}
</div>
</td>
)
}
];
};
const getLineRow = (span: number): RowProps => {
return [
{
span,
content: (
<td className={styles["org-chart-table-line"]}>
{expandable ? (
<div
className={classNames({
[styles["expand-icon"]]: expandable,
[styles["expand-icon-expanded"]]: expandable && expanded,
[styles["expand-icon-collapsed"]]: expandable && !expanded
})}
onClick={() => handleExpandChange()}></div>
) : null}
</td>
)
}
];
};
const getChildrenLineRow = (span: number, itemSpan: number): RowProps => {
const cells: RowProps = [];
for (let index = 0; index < span; index = index + itemSpan) {
cells.push({
span: 1,
content: (
<td
key={index}
className={classNames(
styles["org-chart-table-line"],
styles["org-chart-table-line-children"],
{
[styles["hidden"]]: !expanded
}
)}>
&nbsp;
</td>
)
});
}
return cells;
};
const getChildrenNode = (
datas: OrgChartNodeDataType[] = [],
itemSpan: number
): RowProps => {
const cells: RowProps = [];
datas.forEach((data, index) => {
cells[itemSpan * index] = {
span: itemSpan,
content: (
<td
key={data.key}
className={classNames(styles["org-chart-table-node-children"], {
[styles["hidden"]]: !expanded
})}>
<OrgChartNode {...props} data={data}/>
</td>
)
};
});
return cells;
};
const getRows = (): RowProps[] => {
if (data) {
const rows: RowProps[] = [];
const childrenLength = data?.children?.length || 0;
const span = childrenLength * 2;
rows.push(getOrgNodeRow(span, data));
// 判断是否有子节点
if (data.children?.length ?? 0 > 0) {
rows.push(getLineRow(span));
rows.push(getChildrenLineRow(span, 1));
rows.push(getChildrenNode(data.children, 2));
}
if (direction === "horizontal") {
const newRow: RowProps[] = [];
rows.forEach((row, rowIndex) => {
row.forEach((cell, cellIndex) => {
newRow[cellIndex] = newRow[cellIndex] || [];
newRow[cellIndex][rowIndex] = cell;
});
});
return newRow;
}
return rows;
}
return [];
};
const rows = React.useMemo(() => {
return getRows();
}, [data, expanded, direction]);
React.useEffect(() => {
if (expandable === true) {
setExpanded(!!expandAll);
} else {
setExpanded(true);
}
}, [expandAll, expandable]);
return (
<table
className={classNames(styles["org-chart-table"], {
[styles["org-chart-table-horizontal"]]: direction === "horizontal",
[styles["org-chart-table-vertical"]]: direction === "vertical"
})}>
<tbody>
{rows.map((row, index) => (
<OrgChartNodeRow
key={index}
index={index}
row={row}
direction={direction}
/>
))}
</tbody>
</table>
);
};
export default OrgChartNode;

@ -0,0 +1,27 @@
import { OrgChartNodeProps, RowProps } from '../interface';
import React from 'react';
export interface OrgChartNodeRowProps
extends Pick<OrgChartNodeProps, 'direction'> {
className?: string;
index: number;
row: RowProps;
}
const OrgChartNodeRow = (props: OrgChartNodeRowProps) => {
const { className, index, row, direction } = props;
return (
<tr key={index} className={className}>
{row.map((cell, cellIndex) => {
return React.cloneElement(cell.content, {
key: cellIndex,
...(direction === 'horizontal' ? { rowSpan: cell.span } : {}),
...(direction === 'vertical' ? { colSpan: cell.span } : {}),
});
})}
</tr>
);
};
export default OrgChartNodeRow;

@ -0,0 +1,5 @@
import { OrgChartNodeDataType, OrgChartProps } from './interface';
import OrgChart from './OrgChart';
export { OrgChartNodeDataType, OrgChartProps };
export default OrgChart;

@ -0,0 +1,43 @@
import React from "react";
export interface CellProps {
span: number;
content: React.ReactElement;
}
export type RowProps = CellProps[];
export interface OrgChartNodeDataType {
key: string | number;
label: string;
operatingIncome?: string;
operatingIncomePercent?: string;
operatingProfit?: string;
operatingProfitPercent?: string;
children?: OrgChartNodeDataType[];
className?: string;
style?: React.CSSProperties;
}
export interface OrgChartNodeProps {
data: OrgChartNodeDataType;
expandAll?: boolean;
expandable?: boolean;
direction?: "horizontal" | "vertical";
renderNode?: (
node: OrgChartNodeDataType,
originNode: React.ReactNode
) => React.ReactNode;
onExpand?: (expanded: boolean, node: OrgChartNodeDataType) => void;
onClick?: (node: OrgChartNodeDataType) => void;
}
export interface OrgChartProps extends Partial<OrgChartNodeProps> {
className?: string;
style?: React.CSSProperties;
pan?: boolean;
zoom?: boolean;
maxZoom?: number;
minZoom?: number;
zoomStep?: number;
}

@ -0,0 +1,28 @@
/*
* Author:
* name:
* Description:
* Date: 2023/5/6
*/
import React, { FunctionComponent } from "react";
import styles from "./index.less";
interface OwnProps {
children: any;
title: string;
}
type Props = OwnProps;
const frameBox: FunctionComponent<Props> = (props) => {
const { children, title } = props;
return (
<div className={styles.frameBoxWrapper}>
<div className={styles.header}><span><span>{title}</span></span></div>
{children}
</div>
);
};
export default frameBox;

@ -0,0 +1,28 @@
.frameBoxWrapper {
display: flex;
flex-direction: column;
height: 100%;
.header {
height: 54px;
line-height: 54px;
width: 100%;
padding: 0 16px;
& > span {
display: inline-block;
font-size: 18px;
color: #333333;
letter-spacing: 0;
font-weight: 700;
width: 100%;
height: inherit;
border-bottom: 1px solid #e5e5e5;
span {
border-left: 5px solid #0055F3;
padding-left: 8px;
}
}
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,74 @@
/*
* Author:
* name:
* Description:
* Date: 2023/5/5
*/
import React, { FC } from "react";
import { Progress } from "antd";
import styles from "./index.less";
import { TinyArea } from "@ant-design/plots";
interface OwnProps {
title: string;
number: string;
unit: string;
color: string;
icon: any;
isLine: boolean;
yoyVal?: string;
chainVal?: string;
yoyIcon?: any;
departureVal?: string,
refusal?: string,
chainIcon?: 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 FrameCard1: FC<Props> = (props) => {
const { title, number, unit, color, icon, isLine, yoyIcon, chainIcon, yoyVal, chainVal, departureVal, refusal } = props;
return (
<div className={styles.frameCard1Wrapper}>
<div className={styles.top}>
<div><img src={icon} alt=""/><span>{title}</span></div>
<div><span>{number}</span><span>{unit}</span></div>
</div>
<div className={styles.center}>
{
isLine ?
<TinyArea {..._.assign(config, {
areaStyle: { fill: `l(270) 0:#fff 0.5:${color} 1:${color}` },
line: { color }
})} /> : <Progress percent={0.57} showInfo={false}/>
}
</div>
<div className={styles.bottom}>
{
yoyIcon ? <>
<span><span></span>{yoyIcon && <img src={yoyIcon} alt=""/>}<span>{yoyVal}</span></span>
<span><span></span>{chainIcon && <img src={chainIcon} alt=""/>}<span>{chainVal}</span></span>
</>:<>
<span><span></span>{yoyIcon && <img src={yoyIcon} alt=""/>}<span>{departureVal}</span></span>
<span><span>退</span>{chainIcon && <img src={chainIcon} alt=""/>}<span>{refusal}</span></span>
</>
}
</div>
</div>
);
};
export default FrameCard1;

@ -0,0 +1,51 @@
/*
* Author:
* name:
* Description:
* Date: 2023/5/5
*/
import React, { FC } from "react";
import styles from "./index.less";
interface OwnProps {
title: string;
icon: any;
actualValue: any;
contemValue: any;
yoyVal?: string;
chainVal?: string;
yoyIcon?: any;
chainIcon?: any;
}
type Props = OwnProps;
const FrameCard2: FC<Props> = (props) => {
const { title, icon, actualValue, contemValue, yoyIcon, chainIcon, yoyVal, chainVal } = props;
return (
<div className={styles.frameCard2Wrapper}>
<div className={styles.top}>
<div>{title}</div>
<img src={icon} alt=""/>
</div>
<div className={styles.center}>
<div><span>{actualValue?.title}</span>
<div><span>{actualValue?.number}</span><span>{actualValue?.unit}</span></div>
</div>
<div><span>{contemValue?.title}</span>
<div><span>{contemValue?.number}</span><span>{contemValue?.unit}</span></div>
</div>
</div>
{
yoyIcon &&
<div className={styles.bottom}>
<span><span></span>{yoyIcon && <img src={yoyIcon} alt=""/>}<span>{yoyVal}</span></span>
<span><span></span>{chainIcon && <img src={chainIcon} alt=""/>}<span>{chainVal}</span></span>
</div>
}
</div>
);
};
export default FrameCard2;

@ -0,0 +1,193 @@
.frameCard1Wrapper {
display: flex;
flex-direction: column;
overflow: hidden;
width: 100%;
height: 100%;
.top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
& > div:first-child {
display: flex;
flex-direction: column;
align-items: center;
img {
width: 20px;
margin-bottom: 6px;
}
span {
font-size: 16px;
color: #333333;
letter-spacing: 0;
font-weight: 700;
}
}
& > div:last-child {
& > span:first-child {
font-size: 24px;
color: #14224F;
letter-spacing: 0;
font-weight: 700;
}
& > span:last-child {
font-size: 12px;
color: #D4D4D4;
font-weight: 400;
}
}
}
.center {
flex: 1;
display: flex;
align-items: center;
min-height: 83px;
& > div {
width: 100%;
height: 100%;
}
:global {
.ant-progress {
display: flex;
align-items: center;
}
}
}
.bottom {
display: flex;
justify-content: space-between;
align-items: center;
border-top: 0.5px solid #e5e5e5;
padding-top: 10px;
& > span {
img {
width: 8px;
margin-left: 8px;
margin-right: 4px;
vertical-align: middle;
}
& > span:first-child {
font-size: 12px;
color: #D4D4D4;
font-weight: 400;
}
& > span:last-child {
font-size: 12px;
color: #333333;
font-weight: 400;
}
}
}
}
.frameCard2Wrapper {
display: flex;
flex-direction: column;
overflow: hidden;
width: 100%;
height: 100%;
.top {
display: flex;
justify-content: space-between;
padding-bottom: 16px;
background: url("../../../../assets/images/top_bottom.png") no-repeat;
background-size: 100% auto;
background-position-y: bottom;
& > div {
font-size: 18px;
color: #FFFFFF;
letter-spacing: 0;
font-weight: 700;
}
img {
width: 30px;
}
}
.center {
flex: 1;
display: flex;
justify-content: space-between;
& > div {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
& > span:first-child {
font-size: 16px;
color: #FFFFFF;
letter-spacing: 0;
font-weight: 400;
margin-bottom: 5px;
}
& > div:last-child {
& > span:first-child {
font-size: 24px;
color: #FFF;
letter-spacing: 0;
font-weight: 700;
}
& > span:last-child {
font-size: 12px;
color: #FFF;
font-weight: 400;
}
}
}
}
.bottom {
display: flex;
justify-content: space-between;
align-items: center;
background: rgba(255, 255, 255, 0.3);
border-radius: 2px;
color: #FFF;
padding: 0 8px;
height: 33px;
& > span {
img {
width: 8px;
margin-left: 8px;
margin-right: 4px;
vertical-align: middle;
}
& > span:first-child {
font-size: 12px;
color: #FFF;
font-weight: 400;
}
& > span:last-child {
font-size: 12px;
color: #FFF;
font-weight: 400;
}
}
}
}

@ -0,0 +1,136 @@
const PersonnelFlowBar = (echarts: any) => {
// 实例化对象
const myChart = echarts.init(document.getElementById("personnelFlowBar"));
// 配置项
const option = {
tooltip: {
// 坐标轴指示器,坐标轴触发有效
trigger: "axis",
axisPointer: {
// 默认为直线,可选为:'line' | 'shadow'
type: "shadow"
},
formatter: function (params: any) {
let str = params[0].name + "<br>";
for (let item of params) {
str += item.seriesName + " : " + item.value + "%<br>";
}
return str;
}
},
legend: {
icon: "circle",
top: "-1%",
right: "2%",
itemGap: 20,
textStyle: {
fontSize: 12,//字体大小
color: "#B8B8B8"//字体颜色
}
},
grid: {
top: "15%",
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: "15%",
barGap: "80%",
data: initData([12, 10, 23, 15, 3, 7, 11, 20, 17, 15, 8, 12]),
// bar 样式修改
itemStyle: {
color: "#3EA1FF"
}
},
{
name: "去年",
type: "bar",
showBackground: false,
barWidth: "15%",
data: initData([7, 12, 8, 12, 12, 5, 8, 15, 18, 12, 9, 7]),
// bar 样式修改
itemStyle: {
color: "#0ED8F9"
}
}
]
};
// 配置项给实例对象
myChart.setOption(option);
return myChart;
};
export default PersonnelFlowBar;
const initData = (arr: Array<number>) => {
const total = _.reduce(arr, (pre: number, cur: number) => pre + cur, 0);
const serie = [];
for (let i = 0; i < arr.length; i++) {
const num = arr[i];
const numcount = Percentage(num, total);
serie.push(numcount);
}
return serie;
};
const Percentage = (num: number, total: number) => {
return (Math.round(num / total * 100));// 小数点后两位百分比
};

@ -0,0 +1,161 @@
const TrendsInSeparationsCharts = (echarts: any) => {
// 实例化对象
const myChart = echarts.init(document.getElementById("trendsInSeparationsCharts"));
// 配置项
const option = {
tooltip: {
// 坐标轴指示器,坐标轴触发有效
trigger: "axis",
axisPointer: {
// 默认为直线,可选为:'line' | 'shadow'
type: "shadow"
}
},
legend: {
icon: "circle",
top: "5%",
right: "center",
itemGap: 20,
textStyle: {
fontSize: 12,//字体大小
color: "#787E95"//字体颜色
}
},
grid: {
top: "20%",
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)"
}
}
},
{
type: "value",
// 修改坐标值样式
axisLabel: {
color: "#787E95",
fontSize: 14,
formatter: '{value} %'
},
nameTextStyle: {
color: "#787E95",
fontSize: 16
},
// 修改坐标轴线样式
axisLine: {
show: true,
lineStyle: {
color: "transparent"
}
},
axisTick: {
show: false // 不显示坐标轴刻度线
},
splitLine: {
show: false,
lineStyle: {
type: "dashed",
color: "rgba(93,126,158,1)"
}
}
}
],
series: [
{
name: "主动离职",
type: "bar",
stack:"Search Engine",
showBackground: false,
barWidth: "10%",
barGap: "100%",
data: [80, 30, 10, 38, 45, 70, 70, 70, 40, 50, 30, 10],
// bar 样式修改
itemStyle: {
barBorderRadius: 2,
color: "#00C48B"
}
},
{
name: "辞退",
type: "bar",
stack:"Search Engine",
showBackground: false,
barWidth: "10%",
data: [20, 10, 5, 10, 10, 1, 1, 12, 10, 15, 5, 2],
// bar 样式修改
itemStyle: {
barBorderRadius: 2,
color: "#FFA400"
}
},
{
name: "离职率",
type: "line",
yAxisIndex: 1,
data: [10, 4, 2, 5, 5, 7, 7, 10, 4, 6, 4, 2],
itemStyle: {
normal: {
color: "#66EFE8",
lineStyle: {
color: "#66EFE8"
}
}
},
}
]
};
// 配置项给实例对象
myChart.setOption(option);
return myChart;
};
export default TrendsInSeparationsCharts;

@ -1,6 +1,201 @@
@gap: 16px;
@cardWidth: calc(~"20vw" - 4 * @gap / 5);
.peopleFlowWrapper {
width: 100%;
height: 100%;
background: #e5e5e5;
overflow: auto;
display: flex;
flex-direction: column;
.headerCardWrapper {
display: flex;
margin-bottom: @gap;
padding: 0;
& > li:not(:last-child) {
margin-right: @gap;
}
& > li:not(:first-child) {
background-size: 100% 100% !important;
}
& > li:first-child {
background: #FFF;
}
& > li {
width: @cardWidth;
min-height: 220px;
border-radius: 5px;
padding: 16px;
}
}
.contentBox {
flex: 1;
display: flex;
.contentBoxLeft {
flex: 1;
display: flex;
flex-direction: column;
.contenBoxTop {
flex: 1;
.chartsBox {
flex: 4 1;
display: flex;
& > ul {
width: @cardWidth;
margin: 0 @gap 0 0;
padding: 0;
overflow: hidden;
& > li:first-child {
margin-bottom: @gap;
}
& > li {
background: #FFF;
min-height: 220px;
border-radius: 5px;
padding: 16px;
}
}
& > div {
flex: 3 1;
background: #FFF;
border-radius: 5px;
min-height: 220px;
display: flex;
flex-direction: column;
& > ul {
margin: 20px 16px;
padding: 0;
display: flex;
& > li:hover {
cursor: pointer;
}
& > li.active {
font-size: 16px;
color: #0055F3;
letter-spacing: 0;
font-weight: 700;
border-bottom: 1px solid #0055F3;
}
& > li:not(:last-child) {
margin-right: 26px;
}
li {
font-size: 14px;
color: #999999;
letter-spacing: 0;
font-weight: 400;
padding: 6px 0;
}
}
& > div {
min-height: 377px;
}
}
}
}
.contenBoxBottom {
flex: 1;
margin-top: @gap;
background: #FFF;
border-radius: 5px;
.trendsInSeparations {
min-height: 220px;
max-height: 220px;
}
}
}
.rankingBox {
width: @cardWidth;
margin-left: @gap;
background: #FFF;
border-radius: 5px;
.trainingRanking {
overflow-y: auto;
.rankDescBox {
display: flex;
font-size: 4px;
align-items: center;
img {
width: 30px;
margin-right: 10px;
}
.rankNum {
margin-right: 10px
}
.rankNumber {
width: 30px;
margin-right: 10px;
text-align: center;
}
.rankDescPersBox {
display: flex;
align-items: center;
& > span:last-child {
display: flex;
flex-direction: column;
margin-left: 8px;
& > span:first-child {
font-size: 14px;
color: #333333;
font-weight: 400;
}
& > span:last-child {
font-size: 12px;
color: #B8B8B8;
font-weight: 400;
min-width: 130px;
max-width: 130px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
.rankRow {
background: #FEFAED;
}
:global {
.ant-list-item {
padding: 16px 20px;
}
}
}
}
}
}

@ -4,7 +4,18 @@
* Description:
* Date: 2023/4/27
*/
import React, { FunctionComponent } from "react";
import React, { FunctionComponent, useEffect, useState } from "react";
// @ts-ignore
import echarts from "echarts";
import { Avatar, List } from "antd";
import FrameCard1 from "./components/frameCard1";
import FrameCard2 from "./components/frameCard2";
import FrameBox from "../components/frameBox";
import { frameCard1List, frameCard2List } from "@/pages/personnelReport/constants";
import PersonnelFlowBar from "./components/personnelFlowCharts";
import TrendsInSeparationsCharts from "./components/trendsInSeparationsCharts";
import { personnelFlowTablist, trainingRankingList } from "../constants";
import cs from "classnames";
import styles from "./index.less";
interface OwnProps {
@ -13,9 +24,116 @@ interface OwnProps {
type Props = OwnProps;
const index: FunctionComponent<Props> = (props) => {
const [active, setActive] = useState<number>(0);
useEffect(() => {
const personnelFlowBar = PersonnelFlowBar(echarts);
const trendsInSeparationsCharts = TrendsInSeparationsCharts(echarts);
// 屏幕缩放对chart图表进行自适应处理调用实例的resize方法
window.onresize = () => {
personnelFlowBar.resize();
trendsInSeparationsCharts.resize();
};
return () => {
};
}, []);
return (
<div className={styles.peopleFlowWrapper}>
<ul className={styles.headerCardWrapper}>
<li>
<FrameCard1
title="在职人数"
number="14338"
unit="人"
color="#78C0F9"
isLine
yoyIcon={require("../../../assets/images/down.png")}
chainIcon={require("../../../assets/images/up.png")}
yoyVal="4.75%"
chainVal="4.75%"
icon={require("../../../assets/images/zzrs.png")}
/>
</li>
{_.map(frameCard2List, (item: any, index: number) => {
return (
<li key={index} style={{ background: `url(${item.bg}) no-repeat` }}>
<FrameCard2 {...item} />
</li>
);
})}
</ul>
<div className={styles.contentBox}>
<div className={styles.contentBoxLeft}>
<div className={styles.contenBoxTop}>
<div className={styles.chartsBox}>
<ul>
{_.map(frameCard1List, (item: any, index: number) => {
return (
<li key={index}>
<FrameCard1 {...item} />
</li>
);
})}
</ul>
<div>
<ul>
{
_.map(personnelFlowTablist, (item: any, index: number) => {
const classes = cs({
[styles["active"]]: index === active
});
return <li key={index} className={classes} onClick={() => setActive(index)}>{item}</li>;
})
}
</ul>
<div id="personnelFlowBar"/>
</div>
</div>
</div>
<div className={styles.contenBoxBottom}>
<FrameBox title="离职情况变化趋势">
<div className={styles.trendsInSeparations} id="trendsInSeparationsCharts"/>
</FrameBox>
</div>
</div>
<div className={styles.rankingBox}>
<FrameBox title="培训排名">
<div className={styles.trainingRanking}>
<List
dataSource={trainingRankingList}
renderItem={(item, index) => (
<List.Item key={index}
className={cs({ [styles["rankRow"]]: index === 0 || index === 1 || index === 2 })}>
<List.Item.Meta
title={
<div className={styles.rankDescBox}>
{
(index === 0 || index === 1 || index === 2) ?
<img src={index === 0 ?
require("../../../assets/images/one.png") : index === 1 ?
require("../../../assets/images/two.png") :
require("../../../assets/images/three.png")} alt=""/> :
<span className={styles.rankNumber}>{index + 1}</span>
}
<div className={styles.rankDescPersBox}>
<Avatar src={item.avatar}/>
<span><span>{item.name}</span><span>{item.dept}</span></span>
</div>
</div>
}
/>
<div style={{
fontWeight: (index === 0 || index === 1 || index === 2) ? 700 : 400,
fontSize: (index === 0 || index === 1 || index === 2) ? 16 : 14,
color: index === 0 ? "#FFA000" : index === 1 ? "#6F9DAE" : index === 2 ? "#B0442B" : "#333333"
}}>{item.score}</div>
</List.Item>
)}
/>
</div>
</FrameBox>
</div>
</div>
</div>
);
};

@ -0,0 +1,52 @@
/*
* Author:
* name:
* Description:
* Date: 2023/5/10
*/
import React, { FunctionComponent } from "react";
import { OrgChartNodeDataType } from "@/components/reactChart/interface";
import styles from "./index.less";
import classNames from "classnames";
interface OwnProps {
node: OrgChartNodeDataType;
}
type Props = OwnProps;
const CompanyItem: FunctionComponent<Props> = (props) => {
const { node } = props;
const classes = classNames(styles["header"], {
[styles["parent"]]: node.key === 0
});
return (
<div className={styles.companyItemWrapper}>
<div className={classes}>{node?.label}</div>
<div className={styles.contBox}>
<div>
<div>
<span></span>
<span>{node?.operatingIncome}</span>
</div>
<div>
<span></span>
<span>{node?.operatingIncomePercent}</span>
</div>
</div>
<div>
<div>
<span></span>
<span>{node?.operatingProfit}</span>
</div>
<div>
<span></span>
<span>{node?.operatingProfitPercent}</span>
</div>
</div>
</div>
</div>
);
};
export default CompanyItem;

@ -0,0 +1,94 @@
const IncomeStatisticsRadar = (echarts: any) => {
// 实例化对象
const myChart = echarts.init(document.getElementById("incomeStatisticsRadar"));
// 配置项
const option = {
tooltip: {
show: true
},
legend: {
show: true, //是否显示图例
icon: "rect", //图例形状
bottom: "2%", // 图例距离顶部边距
left: "center", // 图例距离左侧边距
itemWidth: 10, // 图例标记的图形宽度。[ default: 25 ]
itemHeight: 10, // 图例标记的图形高度。[ default: 14 ]
itemGap: 30, // 图例每项之间的间隔。[ default: 10 ]横向布局时为水平间隔,纵向布局时为纵向间隔。
orient: "horizontal", // 图例列表的布局朝向,'horizontal'为横向,''为纵向.
textStyle: { // 图例的公用文本样式。
fontSize: 14,
color: "#D4D4D4"
}
},
radar: {
// shape: 'circle',
// , axisLabel: { show: true } 该属性加上会报错
indicator: [
{ name: "销售", max: 20000 },
{ name: "客服", max: 20000 },
{ name: "开发", max: 20000 },
{ name: "其他", max: 20000 },
{ name: "项目", max: 20000 }
],
splitArea: {
show: false
},
axisLine: {
show: false
}
},
series: [
{
name: "人均浮动收入",
type: "radar",
data: [
{
value: [9000, 2000, 9000, 6000, 6000],
name: "人均浮动收入"
}
],
label: {
show: false,
formatter: function (params: any) {
return params.value;
}
},
itemStyle: { //此属性的颜色和下面areaStyle属性的颜色都设置成相同色即可实现
color: "#64C3FF",
borderColor: "#64C3FF"
},
areaStyle: {
color: "rgba(100,195,255,0.26)"
}
},
{
name: "人均固定收入",
type: "radar",
data: [
{
value: [5500, 8000, 5000, 8800, 8900],
name: "人均固定收入"
}
],
label: {
show: false,
formatter: function (params: any) {
return params.value;
}
},
itemStyle: { //此属性的颜色和下面areaStyle属性的颜色都设置成相同色即可实现
color: "#66EFE8",
borderColor: "#66EFE8"
},
areaStyle: {
color: "rgba(102,239,232,0.26)"
}
}
]
};
// 配置项给实例对象
myChart.setOption(option);
return myChart;
};
export default IncomeStatisticsRadar;

@ -0,0 +1,100 @@
.companyItemWrapper {
display: flex;
flex-direction: column;
.header {
height: 38px;
line-height: 38px;
text-align: center;
color: #FFF;
font-size: 16px;
background: #199DFF;
}
.parent {
background: #1E66F6 !important;
font-size: 16px;
font-weight: 700;
}
.contBox {
display: flex;
padding: 10px;
color: #B8B8B8;
font-size: 12px;
border: 1px solid rgba(25, 157, 255, 1);
border-top: none;
& > div:first-child {
border-right: 1px solid #e5e5e5;
}
& > div {
flex: 1;
display: flex;
flex-direction: column;
& > div:first-child {
margin: 10px;
}
& > div {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
& > span:last-child {
color: #787E95;
}
}
}
}
}
.regionItemWrapper {
display: flex;
flex-direction: column;
background: #EBF5FF;
min-width: 116px;
.header {
height: 38px;
line-height: 38px;
text-align: center;
color: #333333;
font-size: 16px;
}
.contBox {
display: flex;
padding: 10px 0;
color: #B8B8B8;
font-size: 12px;
& > div:first-child {
border-right: 1px solid #e5e5e5;
}
& > div {
flex: 1;
display: flex;
flex-direction: column;
& > div {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
& > span:last-child {
color: #787E95;
}
}
}
}
}
.regionItemWrapperChild {
background: #EAFCFC;
}

@ -0,0 +1,52 @@
/*
* Author:
* name:
* Description:
* Date: 2023/5/10
*/
import React, { FunctionComponent } from "react";
import { OrgChartNodeDataType } from "@/components/reactChart/interface";
import styles from "./index.less";
import classNames from "classnames";
interface OwnProps {
node: OrgChartNodeDataType;
}
type Props = OwnProps;
const RegionItem: FunctionComponent<Props> = (props) => {
const { node } = props;
const classes = classNames(styles["regionItemWrapper"], {
[styles["regionItemWrapperChild"]]: node.key === 20 || node.key === 21 || node.key === 22 || node.key === 23 || node.key === 24 || node.key === 25
});
return (
<div className={classes}>
<div className={styles.header}>{node?.label}</div>
<div className={styles.contBox}>
<div>
<div>
<span></span>
<span>{node?.operatingIncome}</span>
</div>
<div>
<span></span>
<span>{node?.operatingIncomePercent}</span>
</div>
</div>
<div>
<div>
<span></span>
<span>{node?.operatingProfit}</span>
</div>
<div>
<span></span>
<span>{node?.operatingProfitPercent}</span>
</div>
</div>
</div>
</div>
);
};
export default RegionItem;

@ -0,0 +1,133 @@
const TotalCost = (echarts: any) => {
// 实例化对象
const myChart = echarts.init(document.getElementById("totalCost"));
// 配置项
const option = {
tooltip: {
// 坐标轴指示器,坐标轴触发有效
trigger: "axis",
axisPointer: {
// 默认为直线,可选为:'line' | 'shadow'
type: "shadow"
}
},
legend: {
icon: "circle",
top: "5%",
right: "2%",
itemGap: 20,
textStyle: {
fontSize: 12,//字体大小
color: "#787E95"//字体颜色
}
},
grid: {
top: "20%",
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: "20%",
data: [200, 120, 230, 225, 165, 135, 165, 120, 210, 120, 85, 210],
// bar 样式修改
itemStyle: {
color: "#4CAEFF"
}
},
{
name: "人工成本",
type: "line",
yAxisIndex: 0,
data: [190, 145, 250, 225, 165, 140, 170, 130, 215, 120, 85, 210],
itemStyle: {
normal: {
color: "#64C3FF",
lineStyle: {
color: "#64C3FF"
}
}
},
areaStyle: {
normal: {
color: {//分隔区域的颜色
x0: 0,
y0: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0,
color: "#64C3FF" // 0% 处的颜色
}, {
offset: 1,
color: "rgba(100,195,255,0)" // 100% 处的颜色中间还可以设置50%、20%、70%时的颜色
}],
globalCoord: false // 缺省为 false以确保上面的x,y,x2,y2表示的是百分比
}
}
}
}
]
};
// 配置项给实例对象
myChart.setOption(option);
return myChart;
};
export default TotalCost;

@ -0,0 +1,155 @@
const TotalLaborCostPerCapita = (echarts: any) => {
// 实例化对象
const myChart = echarts.init(document.getElementById("totalLaborCostPerCapita"));
// 配置项
const option = {
tooltip: {
// 坐标轴指示器,坐标轴触发有效
trigger: "axis",
axisPointer: {
// 默认为直线,可选为:'line' | 'shadow'
type: "line"
}
},
legend: {
icon: "circle",
top: "5%",
right: "2%",
itemGap: 20,
textStyle: {
fontSize: 12,//字体大小
color: "#787E95"//字体颜色
}
},
grid: {
top: "20%",
left: "2%",
right: "2%",
bottom: "3%",
containLabel: true
},
xAxis: [
{
type: "category",
boundaryGap: false,
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: "line",
yAxisIndex: 0,
data: [190, 145, 250, 225, 165, 140, 170, 130, 215, 120, 85, 210],
itemStyle: {
normal: {
color: "#64C3FF",
lineStyle: {
color: "#64C3FF"
}
}
},
areaStyle: {
normal: {
color: {//分隔区域的颜色
x0: 0,
y0: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0,
color: "#64C3FF" // 0% 处的颜色
}, {
offset: 1,
color: "rgba(100,195,255,0)" // 100% 处的颜色中间还可以设置50%、20%、70%时的颜色
}],
globalCoord: false // 缺省为 false以确保上面的x,y,x2,y2表示的是百分比
}
}
}
},
{
name: "人均人工成本",
type: "line",
yAxisIndex: 0,
data: [149, 145, 220, 160, 130, 165, 145, 125, 175, 110, 120, 167],
itemStyle: {
normal: {
color: "#66EFE8",
lineStyle: {
color: "#66EFE8"
}
}
},
areaStyle: {
normal: {
color: {//分隔区域的颜色
x0: 0,
y0: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0,
color: "#66EFE8" // 0% 处的颜色
}, {
offset: 1,
color: "rgba(102,239,232,0)" // 100% 处的颜色中间还可以设置50%、20%、70%时的颜色
}],
globalCoord: false // 缺省为 false以确保上面的x,y,x2,y2表示的是百分比
}
}
}
}
]
};
// 配置项给实例对象
myChart.setOption(option);
return myChart;
};
export default TotalLaborCostPerCapita;

@ -1,114 +1,277 @@
.profitWrapper {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
background: #e5e5e5;
padding: 8px;
overflow: hidden;
overflow: hidden auto;
.profitLeft {
display: flex;
flex-direction: column;
width: 580px;
overflow: hidden;
& > div:not(:last-child) {
margin-bottom: 16px;
}
& > :not(:last-child) {
margin-bottom: 16px;
}
.profitHeader {
display: flex;
& > div:first-child {
& > div {
height: 100% !important;
}
& > div:not(:last-child) {
margin-right: 16px;
}
& > div {
flex: 1;
min-height: 200px;
background: #FFF;
position: relative;
padding: 40px 10px 8px;
overflow: hidden;
.title {
display: inline-block;
position: absolute;
left: 1%;
top: 2%;
font-size: 16px;
border-radius: 5px;
flex: 1;
}
//数据看板
.dataTableWrapper {
table-layout: fixed;
width: 100%;
height: 100%;
thead th {
text-align: center;
font-size: 14px;
color: #333333;
letter-spacing: 0;
font-weight: 700;
}
:global {
.ant-tabs {
height: 100%;
line-height: normal;
tbody {
td {
text-align: center;
}
td.title {
font-size: 14px;
color: #9A9A9A;
letter-spacing: 0;
font-weight: 400;
}
td.col3 {
img {
width: 15px;
margin-right: 8px;
vertical-align: middle;
}
}
.dataVal {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
& > span:first-child {
border-bottom: 1px solid #e5e5e5;
line-height: 36px;
.ant-tabs-content-holder {
flex: 1;
& > span:first-child {
font-size: 22px;
color: #0055F3;
letter-spacing: 0;
font-weight: 700;
}
.ant-tabs-content {
height: 100%;
& > span:last-child {
font-size: 12px;
color: #D4D4D4;
font-weight: 400;
}
}
.ant-tabs-tabpane {
overflow-y: auto;
& > span:last-child {
margin-top: 8px;
& > span:first-child {
font-size: 12px;
color: #9A9A9A;
letter-spacing: 0;
font-weight: 400;
margin-right: 18px;
}
& > div {
height: 100% !important;
}
}
& > span:last-child {
font-size: 12px;
color: #FF4751;
letter-spacing: 0;
font-weight: 400;
}
}
}
}
}
}
.profitRight {
flex: 1;
position: relative;
margin-left: 16px;
display: flex;
flex-direction: column;
overflow: hidden;
& > div:first-child {
margin-bottom: 16px;
//
.incomeStatisticsRadar {
min-height: 360px;
max-height: 360px;
}
& > div {
background: #fff;
flex: 1;
padding: 40px 10px 8px;
position: relative;
height: 100px;
// 团队分析排名
.teamRankingWrapper, .personRankingWrapper {
min-height: 360px;
max-height: 360px;
overflow-y: auto;
:global {
.ant-tabs {
height: 100%;
line-height: normal;
.rankDescBox {
display: flex;
font-size: 4px;
align-items: center;
img {
width: 30px;
margin-right: 10px;
}
.rankNum {
margin-right: 10px
}
.rankNumber {
width: 30px;
margin-right: 10px;
text-align: center;
}
.ant-tabs-content-holder {
flex: 1;
.rankDescPersBox {
display: flex;
align-items: center;
.ant-tabs-content {
height: 100%;
& > span:last-child {
display: flex;
flex-direction: column;
margin-left: 8px;
& > span:first-child {
font-size: 14px;
color: #333333;
font-weight: 400;
}
.ant-tabs-tabpane {
overflow-y: auto;
}
& > span:last-child {
font-size: 12px;
color: #B8B8B8;
font-weight: 400;
min-width: 130px;
max-width: 130px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.rankDescPersBox {
display: flex;
align-items: center;
& > span:last-child {
display: flex;
flex-direction: column;
margin-left: 8px;
& > span:first-child {
font-size: 14px;
color: #333333;
font-weight: 400;
}
& > span:last-child {
font-size: 12px;
color: #B8B8B8;
font-weight: 400;
min-width: 130px;
max-width: 130px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
.title {
display: inline-block;
position: absolute;
left: 1%;
top: 2%;
font-size: 16px;
font-weight: 700;
.rankRow {
background: #FEFAED;
}
:global {
.ant-select {
margin: 16px;
}
.ant-list-item {
padding: 6px 20px;
}
}
.rankingTabWrapper {
display: flex;
align-items: center;
line-height: 32px;
padding: 16px;
margin: 0;
& > li.active {
color: #0055F3;
font-weight: 700;
border-bottom: 2px solid #0055F3;
}
& > li:not(:last-child) {
margin-right: 16px;
}
& > li {
font-size: 14px;
color: #999999;
letter-spacing: 0;
font-weight: 400;
cursor: pointer;
border-bottom: 2px solid transparent;
min-width: 95px;
max-width: 95px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
.profitCenter {
display: flex;
& > div:first-child {
margin-right: 16px;
}
& > div {
min-height: 200px;
background: #FFF;
border-radius: 5px;
flex: 1;
}
.totalLaborCost {
min-height: 326px;
max-height: 326px;
}
}
.profitBottom {
min-height: 800px;
background: #FFF;
border-radius: 5px;
//& > div {
// height: 100%;
//
// & > div {
// height: 100%;
// }
//}
}
}

@ -4,21 +4,28 @@
* Description:
* Date: 2023/4/27
*/
import React, { FC } from "react";
import styles from "./index.less";
import { Avatar, List, Tabs } from "antd";
import React, { FC, useEffect, useState } from "react";
// @ts-ignore
import echarts from "echarts";
import { Avatar, List, Select } from "antd";
import FrameBox from "../components/frameBox";
import IncomeStatisticsRadar from "./components/incomStaticsRadar";
import TotalCostCharts from "./components/totalCostCharts";
import TotalLaborCostPerCapita from "./components/totalLaborCostPerCapita";
import OrgChart from "@/components/reactChart";
import CompanyItem from "./components/companyItem";
import RegionItem from "./components/regionItem";
import {
fixedIncomePerData,
floatingIncomePerData,
humanAnalysisconfig,
humanEfficiencyRankingData,
laborCostsPerCapitaConfig,
newSaleEfficiencyRankingData,
perCapitaIncomeConfig,
saleEfficiencyRankingData,
totalLaborCostConfig
} from "@/pages/personnelReport/constants";
import { Column, DualAxes, Pie } from "@ant-design/plots";
humanRankingList,
orgList,
personRankingTablist,
teamAnalysisRankingsOptions,
teamRankingList,
humanRankingList_1,
humanRankingList_2
} from "../constants";
import cs from "classnames";
import styles from "./index.less";
interface OwnProps {
}
@ -26,211 +33,212 @@ interface OwnProps {
type Props = OwnProps;
const index: FC<Props> = (props) => {
const [active, setActive] = useState<number>(0);
useEffect(() => {
const incomeStatisticsRadar = IncomeStatisticsRadar(echarts);
const totalCostCharts = TotalCostCharts(echarts);
const totalLaborCostPerCapita = TotalLaborCostPerCapita(echarts);
// 屏幕缩放对chart图表进行自适应处理调用实例的resize方法
window.onresize = () => {
incomeStatisticsRadar.resize();
totalCostCharts.resize();
totalLaborCostPerCapita.resize();
};
return () => {
};
}, []);
// @ts-ignore
return (
<div className={styles.profitWrapper}>
<div className={styles.profitLeft}>
<div className={styles.profitLeftItem1}>
<span className={styles.title}></span>
{/*@ts-ignore*/}
<Column {...humanAnalysisconfig} />
</div>
<div className={styles.profitLeftItem2}>
<span className={styles.title}></span>
<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 className={styles.profitHeader}>
<div>
<FrameBox title="数据看板">
<div style={{ padding: 16, minHeight: 360, maxHeight: 360 }}>
<table className={styles.dataTableWrapper}>
<thead>
<tr>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td className={styles.title}></td>
<td>
<div className={styles.dataVal}>
<span><span>3016</span><span></span></span>
<span><span></span><span>3457</span></span>
</div>
</td>
<td className={styles.col3} style={{ color: "#FF4751" }}>
<img src={require("../../../assets/images/up.png")} alt=""/>
<span>4.75%</span>
</td>
</tr>
<tr>
<td className={styles.title}></td>
<td>
<div className={styles.dataVal}>
<span><span>17456</span><span></span></span>
<span><span></span><span>13567</span></span>
</div>
</td>
<td className={styles.col3} style={{ color: "#FF4751" }}>
<img src={require("../../../assets/images/up.png")} alt=""/>
<span>4.75%</span>
</td>
</tr>
<tr>
<td className={styles.title}></td>
<td>
<div className={styles.dataVal}>
<span><span>34.6</span><span></span></span>
<span><span></span><span>45.16</span></span>
</div>
</td>
<td className={styles.col3} style={{ color: "#00BB7A" }}>
<img src={require("../../../assets/images/down.png")} alt=""/>
<span>4.75%</span>
</td>
</tr>
</tbody>
</table>
</div>
</FrameBox>
</div>
<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>
<FrameBox title="人员收入统计">
<div className={styles.incomeStatisticsRadar} id="incomeStatisticsRadar"/>
</FrameBox>
</div>
</div>
<div className={styles.profitRight}>
<div>
<span className={styles.title}></span>
<Tabs defaultActiveKey="1">
<Tabs.TabPane tab="有效金额排名" key="1">
<List
itemLayout="horizontal"
dataSource={humanEfficiencyRankingData}
renderItem={item => (
<List.Item actions={[<a key="list-loadmore-more">more</a>]}>
<List.Item.Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random"/>}
title={<a href="https://ant.design">{item.title}</a>}
description={item.description}
/>
<div>{item.rank}</div>
</List.Item>
)}
<FrameBox title="团队分析排名">
<div className={styles.teamRankingWrapper}>
<Select defaultValue="1"
options={teamAnalysisRankingsOptions}
/>
</Tabs.TabPane>
<Tabs.TabPane tab="合同数量排名" key="2">
<List
itemLayout="horizontal"
dataSource={saleEfficiencyRankingData}
renderItem={item => (
<List.Item actions={[<a key="list-loadmore-more">more</a>]}>
dataSource={teamRankingList}
renderItem={(item, index) => (
<List.Item key={index}
className={cs({ [styles["rankRow"]]: index === 0 || index === 1 || index === 2 })}>
<List.Item.Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random"/>}
title={<a href="https://ant.design">{item.title}</a>}
description={item.description}
title={
<div
className={styles.rankDescBox}>
{
(index === 0 || index === 1 || index === 2) ?
<img src={index === 0 ?
require("../../../assets/images/one.png") : index === 1 ?
require("../../../assets/images/two.png") :
require("../../../assets/images/three.png")} alt=""/> :
<span className={styles.rankNumber}>{index + 1}</span>
}
<div className={styles.rankDescPersBox}>
{
(index === 0 || index === 1 || index === 2) &&
<Avatar src={item.avatar}/>
}
<span><span>{item.name}</span><span>{item.dept}</span></span>
</div>
</div>
}
/>
<div>{item.rank}</div>
<div style={{
fontWeight: (index === 0 || index === 1 || index === 2) ? 500 : 400,
fontSize: (index === 0 || index === 1 || index === 2) ? 16 : 14,
color: "#333333"
}}>{item.score}</div>
</List.Item>
)}
/>
</Tabs.TabPane>
<Tabs.TabPane tab="有效合同排名" key="3">
<List
itemLayout="horizontal"
dataSource={newSaleEfficiencyRankingData}
renderItem={item => (
<List.Item actions={[<a key="list-loadmore-more">more</a>]}>
<List.Item.Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random"/>}
title={<a href="https://ant.design">{item.title}</a>}
description={item.description}
/>
<div>{item.rank}</div>
</List.Item>
)}
/>
</Tabs.TabPane>
</Tabs>
</div>
</FrameBox>
</div>
<div>
<span className={styles.title}></span>
<Tabs defaultActiveKey="1">
<Tabs.TabPane tab="指标400万" key="1">
<List
itemLayout="horizontal"
dataSource={humanEfficiencyRankingData}
renderItem={item => (
<List.Item actions={[<a key="list-loadmore-more">more</a>]}>
<List.Item.Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random"/>}
title={<a href="https://ant.design">{item.title}</a>}
description={item.description}
/>
<div>{item.rank}</div>
</List.Item>
)}
/>
</Tabs.TabPane>
<Tabs.TabPane tab="400万≤指标600万" key="2">
<List
itemLayout="horizontal"
dataSource={humanEfficiencyRankingData}
renderItem={item => (
<List.Item actions={[<a key="list-loadmore-more">more</a>]}>
<List.Item.Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random"/>}
title={<a href="https://ant.design">{item.title}</a>}
description={item.description}
/>
<div>{item.rank}</div>
</List.Item>
)}
/>
</Tabs.TabPane>
<Tabs.TabPane tab="600万≤指标800万" key="3">
<FrameBox title="人效分析排名">
<div className={styles.personRankingWrapper}>
<ul className={styles.rankingTabWrapper}>
{
_.map(personRankingTablist, (item: any, index: number) => {
const classes = cs({
[styles["active"]]: index === active
});
return <li key={index} className={classes} onClick={() => setActive(index)}
title={item}>{item}</li>;
})
}
</ul>
<List
itemLayout="horizontal"
dataSource={humanEfficiencyRankingData}
renderItem={item => (
<List.Item actions={[<a key="list-loadmore-more">more</a>]}>
dataSource={active === 0 ? humanRankingList : active === 1 ? humanRankingList_1 : humanRankingList_2}
renderItem={(item, index) => (
<List.Item key={index}
className={cs({ [styles["rankRow"]]: index === 0 || index === 1 || index === 2 })}>
<List.Item.Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random"/>}
title={<a href="https://ant.design">{item.title}</a>}
description={item.description}
title={
<div className={styles.rankDescBox}>
{
(index === 0 || index === 1 || index === 2) ?
<img src={index === 0 ?
require("../../../assets/images/one.png") : index === 1 ?
require("../../../assets/images/two.png") :
require("../../../assets/images/three.png")} alt=""/> :
<span className={styles.rankNumber}>{index + 1}</span>
}
<div className={styles.rankDescPersBox}>
{
(index === 0 || index === 1 || index === 2) &&
<Avatar src={item.avatar}/>
}
<span><span>{item.name}</span><span>{item.dept}</span></span>
</div>
</div>
}
/>
<div>{item.rank}</div>
<div style={{
fontWeight: (index === 0 || index === 1 || index === 2) ? 500 : 400,
fontSize: (index === 0 || index === 1 || index === 2) ? 16 : 14,
color: "#333333"
}}>{item.score}</div>
</List.Item>
)}
/>
</Tabs.TabPane>
<Tabs.TabPane tab="800万≤指标1200万" key="4">
<List
itemLayout="horizontal"
dataSource={humanEfficiencyRankingData}
renderItem={item => (
<List.Item actions={[<a key="list-loadmore-more">more</a>]}>
<List.Item.Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random"/>}
title={<a href="https://ant.design">{item.title}</a>}
description={item.description}
/>
<div>{item.rank}</div>
</List.Item>
)}
/>
</Tabs.TabPane>
<Tabs.TabPane tab="1200万≤指标1600万" key="5">
<List
itemLayout="horizontal"
dataSource={humanEfficiencyRankingData}
renderItem={item => (
<List.Item actions={[<a key="list-loadmore-more">more</a>]}>
<List.Item.Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random"/>}
title={<a href="https://ant.design">{item.title}</a>}
description={item.description}
/>
<div>{item.rank}</div>
</List.Item>
)}
/>
</Tabs.TabPane>
<Tabs.TabPane tab="1600万≤指标3000万" key="6">
<List
itemLayout="horizontal"
dataSource={humanEfficiencyRankingData}
renderItem={item => (
<List.Item actions={[<a key="list-loadmore-more">more</a>]}>
<List.Item.Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random"/>}
title={<a href="https://ant.design">{item.title}</a>}
description={item.description}
/>
<div>{item.rank}</div>
</List.Item>
)}
/>
</Tabs.TabPane>
<Tabs.TabPane tab="指标>=3000万" key="7">
<List
itemLayout="horizontal"
dataSource={humanEfficiencyRankingData}
renderItem={item => (
<List.Item actions={[<a key="list-loadmore-more">more</a>]}>
<List.Item.Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random"/>}
title={<a href="https://ant.design">{item.title}</a>}
description={item.description}
/>
<div>{item.rank}</div>
</List.Item>
)}
/>
</Tabs.TabPane>
</Tabs>
</div>
</FrameBox>
</div>
</div>
<div className={styles.profitCenter}>
<div>
<FrameBox title="人工总成本">
<div className={styles.totalLaborCost} id="totalCost"/>
</FrameBox>
</div>
<div>
<FrameBox title="人均人工总成本">
<div className={styles.totalLaborCost} id="totalLaborCostPerCapita"/>
</FrameBox>
</div>
</div>
<div className={styles.profitBottom}>
<FrameBox title="人工利润架构图">
{/*@ts-ignore*/}
{/*<FlowAnalysisGraph {...laborProfitDataConfig}/>*/}
<OrgChart
pan
// zoom
// maxZoom={2}
// minZoom={0.5}
// zoomStep={0.02}
data={orgList} expandable
renderNode={(node) => {
const { key } = node;
return (key === 0 || key === 1 || key === 2 || key === 3 || key === 4 || key === 5) ?
<CompanyItem node={node}/> : <RegionItem node={node}/>;
}}
/>
</FrameBox>
</div>
</div>
);
};

@ -8,12 +8,13 @@ const EducationStaticsRedar = (echarts: any) => {
},
radar: {
// shape: 'circle',
// , axisLabel: { show: true }
indicator: [
{ name: "博士及以上", max: 800, axisLabel: { show: true } },
{ name: "大专以下", max: 800 },
{ name: "博士及以上", max: 800 },
{ name: "硕士", max: 800 },
{ name: "本科", max: 800 },
{ name: "大专", max: 800 },
{ name: "大专一下", max: 800 }
{ name: "大专", max: 800 }
],
splitArea: {
show: false
@ -28,7 +29,7 @@ const EducationStaticsRedar = (echarts: any) => {
type: "radar",
data: [
{
value: [210, 400, 800, 230, 180],
value: [100, 210, 400, 720, 230],
name: "学历统计"
}
],

@ -3,6 +3,10 @@ const GenderStaticsPie = (echarts: any) => {
const myChart = echarts.init(document.getElementById("genderStatics"));
// 配置项
const option = {
tooltip: {
show: true,
formatter: "{a} <br/>{b}: {c} ({d}%)"
},
legend: {
icon: "circle",
bottom: "0%",
@ -22,11 +26,19 @@ const GenderStaticsPie = (echarts: any) => {
{
name: "性别统计",
type: "pie",
startAngle:-180,
startAngle: -180,
radius: [40, 70],
roseType: "radius",
avoidLabelOverlap: false,
animation: false,
label: {
normal: {
formatter: "{d}%",
padding: [0, -25],
position: "outer" //文字显示在内部,如果在外边把这个去掉就好
}
},
labelLine: {
show: false
},
data: [

@ -60,7 +60,25 @@
}
.charts {
width: 100%;
height: 62px;
:global {
div {
width: 100% !important;
height: 100% !important;
}
}
}
}
.vCenter {
position: relative;
min-height: 62px;
& > div {
position: absolute;
top: 0;
}
}
@ -72,11 +90,17 @@
padding-top: 10px;
& > span {
img {
width: 8px;
margin-left: 8px;
margin-right: 4px;
vertical-align: middle;
}
& > span:first-child {
font-size: 12px;
color: #D4D4D4;
font-weight: 400;
margin-right: 8px;
}
& > span:last-child {

@ -6,6 +6,7 @@ const InternStaticsBar = (echarts: any) => {
xAxis: [
{
type: "value",
name: "人",
axisLabel: {
formatter: "{value} "
},
@ -31,7 +32,7 @@ const InternStaticsBar = (echarts: any) => {
grid: {
top: "10%",
left: "5%",
right: "5%",
right: "10%",
bottom: "3%",
containLabel: true
},
@ -39,7 +40,8 @@ const InternStaticsBar = (echarts: any) => {
{
type: "category",
offset: 0,
data: ["项目实习生", "销售实习生", "技术实习生", "大区实习生", "客服实习生"],
inverse:true,
data: ["项目实\n\n" + "习生", "销售实\n\n" + "习生", "技术实\n\n" + "习生", "支撑实\n\n" + "习生", "客服实\n\n" + "习生"],
axisPointer: {
type: "shadow"
},

@ -34,10 +34,10 @@ const LineStaticsPie = (echarts: any) => {
},
data: [
{ value: 40, name: "项目" },
{ value: 15, name: "市场" },
{ value: 20, name: "销售" },
{ value: 15, name: "支撑" },
{ value: 10, name: "销售1" }
{ value: 25, name: "技术" },
{ value: 10, name: "支撑" },
{ value: 5, name: "客服" }
]
}
]

@ -76,7 +76,7 @@ const MonthlyTrendsBar = (echarts: any) => {
showBackground: false,
barWidth: "8%",
barGap: "100%",
data: [250, 250, 320, 410, 280, 320, 405, 405, 320, 250, 280, 405],
data: [90, 70, 120, 60, 130, 80, 60, 125, 80, 60, 120, 40],
// bar 样式修改
itemStyle: {
barBorderRadius: 5,
@ -88,7 +88,7 @@ const MonthlyTrendsBar = (echarts: any) => {
type: "bar",
showBackground: false,
barWidth: "8%",
data: [370, 370, 260, 110, 390, 280, 110, 110, 370, 260, 390, 110],
data: [30, 50, 55, 25, 48, 30, 30, 75, 35, 20, 28, 10],
// bar 样式修改
itemStyle: {
barBorderRadius: 5,
@ -97,21 +97,9 @@ const MonthlyTrendsBar = (echarts: any) => {
},
{
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],
data: [90, 70, 120, 60, 130, 110, 60, 125, 80, 60, 120, 40],
itemStyle: {
normal: {
color: "#FFD700",

@ -5,7 +5,7 @@
* Date: 2023/5/5
*/
import React, { FC } from "react";
import { TinyArea } from "@ant-design/plots";
import { TinyArea, TinyColumn } from "@ant-design/plots";
import styles from "./index.less";
import cs from "classnames";
@ -15,7 +15,13 @@ interface OwnProps {
number: string;
unit: string;
color: string;
yoyVal?: string;
chainVal?: string;
yoyIcon?: any;
chainIcon?: any;
idx?: number;
tabList: Array<any>;
onTabChange?: any;
}
type Props = OwnProps;
@ -32,8 +38,22 @@ const config = {
showContent: false
}
};
const miniBarConfig = {
height: 60,
autoFit: false,
data: [274, 337, 81, 497, 666, 219, 269],
columnWidthRatio: 0.3,
tooltip: {
showCrosshairs: false,
showContent: false
}
};
const StructureCard: FC<Props> = (props) => {
const { active, tabList, title, number, unit, color } = props;
const { active, tabList, title, number, unit, color, yoyIcon, chainIcon, yoyVal, chainVal, idx, onTabChange } = props;
const classes = cs(styles["center"], {
[styles["vCenter"]]: (idx === 3 || idx === 4 || idx === 5)
});
return (
<div className={styles.structureCardWrapper}>
@ -41,25 +61,32 @@ const StructureCard: FC<Props> = (props) => {
<span>{title}</span>
<ul>
{
_.map(tabList, (it, idx) => {
_.map(tabList, (it, index) => {
const classes = cs({
[styles["active"]]: idx === active
[styles["active"]]: index === active
});
return <li key={it} className={classes}>{it}</li>;
return <li key={it} className={classes} onClick={() => onTabChange(idx, index)}>{it}</li>;
})
}
</ul>
</div>
<div className={styles.center}>
<div className={classes}>
<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 className={styles.charts}>
{
idx === 5 ? <TinyColumn {..._.assign(miniBarConfig, {
columnStyle: { fill: color }
})} /> :
<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>
<span><span></span>{yoyIcon && <img src={yoyIcon} alt=""/>}<span>{yoyVal}</span></span>
<span><span></span>{chainIcon && <img src={chainIcon} alt=""/>}<span>{chainVal}</span></span>
</div>
</div>
);

@ -20,7 +20,7 @@
border-radius: 5px;
background: #FFF;
max-height: 160px;
min-height: 100px;
min-height: 160px;
padding: 16px;
}
}

@ -7,8 +7,6 @@
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";
@ -50,12 +48,22 @@ const index: FC<Props> = (props) => {
};
}, []);
const handleChangeTab = (itemIdx: number, val: number) => {
setCard(_.map(card, (item, idex: number) => {
if (idex === itemIdx) {
return { ...item, active: val };
}
return { ...item };
}
));
};
return (
<div className={styles.structureWrapper}>
<ul className={styles.structureColumn_1}>
{
_.map(card, (it, idx) => {
return <li key={idx}><StructureCard {...it}/></li>;
return <li key={idx}><StructureCard {...it} idx={idx} onTabChange={handleChangeTab}/></li>;
})
}
</ul>

Loading…
Cancel
Save