泛微薪资核算iframe表格

This commit is contained in:
黎永顺 2023-10-10 09:24:36 +08:00
parent c219b1a5fb
commit f7237c2db6
14 changed files with 445 additions and 49376 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -16,7 +16,7 @@ class CalculateService extends BasicService {
departmentIds: departmentIds ? departmentIds.split(",") : undefined,
positionIds: positionIds ? positionIds.split(",") : undefined,
subcompanyIds: subcompanyIds ? subcompanyIds.split(",") : undefined,
statuses: statuses ? statuses.split(",") : [],
statuses: statuses ? statuses.split(",") : []
};
for (let key in queryParams) {
if (queryParams[key] === "" || queryParams[key] === "0") {
@ -72,6 +72,10 @@ class CalculateService extends BasicService {
}
return this.post(`/api/bs/hrmsalary/salaryacct/acctresult/sum`, queryParams);
};
//合计行
getAcctResultsum = async (params: any) => {
return this.post(`/api/bs/hrmsalary/salaryacct/acctresult/sum`, params);
};
}
const calculateService = new CalculateService();

View File

@ -22,7 +22,8 @@ export default ({ children, style = {} }: any) => {
background: (
window.location.hash.indexOf("atdTable") !== -1 ||
window.location.hash.indexOf("standingbookTable") !== -1 ||
window.location.hash.indexOf("previewTable") !== -1
window.location.hash.indexOf("previewTable") !== -1 ||
window.location.hash.indexOf("calcTable") !== -1
) ? "#f6f6f6" : "#FFF",
...style
}}

View File

@ -12,6 +12,7 @@ module.exports = {
"/rankMapTable.*": "blank",
"/reportTable.*": "blank",
"/commonTable.*": "blank",
"/calcTable.*": "blank",
"/payrollFilesTable.*": "blank",
"/employeeDeclareTable.*": "blank",
"/manage.*": "manage",

View File

@ -4,17 +4,17 @@
*
*/
import * as React from 'react';
import { createFromIconfontCN } from '@ant-design/icons';
import { Util } from '@/utils';
import * as React from "react";
import { createFromIconfontCN } from "@ant-design/icons";
import { Util } from "@/utils";
export declare type IconType = React.ReactNode | string;
const hrmSalaryUrl = "/spa/hrmSalary/hrmSalaryCalculateDetail/";
const Icon = createFromIconfontCN({
scriptUrl: [
// @ts-ignore
`${Util.getPublicPath()}css/iconfont/iconfont.js`,
],
`${process.env.NODE_ENV === "dev" ? Util.getPublicPath() : hrmSalaryUrl}css/iconfont/iconfont.js`
]
});
/**
@ -25,18 +25,18 @@ export const buildIcon: (
type: string,
icon: IconType,
style?: React.CSSProperties,
className?: string,
className?: string
) => React.ReactNode | null = function (
type,
icon,
style = {},
className = '',
className = ""
) {
let comp;
switch (type) {
case 'IMAGE':
case "IMAGE":
comp =
typeof icon === 'string' ? (
typeof icon === "string" ? (
<img
src={icon}
className={className}
@ -48,7 +48,7 @@ export const buildIcon: (
break;
default:
comp =
typeof icon === 'string' ? (
typeof icon === "string" ? (
<Icon type={icon} style={style} className={className}/>
) : (
icon

View File

@ -84,6 +84,75 @@
color: rgb(217, 0, 27)
}
.expand-th:hover {
.toogle-lock-tool {
width: 30%;
}
}
th.td_odd {
background: #fffaf0 !important;
}
.expand-th {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
.title-text {
width: 90%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1 1;
cursor: pointer;
}
.toogle-lock-tool {
display: flex;
width: 0;
overflow: hidden;
height: 100%;
justify-content: center;
align-items: center;
& > :first-child {
margin-right: 4px;
}
:global {
.anticon {
color: #5d9cec;
cursor: pointer;
font-size: 14px;
}
}
}
}
.explain-icon-area {
display: flex;
justify-content: flex-start;
align-items: center;
.icon-item {
display: flex;
align-items: center;
margin-right: 12px;
color: #5d9cec;
font-size: 12px;
:global {
.anticon {
font-size: 12px;
margin-right: 4px;
}
}
}
}
:global {
.ant-btn-link, .ant-dropdown-trigger {
padding: 0;

View File

@ -0,0 +1,36 @@
import React, { FunctionComponent } from "react";
import Icon from "@/lib/CustomIcon";
import { LockOutlined, UnlockOutlined } from "@ant-design/icons";
import styles from "@/pages/atdTable/components/index.less";
interface OwnProps {
i18n: any;
}
type Props = OwnProps;
const CalcExplainFooter: FunctionComponent<Props> = (props) => {
const { i18n } = props;
return (
<div className={styles["explain-icon-area"]}>
<div className={styles["icon-item"]}>
<Icon type="icon-piliangsuoding"/>
<span>{i18n["批量锁定"]}</span>
</div>
<div className={styles["icon-item"]}>
<Icon type="icon-piliangjiesuo"/>
<span>{i18n["批量解锁"]}</span>
</div>
<div className={styles["icon-item"]}>
<LockOutlined/>
<span>{i18n["当前状态锁定,点击解锁"]}</span>
</div>
<div className={styles["icon-item"]}>
<UnlockOutlined/>
<span>{i18n["当前状态未锁定,点击锁定"]}</span>
</div>
</div>
);
};
export default CalcExplainFooter;

View File

@ -0,0 +1,63 @@
/*
* Author: 黎永顺
* name: 薪资核算-
* Description:
* Date: 2023/9/18
*/
import React, { FunctionComponent, useCallback, useEffect, useMemo, useState } from "react";
import { Spin, Table, Typography } from "antd";
import { ColumnType } from "antd/lib/table";
import API from "@/api";
const { Text } = Typography;
interface OwnProps {
columns: ColumnType<any>[];
dataSourceUrl: string;
payload: any;
}
type Props = OwnProps;
const calcFixedTotal: FunctionComponent<Props> = (props) => {
const [sumRow, setSumRow] = useState<Partial<{}>>({});//薪资核算总计行数据
const [loading, setLoading] = useState<boolean>(false);
const flattenFn = (source: ColumnType<any>[]) => {
let res: ColumnType<any>[] = [];
source.forEach((el: any) => {
_.isEmpty(el.children) && res.push(el);
el.children && res.push(...flattenFn(el.children));
});
return res;
};
const columns = useMemo(() => {
return !_.isEmpty(props.columns) ? flattenFn(props.columns) : [];
}, [props.columns]);
const dataSourceUrl = useCallback((payload) => {
return API.CalculateService.getAcctResultsum(payload);
}, [props.dataSourceUrl]);
useEffect(() => {
if (!_.isEmpty(props.payload)) {
setLoading(true);
dataSourceUrl(props.payload).then(({ data }) => {
setLoading(false);
const { data: result, status } = data;
if (status) setSumRow(result.sumRow || {});
});
}
}, [props.payload]);
return (<>
{
_.map(columns, (item: any, index) => {
return <Table.Summary.Cell index={index + 1} key={index + 1}>
{
loading ? <Spin spinning={loading} size="small"></Spin> :
<Text type="danger">{sumRow[item.dataIndex] || "-"}</Text>
}
</Table.Summary.Cell>;
})
}
</>);
};
export default calcFixedTotal;

