From 7b59ba7cf052727fabd54aa691ba55ce4405e314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=8E=E6=B0=B8=E9=A1=BA?= <971387674@qq.com> Date: Fri, 27 Oct 2023 16:55:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B3=9B=E5=BE=AE=E8=96=AA=E8=B5=84=E6=A0=B8?= =?UTF-8?q?=E7=AE=97iframe=E8=A1=A8=E6=A0=BC-=E8=96=AA=E8=B5=84=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E6=8B=93=E6=89=91=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 + public/css/iconfont/demo_index.html | 98 ++++++- public/css/iconfont/iconfont.css | 22 +- public/css/iconfont/iconfont.js | 2 +- public/css/iconfont/iconfont.json | 32 +- public/css/iconfont/iconfont.ttf | Bin 2248 -> 4028 bytes public/css/iconfont/iconfont.woff | Bin 167836 -> 2480 bytes public/css/iconfont/iconfont.woff2 | Bin 129256 -> 1972 bytes src/api/calculate.service.ts | 4 + src/layouts/config.js | 1 + src/pages/atdTable/components/index.less | 11 +- src/pages/calcTable/calcExplainFooter.tsx | 4 + src/pages/calcTable/customTableTitle.tsx | 2 + src/pages/calcTable/index.tsx | 17 +- src/pages/salaryItemDiagram/data.js | 125 ++++++++ src/pages/salaryItemDiagram/index-g6.tsx | 156 ++++++++++ src/pages/salaryItemDiagram/index.tsx | 168 +++++++++++ src/pages/salaryItemDiagram/registerShape.ts | 292 +++++++++++++++++++ src/pages/welfareLedgerTable/index.tsx | 8 +- 19 files changed, 929 insertions(+), 15 deletions(-) create mode 100644 src/pages/salaryItemDiagram/data.js create mode 100644 src/pages/salaryItemDiagram/index-g6.tsx create mode 100644 src/pages/salaryItemDiagram/index.tsx create mode 100644 src/pages/salaryItemDiagram/registerShape.ts diff --git a/package.json b/package.json index 57f28d1..299e483 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,9 @@ ] }, "dependencies": { + "@ant-design/charts": "^1.4.2", "@ant-design/pro-layout": "6.32.1", + "@antv/g6": "^4.8.17", "@types/lodash": "^4.14.172", "@ztree/ztree_v3": "^3.5.42", "ahooks": "^3.1.3", diff --git a/public/css/iconfont/demo_index.html b/public/css/iconfont/demo_index.html index 7a1168c..9986cd4 100644 --- a/public/css/iconfont/demo_index.html +++ b/public/css/iconfont/demo_index.html @@ -47,11 +47,37 @@
  • Symbol
  • + 查看项目 +
    diff --git a/src/pages/calcTable/index.tsx b/src/pages/calcTable/index.tsx index a0c2c03..8e2c4c9 100644 --- a/src/pages/calcTable/index.tsx +++ b/src/pages/calcTable/index.tsx @@ -14,6 +14,7 @@ import type { ColumnType } from "antd/lib/table"; import type { PaginationData } from "rc-pagination"; import { exceptStr, paginationFun } from "@/utils/common"; import { IPage } from "@/common/types"; +import Icon from "../../lib/CustomIcon"; import styles from "@/pages/atdTable/components/index.less"; interface OwnProps { @@ -68,11 +69,14 @@ const index: FunctionComponent = (props) => { ...item, title: , children: convertColumns(_.map(item.children, o => ({ ...o, i18n: item.i18n }))), className: styles["td_odd"], i18n: item.i18n, - render: (text: string) => ( + render: (text: string, record: any) => ( {text} + handleDiagramTd(item?.dataIndex, record?.id)} + /> { - item.lockStatus === "LOCK" ? : null + item.lockStatus === "LOCK" ? : null } ) @@ -80,6 +84,15 @@ const index: FunctionComponent = (props) => { } }); }; + const handleDiagramTd = (salaryItemId: string, acctEmpId: string) => { + window.parent.postMessage( + { + type: "turn", + payload: { id: "DIAGRAM", params: { salaryItemId, acctEmpId } } + }, + "*" + ); + }; const handleFormulaTd = (dataIndex: string) => { window.parent.postMessage( { diff --git a/src/pages/salaryItemDiagram/data.js b/src/pages/salaryItemDiagram/data.js new file mode 100644 index 0000000..fb261f4 --- /dev/null +++ b/src/pages/salaryItemDiagram/data.js @@ -0,0 +1,125 @@ +export const data = { + id: "root", + label: "利息收入", + subLabel: "3,283.456", + ratio: 3, + children: [ + { + id: "child-a", + label: "平均利息", + subLabel: "9%", + ratio: 1, + increase: true + }, + { + id: "child-b", + label: "贷款余额", + subLabel: "1,789,567", + ratio: 23, + increase: true, + children: [ + { + id: "child-b-a", + label: "投放金额", + subLabel: "2,385,124", + ratio: 17, + increase: true, + operator: "" + }, + { + id: "child-b-b", + label: "还款金额", + subLabel: "595,557", + ratio: 12, + increase: true, + children:[ + { + id: 'child-b-b-a', + label: '还款期限', + subLabel: '7', + ratio: 23, + increase: true, + children:[ + { + id: "child-b-b-a-a", + label: "还款金额", + subLabel: "595,557", + ratio: 12, + increase: true, + children:[ + { + id: "child-b-b-a-a-a", + label: "还款金额", + subLabel: "595,557", + ratio: 12, + increase: true, + children:[ + { + id: "child-b-b-a-a-a-a", + label: "还款金额", + subLabel: "595,557", + ratio: 12, + increase: true, + children:[ + { + id: "child-b-b-a-a-a-a-a", + label: "还款金额", + subLabel: "595,557", + ratio: 12, + increase: true, + children:[ + { + id: "child-b-b-a-a-a-a-a-a", + label: "还款金额", + subLabel: "595,557", + ratio: 12, + increase: true, + children:[ + { + id: "child-b-b-a-a-a-a-a-a-a", + label: "还款金额", + subLabel: "595,557", + ratio: 12, + increase: true, + children:[ + { + id: "child-b-b-a-a-a-a-a-a-a-a", + label: "还款金额", + subLabel: "595,557", + ratio: 12, + increase: true, + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + id: 'child-b-b-6', + label: '还款期限', + subLabel: '7', + ratio: 23, + increase: true, + } + ] + } + ] + }, + { + id: "child-c", + label: "还款期限", + subLabel: "7", + ratio: 23, + increase: false + } + ] +}; diff --git a/src/pages/salaryItemDiagram/index-g6.tsx b/src/pages/salaryItemDiagram/index-g6.tsx new file mode 100644 index 0000000..a9bb328 --- /dev/null +++ b/src/pages/salaryItemDiagram/index-g6.tsx @@ -0,0 +1,156 @@ +/* + * Author: 黎永顺 + * name: 薪资项目-拓扑图 + * Description: + * Date: 2023/10/19 + */ +import React, { FunctionComponent, useEffect, useRef } from "react"; +// @ts-ignore +import G6 from "@antv/g6"; +import API from "@/api"; +// @ts-ignore +import uuidV4 from "uuid/v4"; +import "./registerShape"; + +interface OwnProps { +} + +type Props = OwnProps; + +const index: FunctionComponent = (props) => { + const ref: any = useRef(null); + let graph: any = null; + useEffect(() => { + initG6().then(r => { + }); + return () => { + }; + }, []); + // 1688699208883 + const initG6 = async () => { + const { data: { data: result } }: any = await API.CalculateService.getSalarysobItemTopology({ + salarySobId: "31", + salaryItemId: "1688699208872" + }); + const dataSource = { + id: result.salaryItemId.toString(), label: result.salaryItemName || "", + subLabel: result.result || "", formula: result.formula, level: 0, + children: !_.isEmpty(result.children) ? traverse(result.children) : [] + }; + const width = ref.current.scrollWidth; + const height = ref.current.scrollHeight; + // @ts-ignore + G6.Util.traverseTree(dataSource, (subtree: any) => { + // if (subtree.level === undefined) subtree.level = 0; + // subtree.children?.forEach((child: any) => child.level = subtree.level + 1); + switch (subtree.level) { + case 0: + subtree.type = "root"; + break; + case 1: + subtree.type = "level1node"; + // subtree.collapsed = true; + break; + default: + subtree.type = "othernode"; + } + }); + if (!graph) { + graph = new G6.TreeGraph({ + // fitCenter: true, linkCenter: true, + container: ref.current, width, height, fitView: true, + layout: { + // LR + type: "compactBox", direction: "LR", getHGap: function getVGap() { + return 5; + } + }, + defaultEdge: + // { + // type: "cubic-vertical", + // style: { + // stroke: "#A3B1BF" + // } + // }, + { + type: "round-poly", + sourceAnchor: 0, + targetAnchor: 1, + style: { + radius: 8, + stroke: "rgb(19, 33, 92)" + } + }, + defaultNode: { + style: { + cursor: "pointer" + }, + // anchorPoints: [ + // [0.5, 0], + // [0.5, 1] + // ] + anchorPoints: [ + [0.9, 0.5], + [0, 0.5] + ] + }, + nodeStateStyles: { + hover: { + fill: "#fff", + shadowBlur: 30, + shadowColor: "#ddd" + }, + operatorhover: { + "operator-box": { + opacity: 1 + } + } + }, + modes: { + default: ["zoom-canvas", "drag-canvas", "collapse-expand", "drag-node"] + } + }); + } + graph.data(dataSource); + graph.render(); + graph.on("node:mouseenter", (e: any) => { + if (e.target.get("name")?.includes("operator")) { + graph.setItemState(e.item, "operatorhover", true); + } else { + graph.setItemState(e.item, "hover", true); + } + }); + graph.on("node:mouseleave", (e: any) => { + graph.setItemState(e.item, "operatorhover", false); + graph.setItemState(e.item, "hover", false); + }); + graph.on("node:click", (e: any) => { + // graph.zoom(3, { x: e.canvasX, y: e.canvasY }); + }); + }; + + return (
    ); +}; + +export default index; + +const traverse: any = (arr: any[]) => { + return _.map(arr, item => { + if (!_.isEmpty(item.children)) { + const uuid = uuidV4(); + return { + id: uuid, label: item.salaryItemName, + subLabel: item.result, formula: item.formula, + ratio: 1, increase: true, level: 1, collapsed: true, + children: traverse(item.children) + }; + } else { + const uuid = uuidV4(); + return { + id: uuid, label: item.salaryItemName, + subLabel: item.result, formula: item.formula, + ratio: 1, increase: true, level: 2 + }; + } + }); +}; diff --git a/src/pages/salaryItemDiagram/index.tsx b/src/pages/salaryItemDiagram/index.tsx new file mode 100644 index 0000000..1f24dc3 --- /dev/null +++ b/src/pages/salaryItemDiagram/index.tsx @@ -0,0 +1,168 @@ +/* + * Author: 黎永顺 + * name: 薪资项目-拓扑图 + * Description: + * Date: 2023/10/19 + */ +import React, { FunctionComponent, useEffect, useMemo, useState } from "react"; +import { DecompositionTreeGraph, NodeConfig } from "@ant-design/graphs"; +import { notification } from "antd"; +import { exceptStr } from "@/utils/common"; +import API from "@/api"; +// @ts-ignore +import uuidV4 from "uuid/v4"; + +interface OwnProps { +} + +type Props = OwnProps; +const index: FunctionComponent = (props) => { + const [dataSource, setDataSource] = useState({}); + const [i18n, setI18n] = useState({}); + useEffect(() => { + window.parent.postMessage({ type: "initDiagram" }, "*"); + window.addEventListener("message", receiveMessageFromIndex, false); + return () => { + window.removeEventListener("message", receiveMessageFromIndex, false); + }; + }, []); + const receiveMessageFromIndex = (event: any) => { + const data: any = exceptStr(event.data); + if (!_.isEmpty(data)) { + const { i18n, acctEmpId, salaryItemId, salarySobId } = data; + setI18n(i18n); + init({ acctEmpId, salaryItemId, salarySobId }); + } + }; + const init = (paylaod: any) => { + API.CalculateService.getSalarysobItemTopology(paylaod).then(({ data }) => { + const { status, data: result } = data; + if (status) { + const dataSource = { + id: result.salaryItemId.toString(), + value: { title: result.salaryItemName, formula: result.formula, items: [{ text: result.result }] }, + children: !_.isEmpty(result.children) ? traverse(result.children) : [] + }; + setDataSource(dataSource); + } + }); + }; + const getChildren = async (nodeCfg: NodeConfig) => { + return findChildren(extractTree(dataSource.children), nodeCfg.id); + }; + const openNotification = (data: any) => { + const { value: { formula } } = data; + notification.destroy(); + if (_.isEmpty(formula)) return; + const { formula: description } = formula; + notification.open({ + message: i18n["公式"], + duration: null, + description, + style: { maxWidth: 600 } + }); + }; + const component: any = useMemo(() => { + const config = { + data: { + ...dataSource, + children: dataSource.children ? _.map(dataSource.children, o => ({ ...o, children: null })) : null + }, + autoFit: false, animate: true, + nodeCfg: { + getChildren, + autoWidth: true, + items: { + layout: "follow" + } + }, + markerCfg: (cfg: any) => { + const { g_level, id } = cfg; + return { + show: (g_level === 0 && !_.isEmpty(dataSource.children)) || getStorageIds(dataSource.children).includes(id) + }; + }, + onReady: (graph: any) => { + graph.on("node:click", (evt: any) => { + if (_.isEmpty(evt.target.attrs) || JSON.stringify(evt.target.attrs).indexOf("cursor") !== -1) return; + openNotification(evt.item._cfg.model); + }); + setTimeout(() => { + graph.zoom(1); + }); + }, + behaviors: ["drag-canvas", "zoom-canvas", "drag-node"] + }; + return !_.isEmpty(dataSource) ? : null; + }, [dataSource]); + return component; +}; + +export default index; + +const traverse: any = (arr: any[]) => { + return _.map(arr, item => { + if (!_.isEmpty(item.children)) { + const uuid = uuidV4(); + return { + id: uuid, + value: { title: item.salaryItemName, formula: item.formula, items: [{ text: item.result }] }, + children: traverse(item.children) + }; + } else { + const uuid = uuidV4(); + return { + id: uuid, value: { title: item.salaryItemName, formula: item.formula, items: [{ text: item.result }] } + }; + } + }); +}; +const findChildren: any = (arr: any[], id: string) => { + const dataSource = _.find(arr, o => o.id === id).children; + return _.map(dataSource, o => ({ ...o, children: [] })); +}; +/* + * Author: 黎永顺 + * Description:数组扁平换 + * Params: + * Date: 2023/10/26 + */ +const extractTree = (data: any[]) => { + if (!data.length) return []; + const list: any[] = []; + const getObj = (arr: any[]) => { + arr.forEach((row: any) => { + let obj = {}; + obj = JSON.parse(JSON.stringify(row)); + list.push(obj); + if (row.children) { + getObj(row.children); + } + }); + return list; + }; + return getObj(data); +}; +/* + * Author: 黎永顺 + * Description: 有children子集的id集合 + * Params: + * Date: 2023/10/26 + */ +const getStorageIds = (array: any[]) => { + let ids: any = []; + const getIds = (array: any, ids: any) => { + array.forEach((item: any) => { + if (item.children) ids.push(item.id); + if (item.children) ids = getIds(item.children, ids); + }); + return ids; + }; + if (Array.isArray(array)) { + if (array.length === 0) return []; + } else { + return []; + } + ids = getIds(array, ids); + return ids; +}; diff --git a/src/pages/salaryItemDiagram/registerShape.ts b/src/pages/salaryItemDiagram/registerShape.ts new file mode 100644 index 0000000..f11af8e --- /dev/null +++ b/src/pages/salaryItemDiagram/registerShape.ts @@ -0,0 +1,292 @@ +// @ts-ignore +import G6 from "@antv/g6"; + +G6.registerNode("root", { + draw: (cfg: any, group: any) => { + const size = [80, 30]; + const keyShape = group.addShape("rect", { + attrs: { + width: size[0], + height: size[1], + x: -size[0] / 2, + y: -size[1] / 2, + fill: "rgb(19, 33, 92)", + radius: 5 + }, + draggable: true, + name: "root-keyshape" + }); + group.addShape("text", { + attrs: { + text: `${cfg.ratio}%`, + fill: "rgba(255, 255, 255, 0.85)", + fontSize: 6, + x: 10 - size[0] / 2, + y: 3 + }, + draggable: true, + name: "ratio-shape" + }); + group.addShape("text", { + attrs: { + text: `${cfg.label}`, + fill: "rgba(255, 255, 255, 0.85)", + fontSize: 9, + x: -6, + y: 0 + }, + draggable: true, + name: "label-shape" + }); + group.addShape("line", { + attrs: { + x1: -6, + x2: 35, + y1: 2, + y2: 2, + stroke: "rgba(255, 255, 255, 0.85)", + lineWidth: 0.5 + }, + draggable: true, + name: "divider-shape" + }); + group.addShape("text", { + attrs: { + text: `${cfg.subLabel}`, + fill: "rgba(255, 255, 255, 0.65)", + fontSize: 6, + x: -6, + y: 10 + }, + draggable: true, + name: "sublabel-shape" + }); + return keyShape; + } +}); +G6.registerNode("level1node", { + draw: (cfg: any, group: any) => { + const size = [60, 40]; + const keyShape = group.addShape("rect", { + attrs: { + width: size[0], + height: size[1], + x: -size[0] / 2, + y: -size[1] / 2, + fill: "rgb(213, 225, 247)", + radius: 5 + }, + draggable: true, + name: "level1node-keyshape" + }); + group.addShape("text", { + attrs: { + text: `${cfg.label}`, + fill: "rgba(19, 33, 92, 0.65)", + fontSize: 6, + x: 0, + y: -6, + textAlign: "center" + }, + draggable: true, + name: "label-shape" + }); + group.addShape("text", { + attrs: { + text: `${cfg.subLabel}`, + fill: "rgba(19, 33, 92, 0.65)", + fontSize: 8, + x: 0, + y: 6, + fontWeight: 800, + textAlign: "center" + }, + draggable: true, + name: "sublabel-shape" + }); + group.addShape("rect", { + attrs: { + x: -12, + y: 8, + width: 25, + height: 8, + radius: 4, + fill: cfg.increase ? "rgb(127, 193, 193)" : "rgb(220, 124, 125)" + }, + draggable: true, + name: "ratio-box" + }); + group.addShape("text", { + attrs: { + text: `${cfg.ratio}%`, + fill: "rgba(255, 255, 255, 0.85)", + fontSize: 6, + x: 0, + y: 9, + textAlign: "center", + textBaseline: "top" + }, + draggable: true, + name: "ratio-shape" + }); + // edge end + group.addShape("line", { + attrs: { + x1: -size[0] / 2, + x2: -size[0] / 2 + 6, + y1: 0, + y2: 0, + lineWidth: 1, + stroke: "rgb(19, 33, 92)" + } + }); + group.addShape("circle", { + attrs: { + r: 2, + x: -size[0] / 2 + 6, + y: 0, + fill: "rgb(19, 33, 92)" + } + }); + return keyShape; + }, + update: undefined +}, "rect"); +G6.registerNode("othernode", { + draw: (cfg: any, group: any) => { + const size = [100, 30]; + const keyShape = group.addShape("rect", { + attrs: { + width: size[0], + height: size[1], + x: -size[0] / 2, + y: -size[1] / 2, + fill: "rgb(213, 225, 247)", + radius: 5 + }, + draggable: true, + name: "level1node-keyshape" + }); + group.addShape("text", { + attrs: { + text: `${cfg.label}`, + fill: "rgba(19, 33, 92, 0.65)", + fontSize: 6, + x: 10 - size[0] / 2, + y: -2, + textAlign: "left" + }, + draggable: true, + name: "label-shape" + }); + group.addShape("text", { + attrs: { + text: `${cfg.subLabel}`, + fill: "rgba(19, 33, 92, 0.65)", + fontSize: 8, + fontWeight: 800, + x: 10 - size[0] / 2, + y: 8, + textAlign: "left" + }, + draggable: true, + name: "sublabel-shape" + }); + group.addShape("rect", { + attrs: { + x: 12, + y: -4, + width: 25, + height: 8, + radius: 4, + fill: cfg.increase ? "rgb(127, 193, 193)" : "rgb(220, 124, 125)" + }, + draggable: true, + name: "ratio-box" + }); + group.addShape("text", { + attrs: { + text: `${cfg.ratio}%`, + fill: "rgba(255, 255, 255, 0.85)", + fontSize: 6, + x: 18, + y: -3, + textAlign: "left", + textBaseline: "top" + }, + draggable: true, + name: "ratio-shape" + }); + if (cfg.operator) { + group.addShape("rect", { + attrs: { + x: -8, + y: 27, + width: 16, + height: 16, + lineWidth: 1, + stroke: "#aaa", + lineDash: [2, 1], + opacity: 0 + }, + name: "operator-box" + }); + group.addShape("circle", { + attrs: { + r: 6, + x: 0, + y: 35, + fill: "rgba(240, 90, 109, 0.15)" + }, + name: "operator-circle" + }); + group.addShape("text", { + attrs: { + text: cfg.operator, + x: 0, + y: 34, + fontSize: 12, + fill: "rgba(240, 90, 109, 0.85)", + textAlign: "center", + textBaseline: "middle" + }, + name: "operator-symbol" + }); + } + + // edge end + group.addShape("line", { + attrs: { + x1: -size[0] / 2, + x2: -size[0] / 2 + 6, + y1: 0, + y2: 0, + lineWidth: 1, + stroke: "rgb(19, 33, 92)" + } + }); + group.addShape("circle", { + attrs: { + r: 2, + x: -size[0] / 2 + 6, + y: 0, + fill: "rgb(19, 33, 92)" + } + }); + return keyShape; + }, + update: undefined +}, "rect"); +G6.registerEdge("round-poly", { + getControlPoints: (cfg: any) => { + const { startPoint, endPoint } = cfg; + return [ + startPoint, + { + x: startPoint.x, + y: endPoint.y + }, + endPoint + ]; + } +}, "polyline"); diff --git a/src/pages/welfareLedgerTable/index.tsx b/src/pages/welfareLedgerTable/index.tsx index 8c6dec9..4aa6244 100644 --- a/src/pages/welfareLedgerTable/index.tsx +++ b/src/pages/welfareLedgerTable/index.tsx @@ -44,15 +44,15 @@ const Index: FunctionComponent = (props) => { setPageInfo(pageInfo); setDataSource(dataSource); setColumns(_.map(columns, it => { - if (it.dataIndex === "userName") { + if (it.dataIndex === "userName" || it.dataIndex === "workcode") { return { - ...it, width: 200, title: ( + ...it, width: 140, title: ( - ), fixed: "left" + ), fixed: "left", ellipsis: true }; } return { - ...it, width: 200, title: ( + ...it, width: 140, ellipsis: true, title: ( ) };