diff --git a/src/assets/images/down.png b/src/assets/images/down.png new file mode 100644 index 0000000..dc4e1e1 Binary files /dev/null and b/src/assets/images/down.png differ diff --git a/src/assets/images/gjgwlzl.png b/src/assets/images/gjgwlzl.png new file mode 100644 index 0000000..0bc944b Binary files /dev/null and b/src/assets/images/gjgwlzl.png differ diff --git a/src/assets/images/hc.png b/src/assets/images/hc.png new file mode 100644 index 0000000..7893160 Binary files /dev/null and b/src/assets/images/hc.png differ diff --git a/src/assets/images/jyyglzl.png b/src/assets/images/jyyglzl.png new file mode 100644 index 0000000..0ff3877 Binary files /dev/null and b/src/assets/images/jyyglzl.png differ diff --git a/src/assets/images/ljh.jpg b/src/assets/images/ljh.jpg new file mode 100644 index 0000000..85ecf0c Binary files /dev/null and b/src/assets/images/ljh.jpg differ diff --git a/src/assets/images/lk.png b/src/assets/images/lk.png new file mode 100644 index 0000000..612f3ca Binary files /dev/null and b/src/assets/images/lk.png differ diff --git a/src/assets/images/lq.png b/src/assets/images/lq.png new file mode 100644 index 0000000..55ea2bc Binary files /dev/null and b/src/assets/images/lq.png differ diff --git a/src/assets/images/lt.png b/src/assets/images/lt.png new file mode 100644 index 0000000..4ae979f Binary files /dev/null and b/src/assets/images/lt.png differ diff --git a/src/assets/images/lwl.png b/src/assets/images/lwl.png new file mode 100644 index 0000000..41a535a Binary files /dev/null and b/src/assets/images/lwl.png differ diff --git a/src/assets/images/lzl.png b/src/assets/images/lzl.png new file mode 100644 index 0000000..06c628c Binary files /dev/null and b/src/assets/images/lzl.png differ diff --git a/src/assets/images/lzrs.png b/src/assets/images/lzrs.png new file mode 100644 index 0000000..4866ebe Binary files /dev/null and b/src/assets/images/lzrs.png differ diff --git a/src/assets/images/nyk.jpg b/src/assets/images/nyk.jpg new file mode 100644 index 0000000..5bc1218 Binary files /dev/null and b/src/assets/images/nyk.jpg differ diff --git a/src/assets/images/pjj.png b/src/assets/images/pjj.png new file mode 100644 index 0000000..1df6062 Binary files /dev/null and b/src/assets/images/pjj.png differ diff --git a/src/assets/images/pxglfx.png b/src/assets/images/pxglfx.png new file mode 100644 index 0000000..fd03e69 Binary files /dev/null and b/src/assets/images/pxglfx.png differ diff --git a/src/assets/images/qylzl.png b/src/assets/images/qylzl.png new file mode 100644 index 0000000..d95ee97 Binary files /dev/null and b/src/assets/images/qylzl.png differ diff --git a/src/assets/images/syr.png b/src/assets/images/syr.png new file mode 100644 index 0000000..7a50019 Binary files /dev/null and b/src/assets/images/syr.png differ diff --git a/src/assets/images/top_bottom.png b/src/assets/images/top_bottom.png new file mode 100644 index 0000000..86e62b4 Binary files /dev/null and b/src/assets/images/top_bottom.png differ diff --git a/src/assets/images/up.png b/src/assets/images/up.png new file mode 100644 index 0000000..8af1f24 Binary files /dev/null and b/src/assets/images/up.png differ diff --git a/src/assets/images/wyy.jpg b/src/assets/images/wyy.jpg new file mode 100644 index 0000000..2012b35 Binary files /dev/null and b/src/assets/images/wyy.jpg differ diff --git a/src/assets/images/ycl.png b/src/assets/images/ycl.png new file mode 100644 index 0000000..646d837 Binary files /dev/null and b/src/assets/images/ycl.png differ diff --git a/src/assets/images/ygf.png b/src/assets/images/ygf.png new file mode 100644 index 0000000..7230a80 Binary files /dev/null and b/src/assets/images/ygf.png differ diff --git a/src/assets/images/zbl.png b/src/assets/images/zbl.png new file mode 100644 index 0000000..6c47574 Binary files /dev/null and b/src/assets/images/zbl.png differ diff --git a/src/assets/images/zcy.png b/src/assets/images/zcy.png new file mode 100644 index 0000000..9345c94 Binary files /dev/null and b/src/assets/images/zcy.png differ diff --git a/src/assets/images/zcycd.png b/src/assets/images/zcycd.png new file mode 100644 index 0000000..01ff549 Binary files /dev/null and b/src/assets/images/zcycd.png differ diff --git a/src/assets/images/zd.png b/src/assets/images/zd.png new file mode 100644 index 0000000..9c5adac Binary files /dev/null and b/src/assets/images/zd.png differ diff --git a/src/assets/images/zkw.png b/src/assets/images/zkw.png new file mode 100644 index 0000000..f37f5e5 Binary files /dev/null and b/src/assets/images/zkw.png differ diff --git a/src/assets/images/zwz.jpg b/src/assets/images/zwz.jpg new file mode 100644 index 0000000..58c2843 Binary files /dev/null and b/src/assets/images/zwz.jpg differ diff --git a/src/assets/images/zy.png b/src/assets/images/zy.png new file mode 100644 index 0000000..f505455 Binary files /dev/null and b/src/assets/images/zy.png differ diff --git a/src/assets/images/zzrs.png b/src/assets/images/zzrs.png new file mode 100644 index 0000000..b8677d3 Binary files /dev/null and b/src/assets/images/zzrs.png differ diff --git a/src/components/reactChart/OrgChart.less b/src/components/reactChart/OrgChart.less new file mode 100644 index 0000000..dfe95c5 --- /dev/null +++ b/src/components/reactChart/OrgChart.less @@ -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; + } + } + } + } + } + } +} diff --git a/src/components/reactChart/OrgChart.tsx b/src/components/reactChart/OrgChart.tsx new file mode 100644 index 0000000..915abe3 --- /dev/null +++ b/src/components/reactChart/OrgChart.tsx @@ -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 ? ( + +
+ +
+
+ ) : null; +}; + +OrgChart.defaultProps = { + direction: "vertical", + expandAll: true, + expandable: false +}; + +export default OrgChart; diff --git a/src/components/reactChart/components/ChartWrapper.js b/src/components/reactChart/components/ChartWrapper.js new file mode 100644 index 0000000..f3eccd5 --- /dev/null +++ b/src/components/reactChart/components/ChartWrapper.js @@ -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 ( +
+
{ + 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} +
+
+ ); + } +} diff --git a/src/components/reactChart/components/OrgChartNode.tsx b/src/components/reactChart/components/OrgChartNode.tsx new file mode 100644 index 0000000..419a91d --- /dev/null +++ b/src/components/reactChart/components/OrgChartNode.tsx @@ -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(false); + + const handleExpandChange = () => { + const newExpanded = !expanded; + setExpanded(newExpanded); + onExpand?.(newExpanded, data); + }; + + const getOrgNodeRow = ( + span: number, + data: OrgChartNodeDataType + ): RowProps => { + const contentNode: React.ReactNode = ( +
+ {data.label} +
+ ); + + return [ + { + span, + content: ( + +
onClick?.(data)}> + {!!renderNode ? renderNode(data, contentNode) : contentNode} +
+ + ) + } + ]; + }; + + const getLineRow = (span: number): RowProps => { + return [ + { + span, + content: ( + + {expandable ? ( +
handleExpandChange()}>
+ ) : null} + + ) + } + ]; + }; + + const getChildrenLineRow = (span: number, itemSpan: number): RowProps => { + const cells: RowProps = []; + for (let index = 0; index < span; index = index + itemSpan) { + cells.push({ + span: 1, + content: ( + +   + + ) + }); + } + return cells; + }; + + const getChildrenNode = ( + datas: OrgChartNodeDataType[] = [], + itemSpan: number + ): RowProps => { + const cells: RowProps = []; + datas.forEach((data, index) => { + cells[itemSpan * index] = { + span: itemSpan, + content: ( + + + + ) + }; + }); + 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 ( + + + {rows.map((row, index) => ( + + ))} + +
+ ); +}; + +export default OrgChartNode; diff --git a/src/components/reactChart/components/OrgChartNodeRow.tsx b/src/components/reactChart/components/OrgChartNodeRow.tsx new file mode 100644 index 0000000..cb25fe2 --- /dev/null +++ b/src/components/reactChart/components/OrgChartNodeRow.tsx @@ -0,0 +1,27 @@ +import { OrgChartNodeProps, RowProps } from '../interface'; +import React from 'react'; + +export interface OrgChartNodeRowProps + extends Pick { + className?: string; + index: number; + row: RowProps; +} + +const OrgChartNodeRow = (props: OrgChartNodeRowProps) => { + const { className, index, row, direction } = props; + + return ( + + {row.map((cell, cellIndex) => { + return React.cloneElement(cell.content, { + key: cellIndex, + ...(direction === 'horizontal' ? { rowSpan: cell.span } : {}), + ...(direction === 'vertical' ? { colSpan: cell.span } : {}), + }); + })} + + ); +}; + +export default OrgChartNodeRow; diff --git a/src/components/reactChart/index.ts b/src/components/reactChart/index.ts new file mode 100644 index 0000000..122c096 --- /dev/null +++ b/src/components/reactChart/index.ts @@ -0,0 +1,5 @@ +import { OrgChartNodeDataType, OrgChartProps } from './interface'; +import OrgChart from './OrgChart'; + +export { OrgChartNodeDataType, OrgChartProps }; +export default OrgChart; diff --git a/src/components/reactChart/interface.ts b/src/components/reactChart/interface.ts new file mode 100644 index 0000000..722443e --- /dev/null +++ b/src/components/reactChart/interface.ts @@ -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 { + className?: string; + style?: React.CSSProperties; + pan?: boolean; + zoom?: boolean; + maxZoom?: number; + minZoom?: number; + zoomStep?: number; +} diff --git a/src/pages/personnelReport/components/frameBox.tsx b/src/pages/personnelReport/components/frameBox.tsx new file mode 100644 index 0000000..e5eb1b8 --- /dev/null +++ b/src/pages/personnelReport/components/frameBox.tsx @@ -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) => { + const { children, title } = props; + + return ( +
+
{title}
+ {children} +
+ ); +}; + +export default frameBox; diff --git a/src/pages/personnelReport/components/index.less b/src/pages/personnelReport/components/index.less new file mode 100644 index 0000000..c7360b0 --- /dev/null +++ b/src/pages/personnelReport/components/index.less @@ -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; + } + } + } +} diff --git a/src/pages/personnelReport/constants.js b/src/pages/personnelReport/constants.js index 0885a7b..3aff0eb 100644 --- a/src/pages/personnelReport/constants.js +++ b/src/pages/personnelReport/constants.js @@ -1,4 +1,4 @@ -import { G2, measureTextWidth } from "@ant-design/plots"; +import { G2 } from "@ant-design/plots"; const G = G2.getEngine("canvas"); @@ -6,25 +6,37 @@ export const structureCardList = [ { title: "当期总人数", active: 0, - number: 489, + number: 14338, unit: "人", color: "#FFBB31", + yoyVal: "4.75%", + chainVal: "1.75%", + yoyIcon: require("../../assets/images/down.png"), + chainIcon: require("../../assets/images/up.png"), tabList: ["本月", "上月"] }, { title: "本月入职", active: 0, - number: 100, + number: 120, unit: "人", color: "#3DA5F6", + yoyVal: "3.75%", + chainVal: "4.75%", + yoyIcon: require("../../assets/images/up.png"), + chainIcon: require("../../assets/images/down.png"), tabList: ["本月", "上月"] }, { title: "本月离职", active: 0, - number: 23, + number: 40, unit: "人", color: "#5FD5C7", + yoyVal: "4.75%", + chainVal: "4.75%", + yoyIcon: require("../../assets/images/down.png"), + chainIcon: require("../../assets/images/up.png"), tabList: ["本月", "上月"] }, { @@ -33,6 +45,10 @@ export const structureCardList = [ number: 238, unit: "人", color: "#2B6DF6", + yoyVal: "3.75%", + chainVal: "2.75%", + yoyIcon: require("../../assets/images/down.png"), + chainIcon: require("../../assets/images/up.png"), tabList: ["本年", "去年"] }, { @@ -41,6 +57,10 @@ export const structureCardList = [ number: 198, unit: "人", color: "#B571EA", + yoyVal: "5.75%", + chainVal: "1.75%", + yoyIcon: require("../../assets/images/down.png"), + chainIcon: require("../../assets/images/down.png"), tabList: ["本年", "去年"] }, { @@ -49,886 +69,752 @@ export const structureCardList = [ number: 28.0, unit: "岁", color: "#5FCC7B", + yoyVal: "4.75%", + chainVal: "4.75%", + yoyIcon: require("../../assets/images/down.png"), + chainIcon: require("../../assets/images/up.png"), tabList: ["本年", "去年"] } ]; - - -//人员流动数据 -export const flowingData = [ - { - type: "当期总人数", - value: 27 - }, - { - type: "本月入职", - value: 25 - }, - { - type: "本月离职", - value: 18 - }, - { - type: "年度累计入职", - value: 15 - }, +export const frameCard1List = [ { - type: "年度累计离职", - value: 10 - }, - { - type: "平均年龄", - value: 5 + title: "离职人数", + number: 40, + unit: "人", + color: "#FFD887", + isLine: true, + departureVal: "28人", + refusal: "12人", + icon: require("../../assets/images/lzrs.png") + }, + { + title: "离职率", + color: "", + icon: require("../../assets/images/lzl.png"), + number: "0.27", + unit: "%", + yoyIcon: require("../../assets/images/down.png"), + chainIcon: require("../../assets/images/up.png"), + yoyVal: "4.75%", + chainVal: "1.75%", + isLine: false + // title: "在职人数", + // number: 14338, + // unit: "人", + // color: "#78C0F9", + // isLine: true, + // yoyIcon: require("../../assets/images/down.png"), + // chainIcon: require("../../assets/images/up.png"), + // yoyVal: "4.75%", + // chainVal: "4.75%", + // icon: require("../../assets/images/zzrs.png") } ]; -//人员流动配置信息 -export const flowingConfig = { - appendPadding: 10, - data: flowingData, - angleField: "value", - colorField: "type", - radius: 1, - // 设置圆弧起始角度 - startAngle: Math.PI, - endAngle: Math.PI * 1.5, - label: { - type: "inner", - offset: "-2%", - content: "{name}", - style: { - fontSize: 15 +export const frameCard2List = [ + { + title: "全员离职率", + icon: require("../../assets/images/qylzl.png"), + bg: require("../../assets/images/qylzl_bg.png"), + yoyIcon: require("../../assets/images/down.png"), + chainIcon: require("../../assets/images/up.png"), + yoyVal: "2.75%", + chainVal: "4.75%", + actualValue: { + title: "实际值", + number: 0.22, + unit: "%" + }, + contemValue: { + title: "同期值", + number: 0.45, + unit: "%" } }, - interactions: [], - pieStyle: { - lineWidth: 0 - } -}; - -//司龄占比数据 -export const seniorityData = [ - { - type: "5年", - value: 100 - }, { - type: "10年", - value: 200 + title: "关键岗位离职率", + icon: require("../../assets/images/gjgwlzl.png"), + bg: require("../../assets/images/gjgwlzl_bg.png"), + yoyIcon: require("../../assets/images/down.png"), + chainIcon: require("../../assets/images/up.png"), + yoyVal: "2.32%", + chainVal: "5.67%", + actualValue: { + title: "实际值", + number: 0.32, + unit: "%" + }, + contemValue: { + title: "同期值", + number: 0.13, + unit: "%" + } }, { - type: "15年", - value: 300 + title: "绩优员工离职率", + icon: require("../../assets/images/jyyglzl.png"), + bg: require("../../assets/images/jyyglzl_bg.png"), + yoyIcon: require("../../assets/images/down.png"), + chainIcon: require("../../assets/images/up.png"), + yoyVal: "1.09%", + chainVal: "4.78%", + actualValue: { + title: "实际值", + number: 0.12, + unit: "%" + }, + contemValue: { + title: "同期值", + number: 0.23, + unit: "%" + } }, { - type: "其他", - value: 200 + title: "培训管理分析", + icon: require("../../assets/images/pxglfx.png"), + bg: require("../../assets/images/pxglfx_bg.png"), + actualValue: { + title: "人均培训费用", + number: "2,266.00", + unit: "元" + }, + contemValue: { + title: "人均培训时长", + number: 6, + unit: "小时" + } } ]; -//性别占比数据 -export const genderData = [ +export const personnelFlowTablist = ["全员离职率", "主动离职率", "被动离职率", "关键岗位离职率", "绩优员工离职率", "转正率"]; +export const personRankingTablist = ["销售人员有效金额排名", "销售新客户有效金额排名(2022-01-01前入职)", "销售新客户有效金额排名(2022-01-01后入职)"]; +export const trainingRankingList = [ { - type: "男", - value: 20 + avatar: require("../../assets/images/zcy.png"), + name: "赵晨雨", + dept: "南京EBU二部项目服务部", + score: "98分" }, { - type: "女", - value: 80 - } -]; -//学历占比数据 -export const degreeData = [ + avatar: require("../../assets/images/lk.png"), + name: "李坤(CZ)", + dept: "项目管理部-常州", + score: "94分" + }, { - type: "大专", - value: 45 + avatar: require("../../assets/images/zkw.png"), + name: "张开文", + dept: "青岛EBU一部", + score: "93分" }, { - type: "大专以下", - value: 35 + avatar: require("../../assets/images/zd.png"), + name: "张帝(ZZ)", + dept: "郑州EBU一部业务二部", + score: "92分" }, { - type: "全日制本科", - value: 11 + avatar: require("../../assets/images/pjj.png"), + name: "潘俊杰", + dept: "南京EBU九部项目服务部", + score: "90分" }, { - type: "硕士", - value: 6 + avatar: require("../../assets/images/lwl.png"), + name: "路卫亮", + dept: "河南易筑联合", + score: "88分" }, { - type: "研究生", - value: 1 + avatar: require("../../assets/images/lt.png"), + name: "刘彤(ZZ)", + dept: "项目三部-郑州", + score: "87分" }, { - type: "中专", - value: 1 + avatar: require("../../assets/images/syr.png"), + name: "申衍茹", + dept: "客户服务部-郑州", + score: "85分" }, { - type: "博士", - value: 1 + avatar: require("../../assets/images/ycl.png"), + name: "殷存磊", + dept: "济南EBU四部", + score: "85分" } ]; -//条线占比数据 -export const linesData = [ +export const teamRankingList = [ { - type: "条线1", - value: 35 + avatar: require("../../assets/images/zbl.png"), + name: "左保林", + dept: "上海EBU二部业务六部", + score: "290.40%" }, { - type: "条线2", - value: 25 + avatar: require("../../assets/images/ygf.png"), + name: "杨国福", + dept: "郑州EBU三部", + score: "75.50%" }, { - type: "条线3", - value: 15 + avatar: require("../../assets/images/zcycd.png"), + name: "张晨阳(承德)", + dept: "销售管理部-承德", + score: "71.04%" }, { - type: "条线4", - value: 17 + avatar: "", + name: "郭彦芳", + dept: "北京EBU十七部业务一部", + score: "58.18%" }, { - type: "条线5", - value: 8 + avatar: "", + name: "郭晓龙", + dept: "北京EBU一部业务五部", + score: "53.85%" } ]; -//实习生占比数据 -export const internData = [ +export const humanRankingList = [ { - type: "1", - value: 17 + avatar: require("../../assets/images/lq.png"), + name: "梁倩", + dept: "上海EBU二部销售管理部", + score: "4,260,845.00" }, { - type: "2", - value: 25 + avatar: require("../../assets/images/zy.png"), + name: "宗殷", + dept: "营销管理部-合肥", + score: "2,607,722.00" }, { - type: "3", - value: 8 + avatar: require("../../assets/images/hc.png"), + name: "黄超", + dept: "上海EBU十五部销售部", + score: "2,213,232.00" }, { - type: "4", - value: 35 + avatar: "", + name: "赵艳丽", + dept: "销售管理部-华南二部深圳EBU一部", + score: "2,164,522.00" }, { - type: "5", - value: 15 + avatar: "", + name: "左文钟", + dept: "上海EBU一部业务四部", + score: "2,038,707.00" } ]; -export const seniorityConfig = { - appendPadding: 10, - data: [], - angleField: "value", - colorField: "type", - radius: 0.75, - label: { - type: "inner", - offset: "-30%", - content: ({ percent }) => `${(percent * 100).toFixed(0)}%`, - style: { - fontSize: 14, - textAlign: "center" - } - }, - interactions: [] -}; - -//人员流动情况趋势图数据信息 -export const flowingTrendData = [ +export const humanRankingList_1 = [ { - "name": "入职", - "年份": "2022", - "年份流动信息": 18.9 + avatar: require("../../assets/images/zy.png"), + name: "宗殷", + dept: "营销管理部-合肥", + score: "1,933,749" }, { - "name": "入职", - "年份": "2023", - "年份流动信息": 28.8 + avatar: require("../../assets/images/hc.png"), + name: "黄超", + dept: "上海EBU十五部销售部", + score: "1,857,431" }, { - "name": "离职", - "年份": "2022", - "年份流动信息": 12.4 + avatar: require("../../assets/images/zwz.jpg"), + name: "左文钟", + dept: "上海EBU一部业务四部", + score: "1,609,645" }, { - "name": "离职", - "年份": "2023", - "年份流动信息": 23.2 + avatar: "", + name: "杨艳(BJ)", + dept: "北京EBU一部销售部", + score: "1,295,972" }, { - "name": "在职", - "年份": "2022", - "年份流动信息": 12.4 - }, - { - "name": "在职", - "年份": "2023", - "年份流动信息": 23.2 + avatar: "", + name: "赵礼欢", + dept: "上海EBU七部业务六部销售部", + score: "1,294,900" } ]; -//人员流动情况趋势图配置信息 -export const flowingTrendconfig = { - data: flowingTrendData, - 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" - } - ] - } -}; - - -//机构人员排名数数据 -export const institutionData = [ +export const humanRankingList_2 = [ { - stage: "机构一", - number: 253 + avatar: require("../../assets/images/nyk.jpg"), + name: "吕燕开", + dept: "北京EBU四部销售部", + score: "1,112,938" }, { - stage: "机构二", - number: 151 + avatar: require("../../assets/images/wyy.jpg"), + name: "吴洋洋", + dept: "北京EBU六部业务二部销售部", + score: "1,019,063" }, { - stage: "机构三", - number: 113 + avatar: require("../../assets/images/ljh.jpg"), + name: "罗家辉", + dept: "佛山EBU四部", + score: "737,100" }, { - stage: "机构四", - number: 87 + avatar: "", + name: "张宇航", + dept: "石家庄EBU四部销售部", + score: "681,590" }, { - stage: "机构五", - number: 59 + avatar: "", + name: "郑洪顺", + dept: "销售管理部-福州EBU二部", + score: "490,000" } ]; -//机构人员排名配置信息 -export const institutionConfig = { - data: institutionData, - xField: "stage", - yField: "number", - dynamicHeight: true - // legend: false -}; - -//人员异动分析数数据 -export const personChangeData = [ - { - type: "全员离职率", - value: 253 - }, - { - type: "关键岗位离职率", - value: 151 - }, - { - type: "绩优员工离职率", - value: 113 - } -]; -export const personChangeConfig = { - appendPadding: 10, - data: [], - angleField: "value", - colorField: "type", - radius: 0.75, - label: { - type: "spider", - labelHeight: 28, - content: "{name}\n{percentage}" - }, - interactions: [] -}; - -//详细图表柱状图 -export const multiplData = [ - { "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 teamAnalysisRankingsOptions = [ + { value: "1", label: "团队负责人(指标<400万)" }, + { value: "2", label: "团队负责人(400万<=指标<600万)" }, + { value: "3", label: "团队负责人(600万<=指标<800万)" }, + { value: "4", label: "团队负责人(800万<=指标<1200万)" }, + { value: "5", label: "团队负责人(1200万<=指标<1600万)" }, + { value: "6", label: "团队负责人(1600万<=指标<3000万)" }, + { value: "7", label: "团队负责人(指标>=3000万)" } ]; - -//人员流动情况趋势图配置信息 -export const multipleConfig = { - data: [], - xField: "city", - yField: "value", - seriesField: "type", - isGroup: true, - columnStyle: { - radius: [20, 20, 0, 0] - } +export const orgList = { + "key": 0, + "label": "泛微总公司", + "operatingIncome": "320165万元", + "operatingIncomePercent": "31.18%", + "operatingProfit": "120165万元", + "operatingProfitPercent": "33.18%", + "children": [ + { + "key": 1, "label": "第一集团军", + "operatingIncome": "20,165", + "operatingIncomePercent": "17.23%", + "operatingProfit": "10,165", + "operatingProfitPercent": "13.18%" + }, + { + "key": 2, "label": "第二集团军", + "operatingIncome": "30,165", + "operatingIncomePercent": "19.23%", + "operatingProfit": "20,165", + "operatingProfitPercent": "11.18%" + }, + { + "key": 3, "label": "第三集团军", + "operatingIncome": "21,065", + "operatingIncomePercent": "20.23%", + "operatingProfit": "12,789", + "operatingProfitPercent": "9.18%", + "children": [ + { + "key": 11, "label": "苏皖大区", + "operatingIncome": "9,665", + "operatingIncomePercent": "23.21%", + "operatingProfit": "6,665", + "operatingProfitPercent": "20.43%", + "children": [ + { + "key": 20, "label": "泛微南京", + "operatingIncome": "2,102", + "operatingIncomePercent": "23.45%", + "operatingProfit": "120,165", + "operatingProfitPercent": "12.45%" + }, + { + "key": 21, "label": "泛微苏州", + "operatingIncome": "1,398", + "operatingIncomePercent": "21.45%", + "operatingProfit": "120,165", + "operatingProfitPercent": "10.34%" + }, + { + "key": 22, "label": "泛微无锡", + "operatingIncome": "565.45", + "operatingIncomePercent": "3.45%", + "operatingProfit": "345.56", + "operatingProfitPercent": "3.56%" + }, + { + "key": 23, "label": "泛微常州", + "operatingIncome": "343.56", + "operatingIncomePercent": "2,76%", + "operatingProfit": "220,65", + "operatingProfitPercent": "2.76%" + }, + { + "key": 24, "label": "泛微南通", + "operatingIncome": "202.67", + "operatingIncomePercent": "2.45%", + "operatingProfit": "120,165", + "operatingProfitPercent": "2.12%" + }, + { + "key": 25, "label": "泛微扬州", + "operatingIncome": "240.11", + "operatingIncomePercent": "2.34%", + "operatingProfit": "201.34", + "operatingProfitPercent": "2.09%" + } + ] + }, + { + "key": 12, "label": "山东大区", + "operatingIncome": "1,165", + "operatingIncomePercent": "9.23%", + "operatingProfit": "1,003", + "operatingProfitPercent": "6.18%" + }, + { + "key": 13, "label": "山西大区", + "operatingIncome": "7,135", + "operatingIncomePercent": "17.23%", + "operatingProfit": "6,365", + "operatingProfitPercent": "12.18%" + }, + { + "key": 14, "label": "河南大区", + "operatingIncome": "20,165", + "operatingIncomePercent": "19.23%", + "operatingProfit": "9,455", + "operatingProfitPercent": "12.18%" + } + ] + }, + { + "key": 4, "label": "第四集团军", + "operatingIncome": "19,165", + "operatingIncomePercent": "9.98%", + "operatingProfit": "9,165", + "operatingProfitPercent": "4.98%" + }, + { + "key": 5, "label": "第五集团军", + "operatingIncome": "20,987", + "operatingIncomePercent": "13.23%", + "operatingProfit": "12,995", + "operatingProfitPercent": "8.98%" + } + ] }; -//培训管理分析数据 -export const trainingData = [ - { - type: "人均培训费用", - value: 20 - }, - { - type: "人均培训时长", - value: 80 - } -]; - -//培训排名 -export const rankingData = [ - { - label: "财务部", - type: "前五", - value: 2800 - }, - { - label: "销售部", - type: "前五", - value: 1800 - }, - { - label: "策划部", - type: "前五", - value: 950 - }, - { - label: "运营部", - type: "前五", - value: 500 - }, - { - label: "后勤部", - type: "前五", - value: 170 - }, - { - label: "财务部1", - type: "后五", - value: 2260 - }, - { - label: "销售部1", - type: "后五", - value: 1300 - }, - { - label: "策划部1", - type: "后五", - value: 900 - }, - { - label: "运营部1", - type: "后五", - value: 390 - }, - { - label: "后勤部1", - type: "后五", - value: 100 - } -]; -export const rankingConfig = { - data: [], - isGroup: true, - xField: "value", - yField: "label", - seriesField: "type", - dodgePadding: 4, - label: { - // 可手动配置 label 数据标签位置 - position: "middle", - // 'left', 'middle', 'right' - // 可配置附加的布局方法 - layout: [ - // 柱形图数据标签位置自动调整 - { - type: "interval-adjust-position" - }, // 数据标签防遮挡 - { - type: "interval-hide-overlap" - }, // 数据标签文颜色自动调整 - { - type: "adjust-color" +export const laborProfitData = { + nodes: [ + { + id: "-1", + value: { + title: "泛微总公司", + items: [ + { + text: "泛微总公司" + } + ] } - ] - } -}; - -//人效分析数据 -export const humanAnalysisData = [ - { - "name": "销售投入回报", - "company": "公司1", - "value": 18.9 - }, - { - "name": "销售投入回报", - "company": "公司2", - "value": 28.8 - }, - { - "name": "人均利润", - "company": "公司1", - "value": 12.4 - }, - { - "name": "人均利润", - "company": "公司2", - "value": 23.2 - }, - { - "name": "人均销售额", - "company": "公司1", - "value": 12.4 - }, - { - "name": "人均销售额", - "company": "公司2", - "value": 23.2 - } -]; -export const humanAnalysisconfig = { - data: humanAnalysisData, - isGroup: true, - xField: "company", - yField: "value", - 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" + }, + { + id: "00", + value: { + title: "第一集团军", + items: [ + { + text: "第一集团军" + } + ] } - ] - } -}; -//人效排名数据 -export const humanEfficiencyRankingData = [ - { - title: "员工1", - description: "公司1", - rank: 1 - }, - { - title: "员工2", - description: "公司2", - rank: 2 - }, - { - title: "员工3", - description: "公司3", - rank: 3 - }, - { - title: "员工4", - description: "公司4", - rank: 4 - } -]; -//销售新客户合同数量排名 -export const saleEfficiencyRankingData = [ - { - title: "员工5", - description: "公司1", - rank: 1 - }, - { - title: "员工6", - description: "公司2", - rank: 2 - }, - { - title: "员工7", - description: "公司3", - rank: 3 - }, - { - title: "员工8", - description: "公司4", - rank: 4 - } -]; -//新销售有效合同 -export const newSaleEfficiencyRankingData = [ - { - title: "员工9", - description: "公司1", - rank: 1 - }, - { - title: "员工10", - description: "公司2", - rank: 2 - }, - { - title: "员工11", - description: "公司3", - rank: 3 - }, - { - title: "员工12", - description: "公司4", - rank: 4 - } -]; - - -//人工总成本数据 -export const totalLaborCostdata = [ - { - time: "Jan.", - value: 350, - count: 800 - }, - { - time: "Feb.", - value: 900, - count: 600 - }, - { - time: "Mar.", - value: 300, - count: 400 - }, - { - time: "Apr.", - value: 450, - count: 380 - }, - { - time: "May.", - value: 470, - count: 220 - }, - { - time: "Jun.", - value: 470, - count: 220 - }, - { - time: "Jul.", - value: 470, - count: 220 - }, - { - time: "Aug.", - value: 470, - count: 220 - } -]; -export const totalLaborCostConfig = { - data: [totalLaborCostdata, totalLaborCostdata], - xField: "time", - yField: ["value", "count"], - yAxis: { - // 格式化左坐标轴 - value: { - min: 0, - label: { - formatter: (val) => `${val}W` + }, + { + id: "01", + value: { + title: "第二集团军", + items: [ + { + text: "第二集团军" + } + ] } }, - // 隐藏右坐标轴 - count: false - }, - geometryOptions: [ { - geometry: "column", - color: "#5B8FF9", - columnWidthRatio: 0.4, - label: { - position: "middle" + id: "02", + value: { + title: "第三集团军", + items: [ + { + text: "第三集团军" + } + ] } }, { - geometry: "line", - smooth: true, - color: "#5AD8A6" - } - ], - interactions: [], - annotations: { - value: [ - { - type: "text", - position: ["2019-06", "max"], - content: "柱线混合图" + id: "03", + value: { + title: "第四集团军", + items: [ + { + text: "第四集团军" + } + ] } - ], - count: [ - { - type: "dataMarker", - top: true, - position: ["2019-05", 400], - line: { - length: 20 - }, - text: { - content: "", - style: { - textAlign: "left" + }, + { + id: "04", + value: { + title: "第五集团军", + items: [ + { + text: "第五集团军" } - } + ] } - ] - } -}; - -//人均收入配置数据 1、人均固定收入 2、人均浮动收入 -export const fixedIncomePerData = [ - { - type: "销售", - value: 27 - }, - { - type: "项目", - value: 25 - }, - { - type: "开发", - value: 18 - }, - { - type: "客服", - value: 15 - }, - { - type: "其他", - value: 10 - }, - { - type: "其他", - value: 5 - } -]; -export const floatingIncomePerData = [ - { - type: "销售", - value: 18 - }, - { - type: "项目", - value: 29 - }, - { - type: "开发", - value: 23 - }, - { - type: "客服", - value: 3 - }, - { - type: "其他", - value: 22 - }, - { - type: "其他", - value: 5 - } -]; -//人均收入配置信息 -export const perCapitaIncomeConfig = { - appendPadding: 10, - data: [], - angleField: "value", - colorField: "type", - radius: 1, - innerRadius: 0.64, - meta: { - value: { - formatter: (v) => `${v} ¥` - } - }, - label: { - type: "inner", - offset: "-50%", - style: { - textAlign: "center" - }, - autoRotate: false, - content: "{value}" - }, - statistic: { - title: { - offsetY: -4, - customHtml: (container, view, datum) => { - const { width, height } = container.getBoundingClientRect(); - const d = Math.sqrt(Math.pow(width / 2, 2) + Math.pow(height / 2, 2)); - //总计 - const text = datum ? datum.type : ""; - return renderStatistic(d, text, { - fontSize: 28 - }); + }, + { + id: "1", + value: { + title: "苏皖大区", + items: [ + { + text: "苏皖大区" + } + ] } }, - content: { - offsetY: 4, - style: { - 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 - }); + { + id: "2", + value: { + title: "山东大区", + items: [ + { + text: "山东大区" + } + ] } - } - }, - // 添加 中心统计文本 交互 - interactions: [] -}; - - -//人均人工成本数据 -export const laborCostsPerCapitaData = [ - { - year: "Jan.", - value: 3, - count: 10 - }, - { - year: "Feb.", - value: 4, - count: 4 - }, - { - year: "Mar.", - value: 3.5, - count: 5 - }, - { - year: "Apr.", - value: 5, - count: 5 - }, - { - year: "May.", - value: 4.9, - count: 4.9 - }, - { - year: "Jun.", - value: 6, - count: 35 - }, - { - year: "Jul.", - value: 7, - count: 7 - }, - { - year: "Aug.", - value: 9, - count: 1 - } -]; -export const laborCostsPerCapitaConfig = { - data: [laborCostsPerCapitaData, laborCostsPerCapitaData], - xField: "year", - yField: ["value", "count"], - geometryOptions: [ - { - geometry: "line", - smooth: true, - color: "#5B8FF9", - label: { - formatter: (datum) => { - return `${datum.value}w`; - } - }, - lineStyle: { - lineWidth: 3, - lineDash: [5, 5] + }, + { + id: "3", + value: { + title: "山西大区", + items: [ + { + text: "山西大区" + } + ] } }, { - geometry: "line", - smooth: true, - color: "#5AD8A6", - lineStyle: { - lineWidth: 4, - opacity: 0.5 - }, - label: { - formatter: (datum) => { - return `${datum.count}w`; - } - }, - point: { - shape: "circle", - size: 4, - style: { - opacity: 0.5, - stroke: "#5AD8A6", - fill: "#fff" - } + id: "4", + value: { + title: "河南大区", + items: [ + { + text: "河南大区" + } + ] + } + }, + { + id: "6", + value: { + title: "泛微南京", + items: [ + { + text: "泛微南京" + } + ] + } + }, + { + id: "7", + value: { + title: "泛微苏州", + items: [ + { + text: "泛微苏州" + } + ] } + }, + { + id: "8", + value: { + title: "泛微无锡", + items: [ + { + text: "泛微无锡" + } + ] + } + }, + { + id: "9", + value: { + title: "泛微常州", + items: [ + { + text: "泛微常州" + } + ] + } + }, + { + id: "10", + value: { + title: "泛微南通", + items: [ + { + text: "泛微南通" + } + ] + } + }, + { + id: "11", + value: { + title: "泛微扬州", + items: [ + { + text: "泛微扬州" + } + ] + } + } + ], + edges: [ + { + source: "-1", + target: "00", + value: "" + }, + { + source: "-1", + target: "01", + value: "" + }, + { + source: "-1", + target: "02", + value: "" + }, + { + source: "-1", + target: "03", + value: "" + }, + { + source: "-1", + target: "04", + value: "" + }, + { + source: "02", + target: "1" + }, + { + source: "02", + target: "2" + }, + { + source: "02", + target: "3" + }, + { + source: "02", + target: "4" + }, + { + source: "1", + target: "6" + }, + { + source: "1", + target: "7" + }, + { + source: "1", + target: "8" + }, + { + source: "1", + target: "9" + }, + { + source: "1", + target: "10" + }, + { + source: "1", + target: "11" } ] }; - -function renderStatistic(containerWidth, text, style) { - const { width: textWidth, height: textHeight } = measureTextWidth(text, style); - const R = containerWidth / 2; // r^2 = (w / 2)^2 + (h - offsetY)^2 - - let scale = 1; - - if (containerWidth < textWidth) { - scale = Math.min(Math.sqrt(Math.abs(Math.pow(R, 2) / (Math.pow(textWidth / 2, 2) + Math.pow(textHeight, 2)))), 1); - } - - const textStyleStr = `width:${containerWidth}px;`; - return `
${text}
`; -} - - +export const laborProfitDataConfig = { + data: laborProfitData, + layout: { + rankdir: "TB", + ranksepFunc: () => 20, + nodesepFunc: () => 30 + }, + nodeCfg: { + anchorPoints: [ + [0.5, 0], + [0.5, 1] + ], + customContent: (item, group, cfg) => { + console.log(item, group, cfg); + const { startX, startY, width } = cfg; + const { text, value, icon } = item; + let textShape, valueShape, iconShape; + textShape = + text && group.addShape("text", { + attrs: { + textBaseline: "top", + x: startX, + y: startY, + text, + fill: "#f00" + }, + // group 内唯一字段 + name: `text-${Math.random()}` + }); + valueShape = + value && + group.addShape("text", { + attrs: { + textBaseline: "top", + x: startX + width / 2, + y: startY, + text: value, + fill: "#f00" + }, + name: `value-${Math.random()}` + }); + iconShape = + icon && + group.addShape("image", { + attrs: { + x: startX, + y: startY, + width: 72, + height: 72, + img: icon + }, + name: `image-${Math.random()}` + }); + return Math.max( + textShape?.getBBox().height ?? 0, + valueShape?.getBBox().height ?? 0, + iconShape?.getBBox().height ?? 0 + ); + } + }, + edgeCfg: { + type: "polyline", + endArrow: true + }, + markerCfg: (cfg) => { + return { + position: "bottom", + show: laborProfitData.edges.filter((item) => item.source === cfg.id)?.length + }; + }, + behaviors: ["drag-canvas", "zoom-canvas"] +}; diff --git a/src/pages/personnelReport/peopleFlowTable/components/frameCard1.tsx b/src/pages/personnelReport/peopleFlowTable/components/frameCard1.tsx new file mode 100644 index 0000000..0e2f408 --- /dev/null +++ b/src/pages/personnelReport/peopleFlowTable/components/frameCard1.tsx @@ -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) => { + const { title, number, unit, color, icon, isLine, yoyIcon, chainIcon, yoyVal, chainVal, departureVal, refusal } = props; + + return ( +
+
+
{title}
+
{number}{unit}
+
+
+ { + isLine ? + : + } +
+
+ { + yoyIcon ? <> + 同比:{yoyIcon && }{yoyVal} + 环比:{chainIcon && }{chainVal} + :<> + 主动离职:{yoyIcon && }{departureVal} + 辞退:{chainIcon && }{refusal} + + } +
+
+ ); +}; + +export default FrameCard1; diff --git a/src/pages/personnelReport/peopleFlowTable/components/frameCard2.tsx b/src/pages/personnelReport/peopleFlowTable/components/frameCard2.tsx new file mode 100644 index 0000000..2f7adb7 --- /dev/null +++ b/src/pages/personnelReport/peopleFlowTable/components/frameCard2.tsx @@ -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) => { + const { title, icon, actualValue, contemValue, yoyIcon, chainIcon, yoyVal, chainVal } = props; + + return ( +
+
+
{title}
+ +
+
+
{actualValue?.title} +
{actualValue?.number}{actualValue?.unit}
+
+
{contemValue?.title} +
{contemValue?.number}{contemValue?.unit}
+
+
+ { + yoyIcon && +
+ 同比:{yoyIcon && }{yoyVal} + 环比:{chainIcon && }{chainVal} +
+ } + +
+ ); +}; + +export default FrameCard2; diff --git a/src/pages/personnelReport/peopleFlowTable/components/index.less b/src/pages/personnelReport/peopleFlowTable/components/index.less new file mode 100644 index 0000000..63ceb4a --- /dev/null +++ b/src/pages/personnelReport/peopleFlowTable/components/index.less @@ -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; + } + } + } +} diff --git a/src/pages/personnelReport/peopleFlowTable/components/personnelFlowCharts.ts b/src/pages/personnelReport/peopleFlowTable/components/personnelFlowCharts.ts new file mode 100644 index 0000000..2c23231 --- /dev/null +++ b/src/pages/personnelReport/peopleFlowTable/components/personnelFlowCharts.ts @@ -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 + "
"; + for (let item of params) { + str += item.seriesName + " : " + item.value + "%
"; + } + 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) => { + 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));// 小数点后两位百分比 +}; diff --git a/src/pages/personnelReport/peopleFlowTable/components/trendsInSeparationsCharts.ts b/src/pages/personnelReport/peopleFlowTable/components/trendsInSeparationsCharts.ts new file mode 100644 index 0000000..1a5c481 --- /dev/null +++ b/src/pages/personnelReport/peopleFlowTable/components/trendsInSeparationsCharts.ts @@ -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; diff --git a/src/pages/personnelReport/peopleFlowTable/index.less b/src/pages/personnelReport/peopleFlowTable/index.less index 4ac2fa7..c246cc0 100644 --- a/src/pages/personnelReport/peopleFlowTable/index.less +++ b/src/pages/personnelReport/peopleFlowTable/index.less @@ -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; + } + } + } + } + } } diff --git a/src/pages/personnelReport/peopleFlowTable/index.tsx b/src/pages/personnelReport/peopleFlowTable/index.tsx index 6fdbba3..b9981ba 100644 --- a/src/pages/personnelReport/peopleFlowTable/index.tsx +++ b/src/pages/personnelReport/peopleFlowTable/index.tsx @@ -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) => { - + const [active, setActive] = useState(0); + useEffect(() => { + const personnelFlowBar = PersonnelFlowBar(echarts); + const trendsInSeparationsCharts = TrendsInSeparationsCharts(echarts); + // 屏幕缩放对chart图表进行自适应处理,调用实例的resize方法 + window.onresize = () => { + personnelFlowBar.resize(); + trendsInSeparationsCharts.resize(); + }; + return () => { + }; + }, []); return (
+
    +
  • + +
  • + {_.map(frameCard2List, (item: any, index: number) => { + return ( +
  • + +
  • + ); + })} +
+
+
+
+
+
    + {_.map(frameCard1List, (item: any, index: number) => { + return ( +
  • + +
  • + ); + })} +
+
+
    + { + _.map(personnelFlowTablist, (item: any, index: number) => { + const classes = cs({ + [styles["active"]]: index === active + }); + return
  • setActive(index)}>{item}
  • ; + }) + } +
+
+
+
+ +
+
+ +
+ +
+
+
+ +
+ ( + + + { + (index === 0 || index === 1 || index === 2) ? + : + {index + 1} + } +
+ + {item.name}{item.dept} +
+
+ } + /> +
{item.score}
+ + )} + /> +
+ +
+
); }; diff --git a/src/pages/personnelReport/talentProfitTable/components/companyItem.tsx b/src/pages/personnelReport/talentProfitTable/components/companyItem.tsx new file mode 100644 index 0000000..dee215b --- /dev/null +++ b/src/pages/personnelReport/talentProfitTable/components/companyItem.tsx @@ -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) => { + const { node } = props; + const classes = classNames(styles["header"], { + [styles["parent"]]: node.key === 0 + }); + return ( +
+
{node?.label}
+
+
+
+ 营业收入 + {node?.operatingIncome} +
+
+ 同比 + {node?.operatingIncomePercent} +
+
+
+
+ 利润总额 + {node?.operatingProfit} +
+
+ 同比 + {node?.operatingProfitPercent} +
+
+
+
+ ); +}; + +export default CompanyItem; diff --git a/src/pages/personnelReport/talentProfitTable/components/incomStaticsRadar.ts b/src/pages/personnelReport/talentProfitTable/components/incomStaticsRadar.ts new file mode 100644 index 0000000..88ad2c6 --- /dev/null +++ b/src/pages/personnelReport/talentProfitTable/components/incomStaticsRadar.ts @@ -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; diff --git a/src/pages/personnelReport/talentProfitTable/components/index.less b/src/pages/personnelReport/talentProfitTable/components/index.less new file mode 100644 index 0000000..156c6ef --- /dev/null +++ b/src/pages/personnelReport/talentProfitTable/components/index.less @@ -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; +} diff --git a/src/pages/personnelReport/talentProfitTable/components/regionItem.tsx b/src/pages/personnelReport/talentProfitTable/components/regionItem.tsx new file mode 100644 index 0000000..50378cb --- /dev/null +++ b/src/pages/personnelReport/talentProfitTable/components/regionItem.tsx @@ -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) => { + 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 ( +
+
{node?.label}
+
+
+
+ 营业收入 + {node?.operatingIncome} +
+
+ 同比 + {node?.operatingIncomePercent} +
+
+
+
+ 利润总额 + {node?.operatingProfit} +
+
+ 同比 + {node?.operatingProfitPercent} +
+
+
+
+ ); +}; + +export default RegionItem; diff --git a/src/pages/personnelReport/talentProfitTable/components/totalCostCharts.ts b/src/pages/personnelReport/talentProfitTable/components/totalCostCharts.ts new file mode 100644 index 0000000..a943a10 --- /dev/null +++ b/src/pages/personnelReport/talentProfitTable/components/totalCostCharts.ts @@ -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; diff --git a/src/pages/personnelReport/talentProfitTable/components/totalLaborCostPerCapita.ts b/src/pages/personnelReport/talentProfitTable/components/totalLaborCostPerCapita.ts new file mode 100644 index 0000000..c050938 --- /dev/null +++ b/src/pages/personnelReport/talentProfitTable/components/totalLaborCostPerCapita.ts @@ -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; diff --git a/src/pages/personnelReport/talentProfitTable/index.less b/src/pages/personnelReport/talentProfitTable/index.less index e88969a..c043ad4 100644 --- a/src/pages/personnelReport/talentProfitTable/index.less +++ b/src/pages/personnelReport/talentProfitTable/index.less @@ -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%; + // } + //} } } diff --git a/src/pages/personnelReport/talentProfitTable/index.tsx b/src/pages/personnelReport/talentProfitTable/index.tsx index 5244dc9..53b50b0 100644 --- a/src/pages/personnelReport/talentProfitTable/index.tsx +++ b/src/pages/personnelReport/talentProfitTable/index.tsx @@ -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) => { - + const [active, setActive] = useState(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 (
-
-
- 人效分析 - {/*@ts-ignore*/} - -
-
- 人均收入 - - - - - - - - +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
调查类型实际值较去年同期
销售收入投资回报 +
+ 3016万元 + 同期值3457 +
+
+ + 4.75% +
人均净利润 +
+ 17456 + 同期值13567 +
+
+ + 4.75% +
人均销售额 +
+ 34.6万元 + 同期值45.16 +
+
+ + 4.75% +
+
+
-
- 人工成本 - - - {/*@ts-ignore*/} - - - - - - +
+ +
+
-
-
- 人效分析排名 - - - ( - more]}> - } - title={{item.title}} - description={item.description} - /> -
{item.rank}
-
- )} + +
+