Merge branch 'feature/V2-薪酬统计分析' into develop

This commit is contained in:
黎永顺 2023-05-26 16:06:19 +08:00
commit e4495d6959
11 changed files with 411 additions and 36 deletions

View File

@ -66,3 +66,11 @@ export const deleteRangeSetting = (params) => {
export const saveRangeSetting = (params) => {
return postFetch("/api/bs/hrmsalary/report/statistics/echarts/saveRangeSetting", params);
};
//员工列表
export const statisticsEmployeeList = (params) => {
return postFetch("/api/bs/hrmsalary/report/statistics/employee/list", params);
};
//员工详情列表
export const statisticsEmployeeDetailList = (params) => {
return postFetch("/api/bs/hrmsalary/report/statistics/employee/detailList", params);
};

View File

@ -33,6 +33,7 @@ import RuleConfig from "./pages/ruleConfig";
import Appconfig from "./pages/appConfig";
import FieldManagement from "./pages/fieldManagement";
import AnalysisOfSalaryStatistics from "./pages/analysisOfSalaryStatistics";
import EmployeeList from "./pages/employeeView";
import ReportView from "./pages/reportView";
import stores from "./stores";
@ -155,6 +156,7 @@ const Routes = (
<Route key="appconfig" path="appconfig" component={Appconfig}/>
<Route key="fieldManagement" path="fieldManagement" component={FieldManagement}/>
<Route key="analysisOfSalaryStatistics" path="analysisOfSalaryStatistics" component={AnalysisOfSalaryStatistics}/>
<Route key="analysisOfSalaryStatisticsId" path="analysisOfSalaryStatistics/:employeeId" component={EmployeeList}/>
<Route key="reportView" path="reportView" component={ReportView}/>
</Route>
);

View File

@ -91,9 +91,15 @@ class DimensionTable extends Component {
render: (_, record) => {
return (
<span className="space10">
<a href="javascript: void(0);" onClick={() => onEdit(record.id)}>{getLabel(111, "编辑")}</a>
<a href="javascript: void(0);"
onClick={() => this.dimensionDelete([record.id])}>{getLabel(111, "删除")}</a>
{
record.canEdit &&
<a href="javascript: void(0);" onClick={() => onEdit(record.id)}>{getLabel(111, "编辑")}</a>
}
{
record.canDelete &&
<a href="javascript: void(0);"
onClick={() => this.dimensionDelete([record.id])}>{getLabel(111, "删除")}</a>
}
</span>
);
}

View File

@ -0,0 +1,103 @@
/*
* Author: 黎永顺
* name: 员工明细列表
* Description:
* Date: 2023/5/24
*/
import React, { Component } from "react";
import { WeaLocaleProvider, WeaTable } from "ecCom";
import { statisticsEmployeeList } from "../../../apis/statistics";
import "../index.less";
const { getLabel } = WeaLocaleProvider;
class EmployeeDetails extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
dataSource: [],
columns: [],
pageInfo: {
current: 1,
pageSize: 10,
total: 0
}
};
}
componentDidMount() {
this.statisticsEmployeeList();
}
componentWillReceiveProps(nextProps, nextContext) {
if (nextProps.year !== this.props.year) this.statisticsEmployeeList(nextProps);
}
statisticsEmployeeList = (props) => {
const { pageInfo } = this.state;
const payload = {
year: props ? props.year : this.props.year,
keyword: props ? props.keyword : this.props.keyword,
...pageInfo
};
this.setState({ loading: true });
statisticsEmployeeList(payload).then(({ status, data }) => {
this.setState({ loading: false });
if (status) {
const { columns, list: dataSource, pageNum: current, total, pageSize } = data;
this.setState({
pageInfo: { ...pageInfo, current, pageSize, total },
dataSource,
columns: [...columns, {
title: "操作",
dataIndex: "operate",
width: 80,
render: (_, record) => {
return <a target="_blank"
href={`${window.location.origin}/spa/hrmSalary/static/index.html#/main/hrmSalary/analysisOfSalaryStatistics/${record.id}?name=${record.name}&dept=${record.department || ""}`}>{getLabel(111, "查看")}</a>;
}
}]
});
}
}).catch(() => this.setState({ loading: false }));
};
render() {
const { dataSource, loading, columns, pageInfo } = this.state;
const pagination = {
...pageInfo,
showTotal: total => `${getLabel(111, "共")} ${total} ${getLabel(111, "条")}`,
showQuickJumper: true,
showSizeChanger: true,
pageSizeOptions: ["10", "20", "50", "100"],
onShowSizeChange: (current, pageSize) => {
this.setState({
pageInfo: { ...pageInfo, current, pageSize }
}, () => {
this.statisticsEmployeeList();
});
},
onChange: current => {
this.setState({
pageInfo: { ...pageInfo, current }
}, () => {
this.statisticsEmployeeList();
});
}
};
return (
<WeaTable
rowKey="id"
className="employeeTableWrapper"
dataSource={dataSource}
pagination={pagination}
loading={loading}
columns={columns}
scroll={{ y: `calc(100vh - 174px)` }}
/>
);
}
}
export default EmployeeDetails;