View File

@ -0,0 +1,51 @@
/*
* Author: 黎永顺
* name: 自定义薪资核算表格标题
* Description:
* Date: 2023/9/15
*/
import React, { FunctionComponent } from "react";
import classnames from "classnames";
import Icon from "@/lib/CustomIcon";
import styles from "@/pages/atdTable/components/index.less";
interface OwnProps {
dataIndex?: string;
title?: string;
lockStatus?: string;
onHandleFormulatd?: any;
i18n?: any;
}
type Props = OwnProps;
const customTableTitle: FunctionComponent<Props> = (props) => {
const { dataIndex, title, lockStatus, onHandleFormulatd, i18n = {} } = props;
const handleToggleSalaryItemVal = (salaryItemId: string, type: string) => {
window.parent.postMessage(
{
type: "turn",
payload: { id: "LOCKING", params: { lockType: type, salaryItemId } }
},
"*"
);
};
return (
// th-width-lock
<div className={classnames(styles["expand-th"])}>
<div className={styles["title-text"]} title={title} onClick={() => onHandleFormulatd(dataIndex)}>{title}</div>
{
!!lockStatus &&
<div className={styles["toogle-lock-tool"]}>
<Icon type="icon-piliangsuoding" title={i18n["点击锁定所有解锁的项目值"]}
onClick={() => handleToggleSalaryItemVal(dataIndex as string, "LOCK")}/>
<Icon type="icon-piliangjiesuo" title={i18n["点击解锁所有锁定的项目值"]}
onClick={() => handleToggleSalaryItemVal(dataIndex as string, "UNLOCK")}/>
</div>
}
</div>
);
};
export default customTableTitle;

View File