View File

@ -6,16 +6,18 @@
*/
import React, { Component } from "react";
import { inject, observer } from "mobx-react";
import { WeaInputSearch, WeaLocaleProvider, WeaReqTop } from "ecCom";
import { WeaDatePicker, WeaInputSearch, WeaLocaleProvider, WeaReqTop } from "ecCom";
import { Button } from "antd";
import { condition, reportCondition } from "./components/conditions";
import { commonEnumList, reportGetForm } from "../../apis/ruleconfig";
import { dimensionGetForm } from "../../apis/statistics";
import EmployeeDetails from "./components/employeeDetails";
import StatisticsModal from "./components/statisticsModal";
import DimensionSlide from "./components/dimensionSlide";
import DimensionTable from "./components/dimensionTable";
import ReportList from "./components/reportList";
import ReportForm from "./components/reportForm";
import moment from "moment";
import "./index.less";
const { getLabel } = WeaLocaleProvider;
@ -31,6 +33,8 @@ class Index extends Component {
reportConditions: [],
selectedKey: "statistics",
reportName: "",
keyword: "",
year: moment().format("YYYY"),
slideReq: {
visible: false, formId: ""
},
@ -242,8 +246,8 @@ class Index extends Component {
render() {
const { taxAgentStore: { statisticsReportBtn }, attendanceStore: { statisticsForm, reportForm } } = this.props;
const { selectedKey, modalReq, slideReq, conditions, reportConditions, reportName } = this.state;
const buttons = [
const { selectedKey, modalReq, slideReq, conditions, reportConditions, reportName, keyword, year } = this.state;
const buttons = selectedKey === "statistics" ? [
<Button type="primary" onClick={() => this.handleReqBtnsClick("addReport")}>{getLabel(111, "新建报表")}</Button>,
<Button type="ghost"
onClick={() => this.handleReqBtnsClick("dimension")}>{getLabel(111, "维度统计管理")}</Button>,
@ -251,26 +255,37 @@ class Index extends Component {
value={reportName}
onChange={reportName => this.setState({ reportName })}
onSearch={() => this.handleReqBtnsClick("search")}/>
] : [
<span className="employeeYearWrapper">
<span>{getLabel(111, "年薪资核算人员明细:")}</span>
<WeaDatePicker value={year} format="YYYY" onChange={year => this.setState({ year })}/>
</span>,
<WeaInputSearch placeholder={getLabel(111, "请输入姓名、工号、身份证号")} className="search"
value={keyword}
onChange={keyword => this.setState({ keyword })}
onSearch={() => this.employeeListRef.statisticsEmployeeList()}/>
];
const tabs = [
{ key: "statistics", title: getLabel(111, "统计表") }
// { key: "detail", title: getLabel(111, "员工明细") }
{ key: "statistics", title: getLabel(111, "统计表") },
{ key: "detail", title: getLabel(111, "员工明细") }
];
return (
<WeaReqTop
title={getLabel(111, "薪酬统计报表")} icon={<i className="icon-coms-fa"/>}
iconBgcolor="#F14A2D" buttons={!statisticsReportBtn ? buttons.slice(-1) : buttons} buttonSpace={10}
showDropIcon={false} tabDatas={tabs} className="xc_tj_fx_wrapper"
selectedKey={selectedKey}
title={getLabel(111, "薪酬统计报表")} icon={<i className="icon-coms-fa"/>} selectedKey={selectedKey}
iconBgcolor="#F14A2D" tabDatas={tabs} className="xc_tj_fx_wrapper" showDropIcon={false}
buttons={(!statisticsReportBtn && selectedKey === "statistics") ? buttons.slice(-1) : buttons} buttonSpace={10}
onChange={selectedKey => this.setState({ selectedKey }, () => this.state.selectedKey === "statistics" && this.initReportFormCondition())}
>
{
this.state.selectedKey === "statistics" &&
<ReportList
ref={dom => this.reportListRef = dom}
reportName={reportName}
onEdit={this.handleReqBtnsClick}
/>
selectedKey === "statistics" ?
<ReportList
ref={dom => this.reportListRef = dom}
reportName={reportName}
onEdit={this.handleReqBtnsClick}
/> : <EmployeeDetails
ref={dom => this.employeeListRef = dom}
keyword={keyword} year={year}
/>
}
<StatisticsModal {...modalReq} onCancel={this.handleCancel} form={reportForm}>
{

View File

@ -5,6 +5,15 @@
width: 220px;
}
.employeeYearWrapper {
display: flex;
align-items: center;
& > span:first-child {
margin-right: 4px;
}
}
.wea-new-top-req-content {
background: #FFF;
@ -139,3 +148,31 @@
}
}
}
//员工明细
.employeeTableWrapper {
.operates i.icon-coms-more {
padding: 5px 0;
display: inline-block;
width: 40px;
font-size: 20px;
color: #333;
cursor: pointer;
font-weight: 400;
}
}
.operatePopover {
.ant-popover-inner {
min-width: 100px !important;
}
.ant-popover-inner-content {
padding: 0;
.ant-menu {
border-right: none;
}
}
}

View File

@ -0,0 +1,175 @@
/*
* Author: 黎永顺
* name: 员工明细查看
* Description:
* Date: 2023/5/24
*/
import React, { Component } from "react";
import { WeaLocaleProvider, WeaSelect, WeaTop } from "ecCom";
import { WeaTableNew } from "comsMobx";
import { toJS } from "mobx";
import { Spin } from "antd";
import { inject, observer } from "mobx-react";
import { MonthRangePicker } from "../reportView/components/statisticalMicroSettingsSlide";
import { optionAddWhole } from "../../util/options";
import moment from "moment";
import "./index.less";
const WeaTableComx = WeaTableNew.WeaTable;
const { getLabel } = WeaLocaleProvider;
@inject("taxAgentStore", "payrollFilesStore")
@observer
class Index extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
taxAgentId: "",
countResult: {},
salaryMonth: [moment().startOf("year").format("YYYY-MM"), moment().format("YYYY-MM")],
dataSource: [],
pageInfo: {
current: 1,
pageSize: 10,
total: 0
}
};
}
componentWillMount() {
const { taxAgentStore: { fetchTaxAgentOption } } = this.props;
fetchTaxAgentOption();
}
componentDidMount() {
this.statisticsEmployeeDetailList();
window.addEventListener("message", this.handleReceive, false);
}
handleReceive = ({ data }) => {
const { type, payload: { id, params } = {} } = data;
const { dataSource, pageInfo, countResult } = this.state;
const { payrollFilesStore: { employeeTableStore } } = this.props;
const columns = _.filter(toJS(employeeTableStore.columns), (item) => item.display === "true" && item.dataIndex !== "acctTimes");
if (type === "init") {
this.postMessageToChild({
columns: _.map(columns, (it, idx) => ({
...it,
width: (it.dataIndex === "taxAgent" || it.dataIndex === "salarySob") ? 176 : it.oldWidth,
fixed: (idx === 1 || idx === 0 || idx === 2) ? "left" : "",
ellipsis: true
})), dataSource, countResult,
showSum: true, pageInfo
});
} else if (type === "turn") {
if (id === "PAGEINFO") {
const { pageNum: current, size: pageSize } = params;
this.setState({ pageInfo: { ...pageInfo, current, pageSize } }, () => this.statisticsEmployeeDetailList());
}
}
};
postMessageToChild = (payload) => {
const childFrameObj = document.getElementById("atdTable");
const { dataSource, columns, showSum, pageInfo, countResult } = payload;
childFrameObj && childFrameObj.contentWindow.postMessage(JSON.stringify({
dataSource, columns, showSum, pageInfo, countResult
}), "*");
};
statisticsEmployeeDetailList = () => {
const { params: { employeeId }, payrollFilesStore: { statisticsEmployeeDetailList } } = this.props;
const { taxAgentId, salaryMonth, pageInfo } = this.state;
const payload = {
employeeId, taxAgentId,
salaryMonth: salaryMonth.map(item => moment(item).format("YYYY-MM-DD")),
...pageInfo
};
this.setState({ loading: true });
statisticsEmployeeDetailList(payload).then(({ status, data }) => {
this.setState({ loading: false });
if (status) {
const { countResult, pageInfo: { list, pageNum: current, pageSize, total } } = data;
this.setState({
countResult,
dataSource: list || [],
pageInfo: { ...pageInfo, current, pageSize, total }
}, () => {
// this.postMessageToChild({
// columns: this.state.columns,
// dataSource: this.state.dataSource,
// showSum: false, pageInfo: this.state.pageInfo
// });
});
}
}).catch(() => this.setState({ loading: false }));
};
getColumns = () => {
const { dataSource, pageInfo, countResult } = this.state;
const { payrollFilesStore: { employeeTableStore } } = this.props;
const columns = _.filter(toJS(employeeTableStore.columns), (item) => item.display === "true" && item.dataIndex !== "acctTimes");
this.postMessageToChild({
columns: _.map(columns, (it, idx) => ({
...it,
width: (it.dataIndex === "taxAgent" || it.dataIndex === "salarySob") ? 176 : it.oldWidth,
fixed: (idx === 1 || idx === 0 || idx === 2) ? "left" : "",
ellipsis: true
})), dataSource, countResult,
showSum: true, pageInfo
});
};
render() {
const {
location,
taxAgentStore: { showOperateBtn, taxAgentOption },
payrollFilesStore: { employeeTableStore }
} = this.props;
const { salaryMonth, taxAgentId, loading } = this.state;
const { query: { dept, name } } = location;
const btns = [
<MonthRangePicker viewAttr={2} dateRange={salaryMonth}
onChange={v => this.setState({ salaryMonth: v }, () => this.statisticsEmployeeDetailList())}/>,
<WeaSelect options={optionAddWhole(taxAgentOption)} value={taxAgentId} style={{ width: 200 }}
onChange={v => this.setState({ taxAgentId: v }, () => this.statisticsEmployeeDetailList())}/>
];
const dropMenuDatas = [
{
key: "BTN_COLUMN",
icon: <i className="icon-coms-Custom"/>,
content: "显示列定制",
onClick: () => {
employeeTableStore.setColSetVisible(true);
employeeTableStore.tableColSet(true);
}
}
];
return (
<WeaTop
title={<span><span style={{ marginRight: 8 }}>{name}</span><span>{dept}</span></span>}
icon={<i className="icon-coms-fa"/>} buttons={showOperateBtn ? btns : []}
iconBgcolor="#F14A2D" showDropIcon={true} dropMenuDatas={dropMenuDatas}
>
<div className="employeeDetailWrapper">
<Spin spinning={loading}>
<iframe
style={{ border: 0, width: "100%", height: "100%" }}
// src="http://localhost:7607/#/commonTable"
src="/spa/hrmSalary/hrmSalaryCalculateDetail/index.html#/commonTable"
id="atdTable"
/>
</Spin>
</div>
<WeaTableComx
style={{ display: "none" }}
comsWeaTableStore={employeeTableStore}
needScroll={true}
columns={this.getColumns()}
/>
</WeaTop>
);
}
}
export default Index;

View File

@ -0,0 +1,9 @@
.employeeDetailWrapper {
height: 100%;
padding: 10px 10px 0 10px;
.ant-spin-nested-loading,
.ant-spin-container {
height: 99%;
}
}

View File

@ -323,7 +323,7 @@ const TitleDialog = (props) => {
</div>
</div>;
};
const MonthRangePicker = (props) => {
export const MonthRangePicker = (props) => {
const { dateRange, onChange, viewAttr } = props;
const [startDate, endDate] = dateRange || [];
return <div className="rangePickerBox">

View File

@ -1,12 +1,15 @@
import { action, observable } from "mobx";
import { WeaTableNew } from "comsMobx";
import * as API from "../apis/payrollFiles";
import { statisticsEmployeeDetailList } from "../apis/statistics";
const { TableStore } = WeaTableNew;
export class PayrollFilesStore {
@observable tableStore = new TableStore();
@action("列表查询")
@observable employeeTableStore = new TableStore();
@action("薪资档案-列表查询")
queryList = (payload = {}, searchItemsValue = {}, url = "") => {
return new Promise((resolve, reject) => {
const { departmentIds, positionIds, subcompanyIds, ...extra } = searchItemsValue;
@ -28,4 +31,21 @@ export class PayrollFilesStore {
});
});
};
@action("薪酬统计列表员工详情-列表查询")
statisticsEmployeeDetailList = (payload) => {
return new Promise((resolve, reject) => {
statisticsEmployeeDetailList(payload).then(res => {
const { data, status } = res;
if (status) {
const { dataKey } = data;
const { datas } = dataKey;
this.employeeTableStore.getDatas(datas); // table 请求数据
}
resolve(res);
}).catch(() => {
reject();
});
});
};
}

View File

@ -1,19 +1,19 @@
// 添加全部选项
export const optionAddAll = (options) => {
let results = [...options];
results.unshift({
key: "All",
showname: "全部",
selected: false
})
return results;
}
let results = [...options];
results.unshift({
key: "All",
showname: "全部",
selected: false
});
return results;
};
export const optionAddWhole = (options) => {
let results = [...options];
results.unshift({
key: "",
showname: "全部",
selected: false
})
return results;
}
let results = [...options];
results.unshift({
key: "",
showname: "全部个税扣缴义务人",
selected: false
});
return results;
};