@ -0,0 +1,150 @@
/*
* Author: 黎永顺
* name: 薪资核算-
* Description:
* Date: 2023/9/14
*/
import React, { FunctionComponent, useEffect, useState } from "react";
import { Button, Table, Typography } from "antd";
import { LockOutlined } from "@ant-design/icons";
import CustomTableTitle from "@/pages/calcTable/customTableTitle";
import CalcExplainFooter from "@/pages/calcTable/calcExplainFooter";
import CaclFixedTotal from "./calcFixedTotal";
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 styles from "@/pages/atdTable/components/index.less";
interface OwnProps {
}
type Props = OwnProps;
const { Text } = Typography;
const index: FunctionComponent<Props> = (props) => {
const [columns, setColumns] = useState<ColumnType<any>[]>([]);
const [dataSource, setDataSource] = useState<any[]>([]);
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const [pageInfo, setPageInfo] = useState<Partial<PaginationData>>({});
const [i18n, setI18n] = useState<any>({});
const [showTotalCell, setShowTotalCell] = useState<boolean>(false);
const [sumRowlistUrl, setSumRowlistUrl] = useState<string>("");
const [payload, setPayload] = useState<string>("");
useEffect(() => {
window.parent.postMessage({ type: "init" }, "*");
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 {
columns, dataSource, pageInfo, selectedRowKeys, i18n: i18nRes = {},
showTotalCell = false, sumRowlistUrl = "", payload = {}
} = data;
setSumRowlistUrl(sumRowlistUrl);
setShowTotalCell(showTotalCell);
setI18n(i18nRes);
setPayload(payload);
setPageInfo(pageInfo);
setDataSource(dataSource);
setSelectedRowKeys(selectedRowKeys);
setColumns([...convertColumns(_.map(columns, o => ({ ...o, i18n: i18nRes }))), {
title: i18nRes["操作"], dataIndex: "operate", fixed: "right", width: 120,
render: (__, record) => (<Button type="link" onClick={() => handleEdit(record?.id)}>{i18nRes["编辑"]}</Button>)
}]);
}
};
const convertColumns: any = (cols: any[]) => {
return _.map(cols, item => {
if (_.isNaN(parseInt(item.dataIndex))) {
return { ...item };
} else {
return {
...item, title: <CustomTableTitle {...item} onHandleFormulatd={handleFormulaTd}/>,
children: convertColumns(_.map(item.children, o => ({ ...o, i18n: item.i18n }))),
className: styles["td_odd"], i18n: item.i18n,
render: (text: string) => (
<span className={styles.contentSpan}>
<span title={text} className={styles.contentTitle}>{text}</span>
{
item.lockStatus === "LOCK" ? <LockOutlined title="锁定的项目值"/> : null
}
</span>
)
};
}
});
};
const handleFormulaTd = (dataIndex: string) => {
window.parent.postMessage(
{
type: "turn",
payload: { id: "FORMULA", params: { dataIndex } }
},
"*"
);
};
const handleEdit = (id: string) => {
window.parent.postMessage(
{
type: "turn",
payload: { id: "EDIT", params: { id } }
},
"*"
);
};
const sizeChange = (pageobj: IPage) => {
};
const onChange = (pageobj: IPage) => {
setPageInfo(() => {
window.parent.postMessage(
{
type: "turn",
payload: { id: "PAGEINFO", params: { ...pageInfo, ...pageobj } }
},
"*"
);
return { ...pageInfo, ...pageobj };
});
};
const rowSelection = {
columnWidth: 60,
selectedRowKeys: selectedRowKeys,
onChange: (selectedRowKeys: React.Key[]) => {
setSelectedRowKeys(selectedRowKeys);
window.parent.postMessage(
{
type: "turn",
payload: { id: "CHECKBOX", params: { selectedRowKeys } }
},
"*"
);
}
};
return (<Table
rowKey="id" size="small" bordered className={styles.tableWrapper}
columns={columns} dataSource={dataSource} rowSelection={rowSelection}
scroll={{ x: 1200, y: `calc(100vh - ${!showTotalCell ? 165 : 200}px)` }}
footer={() => <CalcExplainFooter i18n={i18n}/>}
pagination={{
...paginationFun(pageInfo, sizeChange, onChange, i18n),
size: "default"
}}
summary={() => (
!showTotalCell ? <></> :
<Table.Summary fixed>
<Table.Summary.Row>
<Table.Summary.Cell index={0} align="center"><Text type="danger">{i18n["总计"]}</Text></Table.Summary.Cell>
<CaclFixedTotal columns={columns} dataSourceUrl={sumRowlistUrl} payload={payload}/>
</Table.Summary.Row>
</Table.Summary>
)}
/>);
};
export default index;