diff --git a/src/app.tsx b/src/app.tsx
index d952148..8ed90ad 100644
--- a/src/app.tsx
+++ b/src/app.tsx
@@ -128,7 +128,7 @@ export async function getInitialState (type) {
// history.replace({
// pathname: loginPath
// })
- /topnavbar00/hrefficiency
+ // /topnavbar00/hrefficiency
}
}
diff --git a/src/pages/hrefficiency_workreport/WorkReport.js b/src/pages/hrefficiency_workreport/WorkReport.js
index 80dabfd..9a37d87 100644
--- a/src/pages/hrefficiency_workreport/WorkReport.js
+++ b/src/pages/hrefficiency_workreport/WorkReport.js
@@ -1,14 +1,686 @@
-import React, {Fragment, PureComponent} from 'react';
+import React, { PureComponent } from 'react';
+import {
+ Card,
+ Tree,
+ Button,
+ Select,
+ Space,
+ Row,
+ Col,
+ Pagination,
+ Modal,
+ message
+} from 'antd';
+import { history } from 'umi';
+import {
+ ExpandOutlined,
+ UserOutlined,
+ TeamOutlined,
+ ApartmentOutlined,
+ SyncOutlined,
+ DollarOutlined,
+ SettingOutlined,
+ DownloadOutlined,
+ UserAddOutlined,
+ EditOutlined,
+ DeleteOutlined
+} from '@ant-design/icons';
import styles from './WorkReport.less';
+import StandardTable from '@/components/StandardTable';
+import WorkReportAdd from './form/WorkReportAdd';
+import WorkReportRenderSimpleForm from "./form/WorkReportRenderSimpleForm" //表单
+
+const { Option } = Select;
+
class WorkReport extends PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ searchForm: {},
+ selectedKeys: [],
+ expandedKeys: ['0-0', '0-0-0', '0-0-1', '0-0-2'],
+ addModalVisible: false, // 新增弹窗显示状态
+ addLoading: false, // 新增loading状态
+ organizationData: [
+ {
+ title: '飞利信科技有限公司',
+ key: '0-0',
+ count: 356,
+ children: [
+ {
+ title: '技术部',
+ key: '0-0-0',
+ count: 120,
+ children: [
+ { title: '前端组', key: '0-0-0-0', count: 45 },
+ { title: '后端组', key: '0-0-0-1', count: 52 },
+ { title: '测试组', key: '0-0-0-2', count: 23 }
+ ],
+ },
+ {
+ title: '产品部',
+ key: '0-0-1',
+ count: 68,
+ children: [
+ { title: '产品设计组', key: '0-0-1-0', count: 28 },
+ { title: '用户体验组', key: '0-0-1-1', count: 25 },
+ { title: '产品运营组', key: '0-0-1-2', count: 15 }
+ ],
+ },
+ {
+ title: '运营部',
+ key: '0-0-2',
+ count: 52,
+ children: [
+ { title: '市场营销组', key: '0-0-2-0', count: 22 },
+ { title: '客户服务组', key: '0-0-2-1', count: 18 },
+ { title: '商务合作组', key: '0-0-2-2', count: 12 }
+ ],
+ },
+ {
+ title: '财务部',
+ key: '0-0-3',
+ count: 32,
+ children: [
+ { title: '会计组', key: '0-0-3-0', count: 18 },
+ { title: '审计组', key: '0-0-3-1', count: 14 }
+ ],
+ },
+ {
+ title: '人事部',
+ key: '0-0-4',
+ count: 84,
+ children: [
+ { title: 'HR专员组', key: '0-0-4-0', count: 25 },
+ { title: '招聘组', key: '0-0-4-1', count: 22 },
+ { title: '培训组', key: '0-0-4-2', count: 18 },
+ { title: '薪酬组', key: '0-0-4-3', count: 19 }
+ ],
+ },
+ ],
+ },
+ ],
+ tableData: [
+ {
+ key: '1',
+ id: 1,
+ workType: '招聘面试',
+ workPerson: '张三',
+ department: '人事部',
+ startTime: '2025-09-03 09:00',
+ endTime: '2025-09-03 17:30',
+ duration: '8.5小时',
+ workQuantity: 3
+ },
+ {
+ key: '2',
+ id: 2,
+ workType: '培训组织',
+ workPerson: '李四',
+ department: '人事部',
+ startTime: '2025-09-03 08:30',
+ endTime: '2025-09-03 18:00',
+ duration: '9.5小时',
+ workQuantity: 2
+ },
+ {
+ key: '3',
+ id: 3,
+ workType: '绩效考核',
+ workPerson: '王五',
+ department: '人事部',
+ startTime: '2025-09-03 09:15',
+ endTime: '2025-09-03 17:45',
+ duration: '8.5小时',
+ workQuantity: 5
+ },
+ {
+ key: '4',
+ id: 4,
+ workType: '薪酬核算',
+ workPerson: '赵六',
+ department: '人事部',
+ startTime: '2025-09-03 08:45',
+ endTime: '2025-09-03 17:15',
+ duration: '8.5小时',
+ workQuantity: 4
+ },
+ {
+ key: '5',
+ id: 5,
+ workType: '招聘面试',
+ workPerson: '陈七',
+ department: '人事部',
+ startTime: '2025-09-03 09:30',
+ endTime: '2025-09-03 16:30',
+ duration: '7小时',
+ workQuantity: 1
+ },
+ ],
+ pagination: {
+ current: 1,
+ pageSize: 5,
+ total: 356,
+ },
+ selectedRecord: null, // 选中的记录,用于编辑
+ };
+
+ // 默认列配置
+ this.defaultColumns = [
+ {
+ title: '序号',
+ dataIndex: 'id',
+ key: 'id',
+ width: 80,
+ align: 'center',
+ render: (text, record, index) => (
+
+ {index + 1}
+
+ ),
+ },
+ {
+ title: '作业类型',
+ dataIndex: 'workType',
+ key: 'workType',
+ width: 120,
+ align: 'center',
+ render: (text) => (
+
+ {text}
+
+ ),
+ },
+ {
+ title: '作业人员',
+ dataIndex: 'workPerson',
+ key: 'workPerson',
+ width: 100,
+ align: 'center',
+ render: (text, record) => (
+
+ ),
+ },
+ {
+ title: '部门',
+ dataIndex: 'department',
+ key: 'department',
+ width: 120,
+ align: 'center',
+ },
+ {
+ title: '开始时间',
+ dataIndex: 'startTime',
+ key: 'startTime',
+ width: 150,
+ align: 'center',
+ render: (time) => (
+
+ {time}
+
+ ),
+ },
+ {
+ title: '结束时间',
+ dataIndex: 'endTime',
+ key: 'endTime',
+ width: 150,
+ align: 'center',
+ render: (time) => (
+
+ {time}
+
+ ),
+ },
+ {
+ title: '持续时长',
+ dataIndex: 'duration',
+ key: 'duration',
+ width: 100,
+ align: 'center',
+ render: (duration) => (
+
+ {duration}
+
+ ),
+ sorter: (a, b) => {
+ const aHours = parseFloat(a.duration);
+ const bHours = parseFloat(b.duration);
+ return aHours - bHours;
+ },
+ },
+ {
+ title: '作业数量',
+ dataIndex: 'workQuantity',
+ key: 'workQuantity',
+ width: 100,
+ align: 'center',
+ render: (quantity) => (
+ = 4 ? '#52c41a' :
+ quantity >= 2 ? '#1890ff' : '#faad14'
+ }}>
+ {quantity}
+
+ ),
+ sorter: (a, b) => a.workQuantity - b.workQuantity,
+ },
+ {
+ title: '操作',
+ key: 'action',
+ width: 120,
+ align: 'center',
+ render: (_, record) => (
+
+ }
+ title="编辑工作报告"
+ onClick={() => this.handleEdit(record)}
+ >
+ 编辑
+
+ }
+ title="删除记录"
+ onClick={() => this.handleDelete(record)}
+ >
+ 删除
+
+
+ ),
+ },
+ ];
+ }
+
+ // 获取处理后的树形数据
+ getTreeData = () => {
+ const { organizationData } = this.state;
+
+ const processNode = (node) => ({
+ key: node.key,
+ title: (
+
+ {this.getNodeIcon(node.title)}
+ {node.title}
+ ({node.count})
+
+ ),
+ children: node.children ? node.children.map(processNode) : undefined
+ });
+
+ return organizationData.map(processNode);
+ };
+
+ // 获取节点图标
+ getNodeIcon = (title) => {
+ if (title.includes('公司') || title.includes('集团')) return ;
+ if (title.includes('技术') || title.includes('开发') || title.includes('测试')) return ;
+ if (title.includes('产品') || title.includes('设计') || title.includes('体验')) return ;
+ if (title.includes('运营') || title.includes('市场') || title.includes('客户') || title.includes('商务')) return ;
+ if (title.includes('财务') || title.includes('会计') || title.includes('审计')) return ;
+ if (title.includes('人事') || title.includes('HR') || title.includes('招聘') || title.includes('培训') || title.includes('薪酬')) return ;
+ return ;
+ };
+
+ // 搜索处理
+ handleSearch = (values) => {
+ console.log('搜索参数:', values);
+ this.setState({ searchForm: values });
+ };
+
+ // 重置搜索
+ handleReset = () => {
+ this.formRef?.resetFields();
+ this.setState({ searchForm: {} });
+ };
+
+ // 树节点选择
+ onTreeSelect = (selectedKeys, info) => {
+ console.log('选中节点:', selectedKeys, info);
+ this.setState({ selectedKeys });
+ };
+
+ // 树节点展开
+ onTreeExpand = (expandedKeys) => {
+ this.setState({ expandedKeys });
+ };
+
+ // 刷新树数据
+ handleTreeRefresh = () => {
+ // console.log('刷新组织架构');
+ // 这里可以添加刷新树数据的逻辑
+ };
+
+ // 展开/收缩所有节点
+ handleTreeToggle = () => {
+ const { expandedKeys, organizationData } = this.state;
+ if (expandedKeys.length > 0) {
+ // 收缩所有节点
+ this.setState({ expandedKeys: [] });
+ } else {
+ // 展开所有节点
+ const getAllKeys = (nodes) => {
+ let keys = [];
+ nodes.forEach(node => {
+ keys.push(node.key);
+ if (node.children) {
+ keys = keys.concat(getAllKeys(node.children));
+ }
+ });
+ return keys;
+ };
+ this.setState({ expandedKeys: getAllKeys(organizationData) });
+ }
+ };
+
+ renderSimpleForm() {
+ const { prooperlog = {} } = this.props;
+ const { params = {} } = prooperlog;
+ const parentMethods = {
+ handleSearch: this.handleSearch,
+ handleFormReset: this.handleFormReset,
+ toggleForm: this.toggleForm,
+ submitButtons: styles.submitButtons,
+ params
+ };
+ return (
+
+ );
+ }
+
+ renderAdvancedForm() {
+ const { prooperlog: { params } } = this.props;
+ const parentMethods = {
+ handleSearch: this.handleSearch,
+ handleFormReset: this.handleFormReset,
+ toggleForm: this.toggleForm,
+ params
+ };
+
+ return (
+
+ );
+ }
+
+ renderForm() {
+ const { expandForm } = this.state;
+ return expandForm ? this.renderAdvancedForm() : this.renderSimpleForm();
+ }
+
+ // 分页处理
+ onPaginationChange = (page, pageSize) => {
+ this.setState({
+ pagination: {
+ ...this.state.pagination,
+ current: page,
+ pageSize,
+ }
+ });
+ };
+
+ // 显示新增弹窗
+ showAddModal = () => {
+ this.setState({ addModalVisible: true });
+ };
+
+ // 关闭新增/编辑弹窗
+ hideAddModal = () => {
+ this.setState({
+ addModalVisible: false,
+ selectedRecord: null // 清空选中记录
+ });
+ };
+
+ // 新增成功回调
+ handleAddSuccess = (values) => {
+ console.log('新增成功:', values);
+
+ // 这里可以刷新列表数据
+ // 模拟添加到表格数据中
+ const newStaff = {
+ key: String(Date.now()),
+ id: this.state.tableData.length + 1,
+ name: values.name,
+ phone: values.age, // 年龄
+ idCard: values.department, // 部门
+ department: values.position, // 岗位
+ position: values.workType, // 工作性质
+ status: values.phone, // 电话
+ entryDate: new Date().toISOString().split('T')[0]
+ };
+
+ this.setState({
+ tableData: [...this.state.tableData, newStaff],
+ pagination: {
+ ...this.state.pagination,
+ total: this.state.pagination.total + 1
+ }
+ });
+ };
+
+ // 处理作业人员点击事件
+ handlePersonClick = (record) => {
+ Modal.info({
+ title: `${record.workPerson} - 作业详情`,
+ width: 600,
+ content: (
+
+
+ 作业人员:{record.workPerson}
+ 所属部门:{record.department}
+ 作业类型:{record.workType}
+ 作业数量:{record.workQuantity}
+
+ 时间统计
+
+ 开始时间:{record.startTime}
+ 结束时间:{record.endTime}
+ 持续时长:{record.duration}
+ 工作效率:
+ {(record.workQuantity / parseFloat(record.duration)).toFixed(2)} 件/小时
+
+
+
+ ),
+ okText: '确定'
+ });
+ };
+
+ // 处理编辑
+ handleEdit = (record) => {
+ console.log('编辑工作报告:', record);
+ this.setState({
+ selectedRecord: record,
+ addModalVisible: true
+ });
+ };
+
+ // 处理删除
+ handleDelete = (record) => {
+ Modal.confirm({
+ title: '删除确认',
+ content: `确定要删除 "${record.workPerson}" 的工作报告记录吗?`,
+ okText: '确定',
+ cancelText: '取消',
+ onOk: () => {
+ // 实际项目中这里应该调用删除接口
+ console.log('删除工作报告记录:', record.id);
+
+ const newData = this.state.tableData.filter(item => item.id !== record.id);
+ this.setState({
+ tableData: newData,
+ pagination: {
+ ...this.state.pagination,
+ total: newData.length
+ }
+ });
+ message.success('工作报告删除成功');
+ }
+ });
+ };
+
render() {
+ const { tableData, pagination, expandedKeys, addModalVisible, addLoading } = this.state;
return (
- <>
-
- >
- )
+
+ {/* 主体内容 */}
+
+
+
+ {/* 左侧组织架构树 */}
+
+
+
+
+ 组织架构
+
+
+ }
+ size="small"
+ style={{ color: '#1890ff' }}
+ onClick={this.handleTreeRefresh}
+ title="刷新"
+ />
+ }
+ size="small"
+ style={{ color: '#1890ff' }}
+ onClick={this.handleTreeToggle}
+ title={expandedKeys.length > 0 ? "收缩全部" : "展开全部"}
+ />
+
+
+ }
+ className={styles.treeCard}
+ >
+
+
+
+
+
+
+ {/* 右侧人员信息区 */}
+
+ {/* 筛选条件 */}
+
+ {this.renderForm()}
+
+
+ {/* 操作按钮和统计 */}
+
+
+ {/* 共 {pagination.total} 条记录 */}
+
+
+ }
+ size="middle"
+ // className={styles.exportButton}
+ >
+ 导出
+
+ }
+ size="middle"
+ // className={styles.addButton}
+ onClick={this.showAddModal}
+ >
+ 新增
+
+
+
+
+ {/* 人员表格 */}
+
+
+
+ {/* 分页 */}
+
+
+ `显示 ${range[0]} 到 ${range[1]} 条,共 ${total} 条记录`
+ }
+ onChange={this.onPaginationChange}
+ onShowSizeChange={this.onPaginationChange}
+ />
+
+
+
+
+
+
+
+ {/* 新增/编辑绩效弹窗 */}
+
+
+ );
}
}
-export default WorkReport
+export default WorkReport;
\ No newline at end of file
diff --git a/src/pages/hrefficiency_workreport/WorkReport.less b/src/pages/hrefficiency_workreport/WorkReport.less
index d5bff80..bde85d9 100644
--- a/src/pages/hrefficiency_workreport/WorkReport.less
+++ b/src/pages/hrefficiency_workreport/WorkReport.less
@@ -1,10 +1,523 @@
@import '~@/utils/utils.less';
-.frameContent {
- width: 100%;
+.staffInfoContainer {
+ min-height: 100vh;
height: 100vh;
- border: none;
- display: block;
- margin: 0;
- padding: 0;
+ // background-color: #f5f6fa;
+
+ .announcementBar {
+ background: #e6f7ff;
+ border: 1px solid #91d5ff;
+ margin-bottom: 16px;
+ border-radius: 6px;
+ overflow: hidden;
+
+ .announcement {
+ display: flex;
+ align-items: center;
+
+ .announcementLabel {
+ background-color: #fef3c7;
+ color: #92400e;
+ border-radius: 4px;
+ padding: 4px 8px;
+ font-size: 12px;
+ font-weight: 500;
+ margin-right: 12px;
+ white-space: nowrap;
+ display: flex;
+ align-items: center;
+
+ .anticon {
+ margin-right: 4px;
+ }
+ }
+
+ .scrollContainer {
+ flex: 1;
+ overflow: hidden;
+ white-space: nowrap;
+ }
+
+ .scrollContent {
+ display: inline-block;
+ animation: scroll 30s linear infinite;
+ color: #666;
+ font-size: 14px;
+ }
+ }
+ }
+
+ .mainContent {
+ // padding: 12px;
+
+ .contentCard {
+ .ant-card-head {
+ .ant-card-head-title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ }
+ }
+ }
+
+ .treeCard {
+ height: 600px;
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+
+ .treeHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 14px;
+ font-weight: 500;
+ }
+
+ .treeContainer {
+ height: 520px;
+ overflow-y: auto;
+
+ /* 自定义滚动条样式 */
+ &::-webkit-scrollbar {
+ width: 6px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: transparent;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: #d9d9d9;
+ border-radius: 3px;
+
+ &:hover {
+ background: #bfbfbf;
+ }
+ }
+
+ .orgTree {
+ .ant-tree-treenode {
+ padding: 2px 0;
+
+ .ant-tree-node-content-wrapper {
+ border-radius: 4px;
+ transition: all 0.3s ease;
+ padding: 4px 8px;
+
+ &:hover {
+ background-color: #f0f9ff;
+ transform: translateX(2px);
+ }
+
+ &.ant-tree-node-selected {
+ background-color: #e6f7ff !important;
+ box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);
+ }
+ }
+
+ .ant-tree-iconEle {
+ margin-right: 8px;
+ }
+
+ .ant-tree-title {
+ font-size: 14px;
+ }
+ }
+ }
+ }
+ }
+
+ /* Tree节点标题样式 */
+ .tree-node-title {
+ display: flex;
+ align-items: center;
+ width: 100%;
+
+ .node-title {
+ flex: 1;
+ font-size: 14px;
+ margin-left: 8px;
+ color: #333;
+ }
+
+ .node-count {
+ color: #999;
+ font-size: 12px;
+ margin-left: auto;
+ background: #f0f0f0;
+ padding: 1px 6px;
+ border-radius: 10px;
+ min-width: 20px;
+ text-align: center;
+ }
+ }
+
+ .searchCard {
+ margin-bottom: 16px;
+ border-radius: 8px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+
+ .ant-card-body {
+ padding: 16px;
+ }
+
+ /* 搜索表单容器样式 */
+ .searchFormContainer {
+ .searchForm {
+ .ant-form-item {
+ margin-bottom: 16px;
+
+ .ant-form-item-label {
+ font-weight: 500;
+ color: #333;
+
+ label {
+ color: #333 !important;
+ font-size: 14px;
+ }
+ }
+
+ .ant-input,
+ .ant-select-selector,
+ .ant-picker {
+ border-radius: 6px;
+ border: 1px solid #d9d9d9;
+ transition: all 0.3s ease;
+
+ &:hover {
+ border-color: #4c7bff;
+ }
+
+ &:focus,
+ &.ant-select-focused .ant-select-selector,
+ &.ant-picker-focused {
+ border-color: #2d5cf6;
+ box-shadow: 0 0 0 2px rgba(45, 92, 246, 0.2);
+ }
+ }
+
+ .ant-select-selection-placeholder,
+ .ant-input::placeholder,
+ .ant-picker-input input::placeholder {
+ color: #bfbfbf;
+ }
+ }
+ }
+ }
+
+ /* 搜索按钮样式 */
+ .searchButton {
+ background: linear-gradient(135deg, #2d5cf6 0%, #4c7bff 100%);
+ border: none;
+ border-radius: 6px;
+ color: white;
+ font-weight: 500;
+ font-size: 14px;
+ box-shadow: 0 2px 8px rgba(45, 92, 246, 0.3);
+ transition: all 0.3s ease;
+ height: 32px;
+ padding: 0 16px;
+
+ &:hover,
+ &:focus {
+ background: linear-gradient(135deg, #4c7bff 0%, #6b8fff 100%);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(45, 92, 246, 0.4);
+ color: white;
+ }
+
+ &:active {
+ transform: translateY(0);
+ }
+ }
+
+ /* 重置按钮样式 */
+ .resetButton {
+ background: #fff;
+ border: 1px solid #d9d9d9;
+ color: #666;
+ border-radius: 6px;
+ font-weight: 500;
+ font-size: 14px;
+ transition: all 0.3s ease;
+ height: 32px;
+ padding: 0 16px;
+
+ &:hover,
+ &:focus {
+ border-color: #4c7bff;
+ color: #2d5cf6;
+ }
+
+ .anticon {
+ color: #ff7875;
+ }
+ }
+
+ /* 展开按钮样式 */
+ .expandButton {
+ color: #2d5cf6;
+ font-size: 14px;
+ padding: 0 8px;
+ height: 32px;
+
+ &:hover,
+ &:focus {
+ color: #4c7bff;
+ }
+ }
+ }
+
+ .actionBar {
+
+
+
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+
+ .totalInfo {
+ color: #666;
+ font-size: 14px;
+ font-weight: 500;
+ }
+
+ .exportButton {
+ background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
+ border: none;
+ color: white;
+ border-radius: 8px;
+ font-weight: 500;
+ font-size: 14px;
+ box-shadow: 0 4px 12px rgba(82, 196, 26, 0.3);
+ transition: all 0.3s ease;
+ // margin-top: -8px;
+ height: 35px;
+ padding: 0 16px;
+
+
+ &:hover,
+ &:focus {
+ background: linear-gradient(135deg, #73d13d 0%, #95de64 100%);
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px rgba(82, 196, 26, 0.4);
+ color: white;
+ }
+
+ &:active {
+ transform: translateY(0);
+ }
+
+ .anticon {
+ color: white;
+ }
+ }
+
+
+ .addButton {
+ background: linear-gradient(135deg, #fa8c16 0%, #ffa940 100%);
+ border: none;
+ border-radius: 8px;
+ font-weight: 600;
+ font-size: 14px;
+ box-shadow: 0 4px 12px rgba(250, 140, 22, 0.3);
+ transition: all 0.3s ease;
+ // margin-top: -8px;
+ height: 35px;
+ padding: 0 16px;
+
+
+ &:hover,
+ &:focus {
+ background: linear-gradient(135deg, #ffa940 0%, #ffc069 100%);
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px rgba(250, 140, 22, 0.4);
+ }
+
+ &:active {
+ transform: translateY(0);
+ }
+ }
+ }
+
+ .tableCard {
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+ padding-bottom: 10px;
+
+ .ant-table-wrapper {
+ max-height: 600px;
+ overflow-y: auto;
+
+ /* 自定义滚动条样式 */
+ &::-webkit-scrollbar {
+ width: 8px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: #f5f5f5;
+ border-radius: 4px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: #d9d9d9;
+ border-radius: 4px;
+
+ &:hover {
+ background: #bfbfbf;
+ }
+ }
+
+ .ant-table-thead>tr>th {
+ background-color: #fafafa;
+ font-weight: 600;
+ color: #333;
+ border-bottom: 1px solid #e8e8e8;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ .ant-table-tbody>tr {
+ &:hover>td {
+ background-color: #f5f5f5 !important;
+ }
+
+ >td {
+ border-bottom: 1px solid #f0f0f0;
+ }
+ }
+ }
+
+ .paginationWrapper {
+ margin-top: 20px;
+ text-align: right;
+
+ .ant-pagination {
+ .ant-pagination-total-text {
+ color: #666;
+ }
+
+ .ant-pagination-item {
+ border-radius: 4px;
+
+ &.ant-pagination-item-active {
+ background-color: #2d5cf6;
+ border-color: #2d5cf6;
+ }
+ }
+
+ .ant-pagination-prev,
+ .ant-pagination-next {
+ border-radius: 4px;
+ }
+ }
+ }
+ }
+ }
+}
+
+.mainContent {
+ // padding: 12px;
+
+ .contentCard {
+ border: none !important;
+ .ant-card-head {
+ .ant-card-head-title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ }
+ }
+ }
+
+ .actionBar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+
+ .totalInfo {
+ color: #666;
+ font-size: 14px;
+ }
+ }
+
+ :global {
+ .ant-card-body {
+ padding: 12px 24px 0px 24px;
+ }
+ }
+}
+
+
+@keyframes scroll {
+ 0% {
+ transform: translateX(0);
+ }
+
+ 100% {
+ transform: translateX(-50%);
+ }
+}
+
+// 响应式设计
+@media (max-width: 1200px) {
+ .staffInfoContainer {
+ .mainContent {
+ .searchCard {
+ .ant-row {
+ .ant-col {
+ margin-bottom: 16px;
+ }
+ }
+ }
+ }
+ }
+}
+
+// 自定义主题色
+.ant-btn-primary {
+ background-color: #2d5cf6;
+ border-color: #2d5cf6;
+
+ &:hover,
+ &:focus {
+ background-color: #4c7bff;
+ border-color: #4c7bff;
+ }
+}
+
+.ant-tree .ant-tree-node-selected {
+ background-color: #e6f7ff !important;
+}
+
+.ant-select-focused .ant-select-selector,
+.ant-input-affix-wrapper-focused,
+.ant-input:focus,
+.ant-input-focused {
+ border-color: #2d5cf6 !important;
+ box-shadow: 0 0 0 2px rgba(45, 92, 246, 0.2) !important;
+}
+
+// 标签样式优化
+.ant-tag {
+ border-radius: 4px;
+ font-size: 12px;
+}
+
+// 按钮组间距调整
+.ant-space-item {
+ .ant-btn+.ant-btn {
+ margin-left: 8px;
+ }
+}
+
+// 表格链接按钮样式
+.ant-btn-link {
+ padding: 0 4px;
+ font-size: 12px;
}
\ No newline at end of file
diff --git a/src/pages/hrefficiency_workreport/form/WorkReportAdd.js b/src/pages/hrefficiency_workreport/form/WorkReportAdd.js
new file mode 100644
index 0000000..3a96c3f
--- /dev/null
+++ b/src/pages/hrefficiency_workreport/form/WorkReportAdd.js
@@ -0,0 +1,321 @@
+import React, { PureComponent } from 'react';
+import {
+ Modal,
+ Form,
+ Input,
+ Select,
+ InputNumber,
+ Row,
+ Col,
+ message,
+ DatePicker,
+ TreeSelect
+} from 'antd';
+import {
+ UserOutlined,
+ TeamOutlined,
+ ClockCircleOutlined,
+ NumberOutlined
+} from '@ant-design/icons';
+import moment from 'moment';
+
+const { Option } = Select;
+
+class WorkReportAdd extends PureComponent {
+ constructor(props) {
+ super(props);
+ this.formRef = React.createRef();
+
+ // 组织架构树数据
+ this.treeData = [
+ {
+ title: '飞利信科技有限公司',
+ value: '0-0',
+ key: '0-0',
+ children: [
+ {
+ title: '技术部',
+ value: '技术部',
+ key: '0-0-0',
+ children: [
+ { title: '前端组', value: '前端组', key: '0-0-0-0' },
+ { title: '后端组', value: '后端组', key: '0-0-0-1' },
+ { title: '测试组', value: '测试组', key: '0-0-0-2' }
+ ],
+ },
+ {
+ title: '产品部',
+ value: '产品部',
+ key: '0-0-1',
+ children: [
+ { title: '产品设计组', value: '产品设计组', key: '0-0-1-0' },
+ { title: '用户体验组', value: '用户体验组', key: '0-0-1-1' },
+ { title: '产品运营组', value: '产品运营组', key: '0-0-1-2' }
+ ],
+ },
+ {
+ title: '运营部',
+ value: '运营部',
+ key: '0-0-2',
+ children: [
+ { title: '市场营销组', value: '市场营销组', key: '0-0-2-0' },
+ { title: '客户服务组', value: '客户服务组', key: '0-0-2-1' },
+ { title: '商务合作组', value: '商务合作组', key: '0-0-2-2' }
+ ],
+ },
+ {
+ title: '财务部',
+ value: '财务部',
+ key: '0-0-3',
+ children: [
+ { title: '会计组', value: '会计组', key: '0-0-3-0' },
+ { title: '审计组', value: '审计组', key: '0-0-3-1' }
+ ],
+ },
+ {
+ title: '人事部',
+ value: '人事部',
+ key: '0-0-4',
+ children: [
+ { title: 'HR专员组', value: 'HR专员组', key: '0-0-4-0' },
+ { title: '招聘组', value: '招聘组', key: '0-0-4-1' },
+ { title: '培训组', value: '培训组', key: '0-0-4-2' },
+ { title: '薪酬组', value: '薪酬组', key: '0-0-4-3' }
+ ],
+ },
+ ],
+ },
+ ];
+ }
+
+ componentDidUpdate(prevProps) {
+ // 当编辑数据发生变化时,更新表单值
+ if (this.props.editData !== prevProps.editData && this.props.editData) {
+ const editData = this.props.editData;
+ this.formRef.current?.setFieldsValue({
+ workType: editData.workType,
+ workPerson: editData.workPerson,
+ department: editData.department,
+ workQuantity: editData.workQuantity,
+ startTime: editData.startTime ? moment(editData.startTime) : null,
+ endTime: editData.endTime ? moment(editData.endTime) : null,
+ workDescription: editData.workDescription || '',
+ });
+ }
+
+ // 当弹窗关闭时清空表单
+ if (!this.props.visible && prevProps.visible) {
+ this.formRef.current?.resetFields();
+ }
+ }
+
+ // 提交表单
+ handleSubmit = (values) => {
+ const { editData } = this.props;
+ const isEdit = !!editData;
+
+ console.log(isEdit ? '编辑工作报告:' : '新增工作报告:', values);
+
+ // 处理时间数据
+ const formattedValues = {
+ ...values,
+ startTime: values.startTime ? values.startTime.format('YYYY-MM-DD HH:mm:ss') : null,
+ endTime: values.endTime ? values.endTime.format('YYYY-MM-DD HH:mm:ss') : null,
+ };
+
+ // 计算持续时长
+ if (values.startTime && values.endTime) {
+ const duration = moment(values.endTime).diff(moment(values.startTime), 'hours', true);
+ formattedValues.duration = `${duration.toFixed(1)}小时`;
+ }
+
+ // 如果是编辑,保留原有的id
+ if (isEdit) {
+ formattedValues.id = editData.id;
+ formattedValues.key = editData.key;
+ }
+
+ // 这里可以调用API接口保存数据
+ // 模拟保存成功
+ message.success(isEdit ? '编辑工作报告成功!' : '新增工作报告成功!');
+
+ // 重置表单
+ this.formRef.current?.resetFields();
+
+ // 调用父组件的回调函数
+ if (this.props.onSuccess) {
+ this.props.onSuccess(formattedValues);
+ }
+
+ // 关闭弹窗
+ this.handleCancel();
+ };
+
+ // 取消操作
+ handleCancel = () => {
+ // 重置表单
+ this.formRef.current?.resetFields();
+
+ // 调用父组件的关闭回调
+ if (this.props.onCancel) {
+ this.props.onCancel();
+ }
+ };
+
+ render() {
+ const { visible, loading = false, editData } = this.props;
+ const isEdit = !!editData;
+
+ return (
+ this.formRef.current?.submit()}
+ onCancel={this.handleCancel}
+ width={800}
+ confirmLoading={loading}
+ destroyOnClose={true}
+ maskClosable={false}
+ >
+
+
+ );
+ }
+}
+
+export default WorkReportAdd;
diff --git a/src/pages/hrefficiency_workreport/form/WorkReportRenderSimpleForm.js b/src/pages/hrefficiency_workreport/form/WorkReportRenderSimpleForm.js
new file mode 100644
index 0000000..ca6af5d
--- /dev/null
+++ b/src/pages/hrefficiency_workreport/form/WorkReportRenderSimpleForm.js
@@ -0,0 +1,309 @@
+import React, { useState } from 'react';
+import { Button, Col, Form, Input, Row, Select, Space, DatePicker, InputNumber, TreeSelect } from 'antd';
+import { ClearOutlined, SearchOutlined, ExpandOutlined, CompressOutlined, UserOutlined, TeamOutlined, ClockCircleOutlined, NumberOutlined } from '@ant-design/icons';
+import styles from "../WorkReport.less";
+
+const { Option } = Select;
+const { RangePicker } = DatePicker;
+
+// 组织架构树数据
+const treeData = [
+ {
+ title: '飞利信科技有限公司',
+ value: '0-0',
+ key: '0-0',
+ children: [
+ {
+ title: '技术部',
+ value: '技术部',
+ key: '0-0-0',
+ children: [
+ { title: '前端组', value: '前端组', key: '0-0-0-0' },
+ { title: '后端组', value: '后端组', key: '0-0-0-1' },
+ { title: '测试组', value: '测试组', key: '0-0-0-2' }
+ ],
+ },
+ {
+ title: '产品部',
+ value: '产品部',
+ key: '0-0-1',
+ children: [
+ { title: '产品设计组', value: '产品设计组', key: '0-0-1-0' },
+ { title: '用户体验组', value: '用户体验组', key: '0-0-1-1' },
+ { title: '产品运营组', value: '产品运营组', key: '0-0-1-2' }
+ ],
+ },
+ {
+ title: '运营部',
+ value: '运营部',
+ key: '0-0-2',
+ children: [
+ { title: '市场营销组', value: '市场营销组', key: '0-0-2-0' },
+ { title: '客户服务组', value: '客户服务组', key: '0-0-2-1' },
+ { title: '商务合作组', value: '商务合作组', key: '0-0-2-2' }
+ ],
+ },
+ {
+ title: '财务部',
+ value: '财务部',
+ key: '0-0-3',
+ children: [
+ { title: '会计组', value: '会计组', key: '0-0-3-0' },
+ { title: '审计组', value: '审计组', key: '0-0-3-1' }
+ ],
+ },
+ {
+ title: '人事部',
+ value: '人事部',
+ key: '0-0-4',
+ children: [
+ { title: 'HR专员组', value: 'HR专员组', key: '0-0-4-0' },
+ { title: '招聘组', value: '招聘组', key: '0-0-4-1' },
+ { title: '培训组', value: '培训组', key: '0-0-4-2' },
+ { title: '薪酬组', value: '薪酬组', key: '0-0-4-3' }
+ ],
+ },
+ ],
+ },
+];
+
+const WorkReportRenderSimpleForm = (props) => {
+ const [form] = Form.useForm();
+ const [expandForm, setExpandForm] = useState(false);
+ const { handleSearch, handleFormReset, params } = props;
+
+ React.useEffect(() => {
+ form.setFieldsValue({
+ workType: params?.workType,
+ workPerson: params?.workPerson,
+ department: params?.department,
+ workDateRange: params?.workDateRange,
+ durationMin: params?.durationMin,
+ durationMax: params?.durationMax,
+ quantityMin: params?.quantityMin,
+ quantityMax: params?.quantityMax,
+ });
+ }, [params]);
+
+ const onFinish = values => {
+ const searchParams = {
+ ...values,
+ startDate: values.workDateRange ? values.workDateRange[0]?.format('YYYY-MM-DD') : undefined,
+ endDate: values.workDateRange ? values.workDateRange[1]?.format('YYYY-MM-DD') : undefined,
+ };
+ delete searchParams.workDateRange;
+
+ handleSearch && handleSearch(searchParams);
+ };
+
+ const myhandleFormReset = () => {
+ form.resetFields();
+ handleFormReset && handleFormReset();
+ };
+
+ const toggleExpandForm = () => {
+ setExpandForm(!expandForm);
+ };
+
+ return (
+
+
+
+ );
+
+};
+
+export default WorkReportRenderSimpleForm;
diff --git a/src/pages/hrefficiency_workreport/form/WorkReportRenderSimpleForm.less b/src/pages/hrefficiency_workreport/form/WorkReportRenderSimpleForm.less
new file mode 100644
index 0000000..e69de29
diff --git a/src/pages/nav_system_content/SystemContentList.js b/src/pages/nav_system_content/SystemContentList.js
index af4b8ab..841c25c 100644
--- a/src/pages/nav_system_content/SystemContentList.js
+++ b/src/pages/nav_system_content/SystemContentList.js
@@ -27,7 +27,9 @@ const SystemContentList = (props) => {
defaultKey = getDefaultRoute(routes)
const mathRoute = matchRoutes(routes, pathName)
- setRouteActive({ key: mathRoute?.length ? pathName : defaultKey }, routes)
+ // 如果当前路径匹配到菜单路由,或者是以当前模块开头的路径(如详情页),都不强制跳转
+ const shouldUseCurrentPath = mathRoute?.length > 0 || pathName.startsWith(tempRoute[0]?.key + '/')
+ setRouteActive({ key: shouldUseCurrentPath ? pathName : defaultKey }, routes)
}
setMenuItems(newList)
@@ -35,19 +37,41 @@ const SystemContentList = (props) => {
const setRouteActive = (value, menu) => {
const curKey = value.key
+ console.log(curKey,'7777777777777')
const tempMenu = menu ?? menuItems ?? []
const mathRoute = matchRoutes(tempMenu, curKey)
let openKeys = []
let selectedKeys = []
- mathRoute?.map((item, index) => {
- mathRoute?.length !== index + 1 && (openKeys = [...openKeys, item.pathname])
- mathRoute?.length === index + 1 && (selectedKeys = [...selectedKeys, item.pathname])
- })
+ if (mathRoute?.length > 0) {
+ // 如果找到匹配的菜单路由,正常处理
+ mathRoute?.map((item, index) => {
+ mathRoute?.length !== index + 1 && (openKeys = [...openKeys, item.pathname])
+ mathRoute?.length === index + 1 && (selectedKeys = [...selectedKeys, item.pathname])
+ })
+ } else {
+ // 如果是不在菜单中的路由(如详情页),尝试激活父级菜单
+ const pathSegments = curKey.split('/').filter(Boolean)
+ for (let i = pathSegments.length - 1; i > 0; i--) {
+ const parentPath = '/' + pathSegments.slice(0, i).join('/')
+ const parentRoute = matchRoutes(tempMenu, parentPath)
+ if (parentRoute?.length > 0) {
+ parentRoute?.map((item, index) => {
+ parentRoute?.length !== index + 1 && (openKeys = [...openKeys, item.pathname])
+ parentRoute?.length === index + 1 && (selectedKeys = [...selectedKeys, item.pathname])
+ })
+ break
+ }
+ }
+ }
setOpenKey(openKeys)
setSelectedKey(selectedKeys)
- history.replace(curKey)
+
+ // 只有当前路径与目标路径不同时才执行跳转
+ if (location.pathname !== curKey) {
+ history.replace(curKey)
+ }
}
return (
diff --git a/src/pages/performance/Performance.js b/src/pages/performance/Performance.js
new file mode 100644
index 0000000..266b062
--- /dev/null
+++ b/src/pages/performance/Performance.js
@@ -0,0 +1,717 @@
+import React, { PureComponent } from 'react';
+import {
+ Card,
+ Tree,
+ Input,
+ Button,
+ DatePicker,
+ Select,
+ Row,
+ Col,
+ Space,
+ Radio,
+ Checkbox,
+ InputNumber,
+ Avatar,
+ message,
+ Timeline,
+ Tag,
+ Typography,
+ Divider,
+ Upload,
+ Form,
+ Table,
+ Pagination
+} from 'antd';
+import {
+ SaveOutlined,
+ CloseOutlined,
+ PlusCircleOutlined,
+ DeleteOutlined,
+ ExpandOutlined,
+ UserOutlined,
+ TeamOutlined,
+ ApartmentOutlined,
+ SyncOutlined,
+ DollarOutlined,
+ SettingOutlined,
+ CalendarOutlined,
+ UsergroupAddOutlined,
+ UserSwitchOutlined,
+ StopOutlined,
+ CheckCircleOutlined,
+ CloseCircleOutlined,
+ ContactsOutlined,
+ PrinterOutlined,
+ CheckOutlined,
+ ClockCircleOutlined,
+ LoadingOutlined,
+ EyeOutlined,
+ SearchOutlined,
+ PictureOutlined,
+ UploadOutlined,
+ EditOutlined,
+ ShareAltOutlined,
+ FilterOutlined,
+ DownloadOutlined,
+ PlusOutlined,
+ CaretDownOutlined,
+ SortAscendingOutlined,
+ SortDescendingOutlined
+} from '@ant-design/icons';
+import ReactECharts from 'echarts-for-react';
+import dayjs from 'dayjs';
+import styles from './Performance.less';
+
+const { Option } = Select;
+const { TextArea } = Input;
+const { Title, Text, Paragraph } = Typography;
+const { RangePicker } = DatePicker;
+
+class Performance extends PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ selectedOrgKeys: ['0-0-4'], // 默认选中人事部
+ expandedKeys: ['0-0', '0-0-0', '0-0-1', '0-0-2'],
+ // 绩效数据
+ performanceData: {
+ selectedPlan: '2023年Q3季度考核',
+ currentPage: 1,
+ pageSize: 10,
+ total: 128,
+ // 绩效评分分布数据
+ scoreDistribution: [
+ { name: '0-60分', value: 12, color: '#FF6B6B' },
+ { name: '60-80分', value: 45, color: '#4ECDC4' },
+ { name: '80-100分', value: 71, color: '#45B7D1' }
+ ],
+ // 部门绩效平均分数据
+ departmentScores: {
+ departments: ['技术研发', '市场营销', '产品运营', '人力资源', '财务中心', '客户服务'],
+ scores: [89.5, 87.2, 85.8, 84.3, 83.7, 82.1]
+ },
+ // 人员绩效明细数据
+ performanceList: [
+ {
+ key: '1',
+ name: '张明远',
+ department: '技术研发中心',
+ totalScore: 96.5,
+ codeLines: 12500,
+ documentWords: 8500,
+ projectAmount: '¥1,250,000',
+ workHours: 186,
+ values: 48.0
+ },
+ {
+ key: '2',
+ name: '李思颖',
+ department: '市场营销中心',
+ totalScore: 95.0,
+ codeLines: 11800,
+ documentWords: 9200,
+ projectAmount: '¥1,100,000',
+ workHours: 182,
+ values: 47.0
+ },
+ {
+ key: '3',
+ name: '王浩然',
+ department: '产品运营中心',
+ totalScore: 93.5,
+ codeLines: 11000,
+ documentWords: 7800,
+ projectAmount: '¥980,000',
+ workHours: 180,
+ values: 47.0
+ },
+ {
+ key: '4',
+ name: '陈雨桐',
+ department: '人力资源中心',
+ totalScore: 92.0,
+ codeLines: 10500,
+ documentWords: 9500,
+ projectAmount: '¥920,000',
+ workHours: 178,
+ values: 46.5
+ },
+ {
+ key: '5',
+ name: '刘子轩',
+ department: '技术研发中心',
+ totalScore: 91.5,
+ codeLines: 9800,
+ documentWords: 7200,
+ projectAmount: '¥890,000',
+ workHours: 175,
+ values: 46.0
+ },
+ {
+ key: '6',
+ name: '赵欣然',
+ department: '客户服务中心',
+ totalScore: 90.0,
+ codeLines: 9200,
+ documentWords: 8500,
+ projectAmount: '¥850,000',
+ workHours: 172,
+ values: 45.5
+ },
+ {
+ key: '7',
+ name: '孙伟杰',
+ department: '财务中心',
+ totalScore: 89.5,
+ codeLines: 8900,
+ documentWords: 6800,
+ projectAmount: '¥820,000',
+ workHours: 170,
+ values: 45.0
+ },
+ {
+ key: '8',
+ name: '周晓彤',
+ department: '市场营销中心',
+ totalScore: 88.0,
+ codeLines: 8500,
+ documentWords: 7900,
+ projectAmount: '¥780,000',
+ workHours: 168,
+ values: 44.5
+ },
+ {
+ key: '9',
+ name: '吴宇航',
+ department: '技术研发中心',
+ totalScore: 87.5,
+ codeLines: 8200,
+ documentWords: 6500,
+ projectAmount: '¥750,000',
+ workHours: 165,
+ values: 44.0
+ },
+ {
+ key: '10',
+ name: '郑雅文',
+ department: '产品运营中心',
+ totalScore: 86.0,
+ codeLines: 8000,
+ documentWords: 7800,
+ projectAmount: '¥720,000',
+ workHours: 162,
+ values: 43.5
+ }
+ ]
+ },
+ organizationData: [
+ {
+ title: '飞利信科技有限公司',
+ key: '0-0',
+ count: 356,
+ children: [
+ {
+ title: '技术部',
+ key: '0-0-0',
+ count: 120,
+ children: [
+ { title: '前端组', key: '0-0-0-0', count: 45 },
+ { title: '后端组', key: '0-0-0-1', count: 52 },
+ { title: '测试组', key: '0-0-0-2', count: 23 }
+ ],
+ },
+ {
+ title: '产品部',
+ key: '0-0-1',
+ count: 68,
+ children: [
+ { title: '产品设计组', key: '0-0-1-0', count: 28 },
+ { title: '用户体验组', key: '0-0-1-1', count: 25 },
+ { title: '产品运营组', key: '0-0-1-2', count: 15 }
+ ],
+ },
+ {
+ title: '运营部',
+ key: '0-0-2',
+ count: 52,
+ children: [
+ { title: '市场营销组', key: '0-0-2-0', count: 22 },
+ { title: '客户服务组', key: '0-0-2-1', count: 18 },
+ { title: '商务合作组', key: '0-0-2-2', count: 12 }
+ ],
+ },
+ {
+ title: '财务部',
+ key: '0-0-3',
+ count: 32,
+ children: [
+ { title: '会计组', key: '0-0-3-0', count: 18 },
+ { title: '审计组', key: '0-0-3-1', count: 14 }
+ ],
+ },
+ {
+ title: '人事部',
+ key: '0-0-4',
+ count: 84,
+ children: [
+ { title: 'HR专员组', key: '0-0-4-0', count: 25 },
+ { title: '招聘组', key: '0-0-4-1', count: 22 },
+ { title: '培训组', key: '0-0-4-2', count: 18 },
+ { title: '薪酬组', key: '0-0-4-3', count: 19 }
+ ],
+ },
+ ],
+ },
+ ]
+ };
+ }
+
+ // 获取处理后的树形数据
+ getTreeData = () => {
+ const { organizationData } = this.state;
+
+ const processNode = (node) => ({
+ key: node.key,
+ title: (
+
+ {this.getNodeIcon(node.title)}
+ {node.title}
+ ({node.count})
+
+ ),
+ children: node.children ? node.children.map(processNode) : undefined
+ });
+
+ return organizationData.map(processNode);
+ };
+
+ // 获取节点图标
+ getNodeIcon = (title) => {
+ if (title.includes('公司') || title.includes('集团')) return ;
+ if (title.includes('技术') || title.includes('开发') || title.includes('测试')) return ;
+ if (title.includes('产品') || title.includes('设计') || title.includes('体验')) return ;
+ if (title.includes('运营') || title.includes('市场') || title.includes('客户') || title.includes('商务')) return ;
+ if (title.includes('财务') || title.includes('会计') || title.includes('审计')) return ;
+ if (title.includes('人事') || title.includes('HR') || title.includes('招聘') || title.includes('培训') || title.includes('薪酬')) return ;
+ return ;
+ };
+
+ // 刷新树数据
+ handleTreeRefresh = () => {
+ message.info('刷新组织架构数据');
+ };
+
+ // 展开/收缩所有节点
+ handleTreeToggle = () => {
+ const { expandedKeys, organizationData } = this.state;
+ if (expandedKeys.length > 0) {
+ this.setState({ expandedKeys: [] });
+ } else {
+ const getAllKeys = (nodes) => {
+ let keys = [];
+ nodes.forEach(node => {
+ keys.push(node.key);
+ if (node.children) {
+ keys = keys.concat(getAllKeys(node.children));
+ }
+ });
+ return keys;
+ };
+ this.setState({ expandedKeys: getAllKeys(organizationData) });
+ }
+ };
+
+ // 组织树选择
+ onTreeSelect = (selectedKeys, info) => {
+ console.log('选中节点:', selectedKeys, info);
+ this.setState({ selectedOrgKeys: selectedKeys });
+ };
+
+ // 树展开/收缩
+ onTreeExpand = (expandedKeys) => {
+ this.setState({ expandedKeys });
+ };
+
+ // 绩效计划切换
+ handlePlanChange = (value) => {
+ this.setState({
+ performanceData: {
+ ...this.state.performanceData,
+ selectedPlan: value
+ }
+ });
+ message.success(`切换到: ${value}`);
+ };
+
+ // 部门层级切换
+ handleDepartmentLevelChange = (value) => {
+ message.success(`切换到: ${value}`);
+ };
+
+ // 分页变化
+ handlePageChange = (page, pageSize) => {
+ this.setState({
+ performanceData: {
+ ...this.state.performanceData,
+ currentPage: page,
+ pageSize: pageSize
+ }
+ });
+ };
+
+ // 饼图配置
+ getPieOption = () => {
+ const { scoreDistribution } = this.state.performanceData;
+ return {
+ animation: false,
+ tooltip: {
+ trigger: 'item',
+ formatter: '{a}
{b}: {c} ({d}%)'
+ },
+ legend: {
+ orient: 'vertical',
+ right: 10,
+ top: 'center',
+ data: scoreDistribution.map(item => item.name)
+ },
+ series: [
+ {
+ name: '绩效评分分布',
+ type: 'pie',
+ radius: ['50%', '70%'],
+ center: ['40%', '50%'],
+ avoidLabelOverlap: false,
+ itemStyle: {
+ borderRadius: 4,
+ borderColor: '#fff',
+ borderWidth: 2
+ },
+ label: {
+ show: false,
+ position: 'center'
+ },
+ emphasis: {
+ label: {
+ show: true,
+ fontSize: '14',
+ fontWeight: 'bold'
+ }
+ },
+ labelLine: {
+ show: false
+ },
+ data: scoreDistribution.map(item => ({
+ value: item.value,
+ name: item.name,
+ itemStyle: {
+ color: item.color
+ }
+ }))
+ }
+ ]
+ };
+ };
+
+ // 柱状图配置
+ getBarOption = () => {
+ const { departmentScores } = this.state.performanceData;
+ const colorList = ['#45B7D1', '#4ECDC4', '#A5DEE4', '#7AC5CD', '#66C6C4', '#85C1E9'];
+
+ return {
+ animation: false,
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: {
+ type: 'shadow'
+ }
+ },
+ grid: {
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ top: '10%',
+ containLabel: true
+ },
+ xAxis: {
+ type: 'value',
+ boundaryGap: [0, 0.01]
+ },
+ yAxis: {
+ type: 'category',
+ data: departmentScores.departments
+ },
+ series: [
+ {
+ name: '平均分',
+ type: 'bar',
+ data: departmentScores.scores.map((score, index) => ({
+ value: score,
+ itemStyle: {
+ color: colorList[index],
+ borderRadius: [0, 4, 4, 0]
+ }
+ }))
+ }
+ ]
+ };
+ };
+
+ render() {
+ const { selectedOrgKeys, expandedKeys, performanceData } = this.state;
+
+ // 表格列配置
+ const columns = [
+ {
+ title: (
+
+ 姓名
+
+
+ ),
+ dataIndex: 'name',
+ key: 'name',
+ sorter: true,
+ render: (text) => {text}
+ },
+ {
+ title: (
+
+ 部门
+
+
+ ),
+ dataIndex: 'department',
+ key: 'department',
+ sorter: true
+ },
+ {
+ title: (
+
+ 总分
+
+
+ ),
+ dataIndex: 'totalScore',
+ key: 'totalScore',
+ sorter: true,
+ render: (score) => {score}
+ },
+ {
+ title: (
+
+ 代码行数
+
+
+ ),
+ dataIndex: 'codeLines',
+ key: 'codeLines',
+ sorter: true,
+ render: (value) => value.toLocaleString()
+ },
+ {
+ title: (
+
+ 文档字数
+
+
+ ),
+ dataIndex: 'documentWords',
+ key: 'documentWords',
+ sorter: true,
+ render: (value) => value.toLocaleString()
+ },
+ {
+ title: (
+
+ 项目金额数
+
+
+ ),
+ dataIndex: 'projectAmount',
+ key: 'projectAmount',
+ sorter: true
+ },
+ {
+ title: (
+
+ 出勤时长
+
+
+ ),
+ dataIndex: 'workHours',
+ key: 'workHours',
+ sorter: true
+ },
+ {
+ title: (
+
+ 价值观
+
+
+ ),
+ dataIndex: 'values',
+ key: 'values',
+ sorter: true
+ }
+ ];
+
+ return (
+
+
+
+
+ {/* 左侧组织架构树 - 保持不变 */}
+
+
+
+
+ 组织架构
+
+
+ }
+ size="small"
+ style={{ color: '#1890ff' }}
+ onClick={this.handleTreeRefresh}
+ title="刷新"
+ />
+ }
+ size="small"
+ style={{ color: '#1890ff' }}
+ onClick={this.handleTreeToggle}
+ title={expandedKeys.length > 0 ? "收缩全部" : "展开全部"}
+ />
+
+
+ }
+ className={styles.treeCard}
+ >
+
+
+
+
+
+
+ {/* 右侧绩效仪表盘 */}
+
+
+ {/* 滚动内容区 */}
+
+ {/* 头部标题和选择器 */}
+
+
绩效仪表盘
+
+ 绩效计划:
+ }
+ >
+
+
+
+
+
+
+
+
+ {/* 图表区域 */}
+
+
+
+ 绩效评分分布
+
+
+
+
+
+
+
+
+
部门绩效平均分
+ }
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 人员绩效明细表 */}
+
+ 人员绩效明细
+
+
({
+ className: styles.tableRowHover
+ })}
+ />
+
+ {/* 分页 */}
+
+
+ 显示 {((performanceData.currentPage - 1) * performanceData.pageSize) + 1} 到{' '}
+ {Math.min(performanceData.currentPage * performanceData.pageSize, performanceData.total)} 条,
+ 共 {performanceData.total} 条记录
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default Performance;
\ No newline at end of file
diff --git a/src/pages/performance/Performance.less b/src/pages/performance/Performance.less
new file mode 100644
index 0000000..9ac1b82
--- /dev/null
+++ b/src/pages/performance/Performance.less
@@ -0,0 +1,429 @@
+@import '~@/utils/utils.less';
+
+// 使用与OnBoarding相同的基础样式
+.container {
+ min-height: 100vh;
+ height: 100vh;
+ overflow: hidden;
+ background-color: #f5f6fa;
+}
+
+.mainContent {
+ height: 100vh;
+ overflow: hidden;
+
+ .contentCard {
+ height: 100%;
+ border: none !important;
+
+ :global(.ant-card-body) {
+ padding: 12px 24px;
+ // height: calc(100vh - 24px);
+ // overflow: hidden;
+ }
+ }
+}
+
+// 组织架构树样式 - 与OnBoarding保持一致
+.treeCard {
+ height: 600px;
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+
+ .treeContainer {
+ height: 520px;
+ overflow-y: auto;
+
+ &::-webkit-scrollbar {
+ width: 6px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.02);
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: rgba(0, 0, 0, 0.15);
+ border-radius: 3px;
+
+ &:hover {
+ background: rgba(0, 0, 0, 0.25);
+ }
+ }
+ }
+
+ .orgTree {
+ .ant-tree-node-content-wrapper {
+ border-radius: 4px;
+ transition: all 0.3s ease;
+ padding: 4px 8px;
+
+ &:hover {
+ background-color: #f0f9ff;
+ transform: translateX(2px);
+ }
+
+ &.ant-tree-node-selected {
+ background-color: #e6f7ff !important;
+ box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);
+ }
+ }
+ }
+}
+
+.tree-node-title {
+ display: flex;
+ align-items: center;
+ width: 100%;
+
+ .node-title {
+ flex: 1;
+ font-size: 14px;
+ margin-left: 8px;
+ color: #333;
+ }
+
+ .node-count {
+ color: #999;
+ font-size: 12px;
+ margin-left: auto;
+ background: #f0f0f0;
+ padding: 1px 6px;
+ border-radius: 10px;
+ min-width: 20px;
+ text-align: center;
+ }
+}
+
+// 主卡片样式
+.mainCard {
+ height: calc(100vh - 60px);
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+ overflow: hidden;
+
+ .ant-card-body {
+ padding: 0;
+ height: 100%;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ }
+}
+
+// 内容区域
+.content {
+ flex: 1;
+ padding: 20px 24px 28px 24px;
+ overflow-y: auto;
+ overflow-x: hidden;
+ min-height: 0; // 关键:允许flex子元素收缩
+ height: calc(100vh - 110px); // 减去头部高度和其他空间,强制设置高度
+ max-height: calc(100vh - 110px); // 明确最大高度,确保滚动条出现
+ &::-webkit-scrollbar {
+ width: 8px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.02);
+ border-radius: 4px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: rgba(0, 0, 0, 0.18);
+ border-radius: 4px;
+
+ &:hover {
+ background: rgba(0, 0, 0, 0.28);
+ }
+ }
+
+ scrollbar-width: thin;
+ scrollbar-color: rgba(0, 0, 0, 0.18) rgba(0, 0, 0, 0.02);
+}
+
+// 仪表盘头部
+.dashboardHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 24px;
+
+ .dashboardTitle {
+ margin: 0 !important;
+ font-size: 20px !important;
+ font-weight: 700 !important;
+ color: #333 !important;
+ }
+
+ .planSelector {
+ display: flex;
+ align-items: center;
+
+ .selectorLabel {
+ font-size: 14px;
+ color: #666;
+ margin-right: 8px;
+ }
+
+ .planSelect {
+ min-width: 180px;
+
+ .ant-select-selector {
+ border-radius: 4px;
+ background: #f5f6fa;
+ border: 1px solid #e8e8e8;
+
+ &:hover {
+ border-color: #2d5cf6;
+ }
+ }
+ }
+ }
+}
+
+// 图表区域
+.chartsRow {
+ margin-bottom: 24px;
+}
+
+.chartCard {
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+
+ .ant-card-body {
+ padding: 20px;
+ }
+
+ .chartHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+
+ .departmentSelect {
+ min-width: 120px;
+
+ .ant-select-selector {
+ border-radius: 4px;
+ background: #f5f6fa;
+ border: 1px solid #e8e8e8;
+
+ &:hover {
+ border-color: #2d5cf6;
+ }
+ }
+ }
+ }
+
+ .chartTitle {
+ margin: 0 0 16px 0 !important;
+ font-size: 16px !important;
+ font-weight: 600 !important;
+ color: #333 !important;
+ }
+
+ .chartContainer {
+ height: 300px;
+
+ // echarts-for-react容器样式
+ .echarts-for-react {
+ height: 100% !important;
+ width: 100% !important;
+ }
+
+ // 确保ECharts容器正确显示
+ > div {
+ height: 100% !important;
+ width: 100% !important;
+ }
+ }
+}
+
+// 表格卡片
+.tableCard {
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+
+ .ant-card-body {
+ padding: 20px;
+ }
+
+ .tableTitle {
+ margin: 0 0 16px 0 !important;
+ font-size: 16px !important;
+ font-weight: 600 !important;
+ color: #333 !important;
+ }
+}
+
+// 表格样式
+.tableWrapper {
+ .performanceTable {
+ .ant-table {
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ overflow: hidden;
+
+ .ant-table-thead > tr > th {
+ background-color: #fafafa;
+ border-bottom: 1px solid #e8e8e8;
+ padding: 12px 16px;
+ font-weight: 600;
+ color: #333;
+ font-size: 12px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+
+ &:hover {
+ background-color: #f5f6fa;
+ }
+ }
+
+ .ant-table-tbody > tr {
+ transition: all 0.3s ease;
+
+ &:hover {
+ background-color: #f8f9fa !important;
+ transform: translateY(-1px);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ }
+
+ > td {
+ border-bottom: 1px solid #f0f0f0;
+ padding: 12px 16px;
+ font-size: 14px;
+ color: #333;
+ }
+ }
+ }
+ }
+}
+
+// 表格头部样式
+.tableHeader {
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+
+ .sortIcon {
+ margin-left: 4px;
+ font-size: 10px;
+ color: #bfbfbf;
+ opacity: 0.6;
+ }
+
+ &:hover .sortIcon {
+ color: #2d5cf6;
+ opacity: 1;
+ }
+}
+
+// 总分文字样式
+.scoreText {
+ color: #2d5cf6 !important;
+ font-weight: 600 !important;
+}
+
+// 分页容器
+.paginationContainer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: 16px;
+ padding-top: 16px;
+ border-top: 1px solid #f0f0f0;
+
+ .paginationInfo {
+ color: #666;
+ font-size: 14px;
+ }
+
+ .pagination {
+ .ant-pagination-item {
+ border-radius: 4px;
+
+ &.ant-pagination-item-active {
+ background: #2d5cf6;
+ border-color: #2d5cf6;
+ }
+ }
+
+ .ant-pagination-prev,
+ .ant-pagination-next {
+ border-radius: 4px;
+ }
+ }
+}
+
+// 响应式设计
+@media (max-width: 1200px) {
+ .treeCard {
+ height: 300px;
+ }
+
+ .mainCard {
+ height: auto;
+ max-height: 80vh;
+ min-height: 500px;
+
+ .content {
+ max-height: 65vh;
+
+ &::-webkit-scrollbar {
+ width: 4px;
+ }
+ }
+ }
+
+ .dashboardHeader {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 16px;
+ }
+
+ .chartsRow {
+ .chartCard {
+ margin-bottom: 16px;
+
+ .chartContainer {
+ height: 250px;
+ }
+ }
+ }
+
+ .chartHeader {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 12px;
+ }
+}
+
+// 页面特定样式
+.container {
+ .ant-btn-primary {
+ border-radius: 6px;
+ font-weight: 500;
+ transition: all 0.3s ease;
+ }
+
+ .ant-btn:not(.ant-btn-primary) {
+ border-radius: 6px;
+ transition: all 0.3s ease;
+
+ &:hover,
+ &:focus {
+ border-color: #4c7bff;
+ color: #2d5cf6;
+ }
+ }
+
+ // 选择器样式优化
+ .ant-select-focused .ant-select-selector {
+ border-color: #2d5cf6 !important;
+ box-shadow: 0 0 0 2px rgba(45, 92, 246, 0.2) !important;
+ }
+}
\ No newline at end of file
diff --git a/src/pages/salarymanage_salarydata/SalaryData.js b/src/pages/salarymanage_salarydata/SalaryData.js
index 659fec4..8d9aa2d 100644
--- a/src/pages/salarymanage_salarydata/SalaryData.js
+++ b/src/pages/salarymanage_salarydata/SalaryData.js
@@ -1,14 +1,607 @@
-import React, {Fragment, PureComponent} from 'react';
+import React, { PureComponent } from 'react';
+import {
+ Card,
+ Tree,
+ Button,
+ Select,
+ Space,
+ Row,
+ Col,
+ Pagination,
+ Modal
+} from 'antd';
+import { history } from 'umi';
+import {
+ ExpandOutlined,
+ UserOutlined,
+ TeamOutlined,
+ ApartmentOutlined,
+ SyncOutlined,
+ DollarOutlined,
+ SettingOutlined,
+ DownloadOutlined,
+ UserAddOutlined,
+ EditOutlined,
+ DeleteOutlined,
+ EyeOutlined
+} from '@ant-design/icons';
import styles from './SalaryData.less';
+import StandardTable from '@/components/StandardTable';
+import SalaryDataAdd from './form/SalaryDataAdd';
+import SalaryDataRenderSimpleForm from "./form/SalaryDataRenderSimpleForm" //表单
+
+const { Option } = Select;
+
class SalaryData extends PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ searchForm: {},
+ selectedKeys: [],
+ expandedKeys: ['0-0', '0-0-0', '0-0-1', '0-0-2'],
+ addModalVisible: false, // 新增弹窗显示状态
+ addLoading: false, // 新增loading状态
+ organizationData: [
+ {
+ title: '飞利信科技有限公司',
+ key: '0-0',
+ count: 356,
+ children: [
+ {
+ title: '技术部',
+ key: '0-0-0',
+ count: 120,
+ children: [
+ { title: '前端组', key: '0-0-0-0', count: 45 },
+ { title: '后端组', key: '0-0-0-1', count: 52 },
+ { title: '测试组', key: '0-0-0-2', count: 23 }
+ ],
+ },
+ {
+ title: '产品部',
+ key: '0-0-1',
+ count: 68,
+ children: [
+ { title: '产品设计组', key: '0-0-1-0', count: 28 },
+ { title: '用户体验组', key: '0-0-1-1', count: 25 },
+ { title: '产品运营组', key: '0-0-1-2', count: 15 }
+ ],
+ },
+ {
+ title: '运营部',
+ key: '0-0-2',
+ count: 52,
+ children: [
+ { title: '市场营销组', key: '0-0-2-0', count: 22 },
+ { title: '客户服务组', key: '0-0-2-1', count: 18 },
+ { title: '商务合作组', key: '0-0-2-2', count: 12 }
+ ],
+ },
+ {
+ title: '财务部',
+ key: '0-0-3',
+ count: 32,
+ children: [
+ { title: '会计组', key: '0-0-3-0', count: 18 },
+ { title: '审计组', key: '0-0-3-1', count: 14 }
+ ],
+ },
+ {
+ title: '人事部',
+ key: '0-0-4',
+ count: 84,
+ children: [
+ { title: 'HR专员组', key: '0-0-4-0', count: 25 },
+ { title: '招聘组', key: '0-0-4-1', count: 22 },
+ { title: '培训组', key: '0-0-4-2', count: 18 },
+ { title: '薪酬组', key: '0-0-4-3', count: 19 }
+ ],
+ },
+ ],
+ },
+ ],
+ tableData: [
+ {
+ key: '1',
+ id: 1,
+ name: '张三',
+ gender: '男',
+ age: 28,
+ department: '技术部',
+ position: '高级工程师',
+ month: '2025-08',
+ preTaxSalary: 15000,
+ afterTaxSalary: 12850,
+ baseSalary: 10000,
+ floatingSalary: 5000
+ },
+ {
+ key: '2',
+ id: 2,
+ name: '李四',
+ gender: '男',
+ age: 30,
+ department: '产品部',
+ position: '产品经理',
+ month: '2025-08',
+ preTaxSalary: 18000,
+ afterTaxSalary: 15200,
+ baseSalary: 12000,
+ floatingSalary: 6000
+ },
+ {
+ key: '3',
+ id: 3,
+ name: '王五',
+ gender: '女',
+ age: 25,
+ department: '运营部',
+ position: '运营专员',
+ month: '2025-08',
+ preTaxSalary: 8000,
+ afterTaxSalary: 7200,
+ baseSalary: 6000,
+ floatingSalary: 2000
+ },
+ {
+ key: '4',
+ id: 4,
+ name: '赵六',
+ gender: '男',
+ age: 32,
+ department: '财务部',
+ position: '会计',
+ month: '2025-08',
+ preTaxSalary: 12000,
+ afterTaxSalary: 10400,
+ baseSalary: 8000,
+ floatingSalary: 4000
+ },
+ {
+ key: '5',
+ id: 5,
+ name: '陈七',
+ gender: '女',
+ age: 26,
+ department: '人事部',
+ position: 'HR专员',
+ month: '2025-08',
+ preTaxSalary: 10000,
+ afterTaxSalary: 8800,
+ baseSalary: 7000,
+ floatingSalary: 3000
+ },
+ ],
+ pagination: {
+ current: 1,
+ pageSize: 5,
+ total: 356,
+ }
+ };
+
+ // 默认列配置
+ this.defaultColumns = [
+ {
+ title: '姓名',
+ dataIndex: 'name',
+ key: 'name',
+ width: 100,
+ align: 'center',
+ },
+ {
+ title: '性别',
+ dataIndex: 'gender',
+ key: 'gender',
+ width: 80,
+ align: 'center',
+ },
+ {
+ title: '年龄',
+ dataIndex: 'age',
+ key: 'age',
+ width: 80,
+ align: 'center',
+ },
+ {
+ title: '部门',
+ dataIndex: 'department',
+ key: 'department',
+ width: 120,
+ align: 'center',
+ },
+ {
+ title: '岗位',
+ dataIndex: 'position',
+ key: 'position',
+ width: 140,
+ align: 'center',
+ },
+ {
+ title: '月份',
+ dataIndex: 'month',
+ key: 'month',
+ width: 100,
+ align: 'center',
+ },
+ {
+ title: '税前工资',
+ dataIndex: 'preTaxSalary',
+ key: 'preTaxSalary',
+ width: 120,
+ align: 'center',
+ render: (salary) => (
+
+ ¥{salary?.toLocaleString()}
+
+ ),
+ sorter: (a, b) => a.preTaxSalary - b.preTaxSalary,
+ },
+ {
+ title: '税后工资',
+ dataIndex: 'afterTaxSalary',
+ key: 'afterTaxSalary',
+ width: 120,
+ align: 'center',
+ render: (salary) => (
+
+ ¥{salary?.toLocaleString()}
+
+ ),
+ sorter: (a, b) => a.afterTaxSalary - b.afterTaxSalary,
+ },
+ {
+ title: '基本工资',
+ dataIndex: 'baseSalary',
+ key: 'baseSalary',
+ width: 120,
+ align: 'center',
+ render: (salary) => `¥${salary?.toLocaleString()}`,
+ sorter: (a, b) => a.baseSalary - b.baseSalary,
+ },
+ {
+ title: '浮动工资',
+ dataIndex: 'floatingSalary',
+ key: 'floatingSalary',
+ width: 120,
+ align: 'center',
+ render: (salary) => `¥${salary?.toLocaleString()}`,
+ sorter: (a, b) => a.floatingSalary - b.floatingSalary,
+ },
+ {
+ title: '详情',
+ key: 'detail',
+ width: 80,
+ align: 'center',
+ render: (_, record) => (
+ }
+ onClick={() => this.handleViewDetail(record)}
+ title="查看详情"
+ style={{ color: '#1890ff' }}
+ />
+ ),
+ },
+ ];
+ }
+
+ // 获取处理后的树形数据
+ getTreeData = () => {
+ const { organizationData } = this.state;
+
+ const processNode = (node) => ({
+ key: node.key,
+ title: (
+
+ {this.getNodeIcon(node.title)}
+ {node.title}
+ ({node.count})
+
+ ),
+ children: node.children ? node.children.map(processNode) : undefined
+ });
+
+ return organizationData.map(processNode);
+ };
+
+ // 获取节点图标
+ getNodeIcon = (title) => {
+ if (title.includes('公司') || title.includes('集团')) return ;
+ if (title.includes('技术') || title.includes('开发') || title.includes('测试')) return ;
+ if (title.includes('产品') || title.includes('设计') || title.includes('体验')) return ;
+ if (title.includes('运营') || title.includes('市场') || title.includes('客户') || title.includes('商务')) return ;
+ if (title.includes('财务') || title.includes('会计') || title.includes('审计')) return ;
+ if (title.includes('人事') || title.includes('HR') || title.includes('招聘') || title.includes('培训') || title.includes('薪酬')) return ;
+ return ;
+ };
+
+ // 搜索处理
+ handleSearch = (values) => {
+ console.log('搜索参数:', values);
+ this.setState({ searchForm: values });
+ };
+
+ // 重置搜索
+ handleReset = () => {
+ this.formRef?.resetFields();
+ this.setState({ searchForm: {} });
+ };
+
+ // 树节点选择
+ onTreeSelect = (selectedKeys, info) => {
+ console.log('选中节点:', selectedKeys, info);
+ this.setState({ selectedKeys });
+ };
+
+ // 树节点展开
+ onTreeExpand = (expandedKeys) => {
+ this.setState({ expandedKeys });
+ };
+
+ // 刷新树数据
+ handleTreeRefresh = () => {
+ // console.log('刷新组织架构');
+ // 这里可以添加刷新树数据的逻辑
+ };
+
+ // 展开/收缩所有节点
+ handleTreeToggle = () => {
+ const { expandedKeys, organizationData } = this.state;
+ if (expandedKeys.length > 0) {
+ // 收缩所有节点
+ this.setState({ expandedKeys: [] });
+ } else {
+ // 展开所有节点
+ const getAllKeys = (nodes) => {
+ let keys = [];
+ nodes.forEach(node => {
+ keys.push(node.key);
+ if (node.children) {
+ keys = keys.concat(getAllKeys(node.children));
+ }
+ });
+ return keys;
+ };
+ this.setState({ expandedKeys: getAllKeys(organizationData) });
+ }
+ };
+
+ renderSimpleForm() {
+ const { prooperlog = {} } = this.props;
+ const { params = {} } = prooperlog;
+ const parentMethods = {
+ handleSearch: this.handleSearch,
+ handleFormReset: this.handleFormReset,
+ toggleForm: this.toggleForm,
+ submitButtons: styles.submitButtons,
+ params
+ };
+ return (
+
+ );
+ }
+
+ renderAdvancedForm() {
+ const { prooperlog: { params } } = this.props;
+ const parentMethods = {
+ handleSearch: this.handleSearch,
+ handleFormReset: this.handleFormReset,
+ toggleForm: this.toggleForm,
+ params
+ };
+
+ return (
+
+ );
+ }
+
+ renderForm() {
+ const { expandForm } = this.state;
+ return expandForm ? this.renderAdvancedForm() : this.renderSimpleForm();
+ }
+
+ // 分页处理
+ onPaginationChange = (page, pageSize) => {
+ this.setState({
+ pagination: {
+ ...this.state.pagination,
+ current: page,
+ pageSize,
+ }
+ });
+ };
+
+ // 显示新增弹窗
+ showAddModal = () => {
+ this.setState({ addModalVisible: true });
+ };
+
+ // 关闭新增弹窗
+ hideAddModal = () => {
+ this.setState({ addModalVisible: false });
+ };
+
+ // 新增成功回调
+ handleAddSuccess = (values) => {
+ console.log('新增成功:', values);
+
+ // 这里可以刷新列表数据
+ // 模拟添加到表格数据中
+ const newStaff = {
+ key: String(Date.now()),
+ id: this.state.tableData.length + 1,
+ name: values.name,
+ phone: values.age, // 年龄
+ idCard: values.department, // 部门
+ department: values.position, // 岗位
+ position: values.workType, // 工作性质
+ status: values.phone, // 电话
+ entryDate: new Date().toISOString().split('T')[0]
+ };
+
+ this.setState({
+ tableData: [...this.state.tableData, newStaff],
+ pagination: {
+ ...this.state.pagination,
+ total: this.state.pagination.total + 1
+ }
+ });
+ };
+
+ // 处理姓名点击事件
+ handleNameClick = (record) => {
+ console.log('查看员工薪酬信息:', record);
+ // 可以跳转到员工薪酬详情页面或显示详情模态框
+ };
+
+ // 处理查看详情
+ handleViewDetail = (record) => {
+ // 跳转到薪酬详情页面
+ history.push('/topnavbar00/salarymanage/salarydetail');
+ };
+
+ // 处理编辑
+ handleEdit = (record) => {
+ console.log('编辑薪酬:', record);
+ this.setState({
+ addModalVisible: true
+ });
+ };
+
+ // 处理删除
+ handleDelete = (record) => {
+ console.log('删除薪酬记录:', record);
+ // 这里可以弹出确认框并执行删除操作
+ };
+
render() {
+ const { tableData, pagination, expandedKeys, addModalVisible, addLoading } = this.state;
return (
- <>
-
- >
- )
+
+ {/* 主体内容 */}
+
+
+
+ {/* 左侧组织架构树 */}
+
+
+
+
+ 组织架构
+
+
+ }
+ size="small"
+ style={{ color: '#1890ff' }}
+ onClick={this.handleTreeRefresh}
+ title="刷新"
+ />
+ }
+ size="small"
+ style={{ color: '#1890ff' }}
+ onClick={this.handleTreeToggle}
+ title={expandedKeys.length > 0 ? "收缩全部" : "展开全部"}
+ />
+
+
+ }
+ className={styles.treeCard}
+ >
+
+
+
+
+
+
+ {/* 右侧人员信息区 */}
+
+ {/* 筛选条件 */}
+
+ {this.renderForm()}
+
+
+ {/* 操作按钮和统计 */}
+ {/*
+
+ 共 {pagination.total} 条记录
+
+
+ }
+ size="large"
+ className={styles.exportButton}
+ >
+ 导出
+
+ }
+ size="large"
+ className={styles.addButton}
+ onClick={this.showAddModal}
+ >
+ 新增
+
+
+
*/}
+
+ {/* 人员表格 */}
+
+
+
+ {/* 分页 */}
+
+
+ `显示 ${range[0]} 到 ${range[1]} 条,共 ${total} 条记录`
+ }
+ onChange={this.onPaginationChange}
+ onShowSizeChange={this.onPaginationChange}
+ />
+
+
+
+
+
+
+
+ {/* 新增人员弹窗 */}
+
+
+ );
}
}
-export default SalaryData
+export default SalaryData;
\ No newline at end of file
diff --git a/src/pages/salarymanage_salarydata/SalaryData.less b/src/pages/salarymanage_salarydata/SalaryData.less
index d5bff80..e5fd647 100644
--- a/src/pages/salarymanage_salarydata/SalaryData.less
+++ b/src/pages/salarymanage_salarydata/SalaryData.less
@@ -1,10 +1,523 @@
@import '~@/utils/utils.less';
-.frameContent {
- width: 100%;
+.staffInfoContainer {
+ min-height: 100vh;
height: 100vh;
- border: none;
- display: block;
- margin: 0;
- padding: 0;
+ // background-color: #f5f6fa;
+
+ .announcementBar {
+ background: #e6f7ff;
+ border: 1px solid #91d5ff;
+ margin-bottom: 16px;
+ border-radius: 6px;
+ overflow: hidden;
+
+ .announcement {
+ display: flex;
+ align-items: center;
+
+ .announcementLabel {
+ background-color: #fef3c7;
+ color: #92400e;
+ border-radius: 4px;
+ padding: 4px 8px;
+ font-size: 12px;
+ font-weight: 500;
+ margin-right: 12px;
+ white-space: nowrap;
+ display: flex;
+ align-items: center;
+
+ .anticon {
+ margin-right: 4px;
+ }
+ }
+
+ .scrollContainer {
+ flex: 1;
+ overflow: hidden;
+ white-space: nowrap;
+ }
+
+ .scrollContent {
+ display: inline-block;
+ animation: scroll 30s linear infinite;
+ color: #666;
+ font-size: 14px;
+ }
+ }
+ }
+
+ .mainContent {
+ // padding: 12px;
+
+ .contentCard {
+ .ant-card-head {
+ .ant-card-head-title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ }
+ }
+ }
+
+ .treeCard {
+ height: 600px;
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+
+ .treeHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 14px;
+ font-weight: 500;
+ }
+
+ .treeContainer {
+ height: 520px;
+ overflow-y: auto;
+
+ /* 自定义滚动条样式 */
+ &::-webkit-scrollbar {
+ width: 6px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: transparent;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: #d9d9d9;
+ border-radius: 3px;
+
+ &:hover {
+ background: #bfbfbf;
+ }
+ }
+
+ .orgTree {
+ .ant-tree-treenode {
+ padding: 2px 0;
+
+ .ant-tree-node-content-wrapper {
+ border-radius: 4px;
+ transition: all 0.3s ease;
+ padding: 4px 8px;
+
+ &:hover {
+ background-color: #f0f9ff;
+ transform: translateX(2px);
+ }
+
+ &.ant-tree-node-selected {
+ background-color: #e6f7ff !important;
+ box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);
+ }
+ }
+
+ .ant-tree-iconEle {
+ margin-right: 8px;
+ }
+
+ .ant-tree-title {
+ font-size: 14px;
+ }
+ }
+ }
+ }
+ }
+
+ /* Tree节点标题样式 */
+ .tree-node-title {
+ display: flex;
+ align-items: center;
+ width: 100%;
+
+ .node-title {
+ flex: 1;
+ font-size: 14px;
+ margin-left: 8px;
+ color: #333;
+ }
+
+ .node-count {
+ color: #999;
+ font-size: 12px;
+ margin-left: auto;
+ background: #f0f0f0;
+ padding: 1px 6px;
+ border-radius: 10px;
+ min-width: 20px;
+ text-align: center;
+ }
+ }
+
+ .searchCard {
+ margin-bottom: 16px;
+ border-radius: 8px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+
+ .ant-card-body {
+ padding: 16px;
+ }
+
+ /* 搜索表单容器样式 */
+ .searchFormContainer {
+ .searchForm {
+ .ant-form-item {
+ margin-bottom: 16px;
+
+ .ant-form-item-label {
+ font-weight: 500;
+ color: #333;
+
+ label {
+ color: #333 !important;
+ font-size: 14px;
+ }
+ }
+
+ .ant-input,
+ .ant-select-selector,
+ .ant-picker {
+ border-radius: 6px;
+ border: 1px solid #d9d9d9;
+ transition: all 0.3s ease;
+
+ &:hover {
+ border-color: #4c7bff;
+ }
+
+ &:focus,
+ &.ant-select-focused .ant-select-selector,
+ &.ant-picker-focused {
+ border-color: #2d5cf6;
+ box-shadow: 0 0 0 2px rgba(45, 92, 246, 0.2);
+ }
+ }
+
+ .ant-select-selection-placeholder,
+ .ant-input::placeholder,
+ .ant-picker-input input::placeholder {
+ color: #bfbfbf;
+ }
+ }
+ }
+ }
+
+ /* 搜索按钮样式 */
+ .searchButton {
+ background: linear-gradient(135deg, #2d5cf6 0%, #4c7bff 100%);
+ border: none;
+ border-radius: 6px;
+ color: white;
+ font-weight: 500;
+ font-size: 14px;
+ box-shadow: 0 2px 8px rgba(45, 92, 246, 0.3);
+ transition: all 0.3s ease;
+ height: 32px;
+ padding: 0 16px;
+
+ &:hover,
+ &:focus {
+ background: linear-gradient(135deg, #4c7bff 0%, #6b8fff 100%);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(45, 92, 246, 0.4);
+ color: white;
+ }
+
+ &:active {
+ transform: translateY(0);
+ }
+ }
+
+ /* 重置按钮样式 */
+ .resetButton {
+ background: #fff;
+ border: 1px solid #d9d9d9;
+ color: #666;
+ border-radius: 6px;
+ font-weight: 500;
+ font-size: 14px;
+ transition: all 0.3s ease;
+ height: 32px;
+ padding: 0 16px;
+
+ &:hover,
+ &:focus {
+ border-color: #4c7bff;
+ color: #2d5cf6;
+ }
+
+ .anticon {
+ color: #ff7875;
+ }
+ }
+
+ /* 展开按钮样式 */
+ .expandButton {
+ color: #2d5cf6;
+ font-size: 14px;
+ padding: 0 8px;
+ height: 32px;
+
+ &:hover,
+ &:focus {
+ color: #4c7bff;
+ }
+ }
+ }
+
+ .actionBar {
+
+
+
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+
+ .totalInfo {
+ color: #666;
+ font-size: 14px;
+ font-weight: 500;
+ }
+
+ .exportButton {
+ background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
+ border: none;
+ color: white;
+ border-radius: 8px;
+ font-weight: 500;
+ font-size: 14px;
+ box-shadow: 0 4px 12px rgba(82, 196, 26, 0.3);
+ transition: all 0.3s ease;
+ // margin-top: -8px;
+ height: 35px;
+ padding: 0 16px;
+
+
+ &:hover,
+ &:focus {
+ background: linear-gradient(135deg, #73d13d 0%, #95de64 100%);
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px rgba(82, 196, 26, 0.4);
+ color: white;
+ }
+
+ &:active {
+ transform: translateY(0);
+ }
+
+ .anticon {
+ color: white;
+ }
+ }
+
+
+ .addButton {
+ background: linear-gradient(135deg, #fa8c16 0%, #ffa940 100%);
+ border: none;
+ border-radius: 8px;
+ font-weight: 600;
+ font-size: 14px;
+ box-shadow: 0 4px 12px rgba(250, 140, 22, 0.3);
+ transition: all 0.3s ease;
+ // margin-top: -8px;
+ height: 35px;
+ padding: 0 16px;
+
+
+ &:hover,
+ &:focus {
+ background: linear-gradient(135deg, #ffa940 0%, #ffc069 100%);
+ transform: translateY(-2px);
+ box-shadow: 0 6px 20px rgba(250, 140, 22, 0.4);
+ }
+
+ &:active {
+ transform: translateY(0);
+ }
+ }
+ }
+
+ .tableCard {
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+ padding-bottom: 10px;
+
+ .ant-table-wrapper {
+ max-height: 600px;
+ overflow-y: auto;
+
+ /* 自定义滚动条样式 */
+ &::-webkit-scrollbar {
+ width: 8px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: #f5f5f5;
+ border-radius: 4px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: #d9d9d9;
+ border-radius: 4px;
+
+ &:hover {
+ background: #bfbfbf;
+ }
+ }
+
+ .ant-table-thead>tr>th {
+ background-color: #fafafa;
+ font-weight: 600;
+ color: #333;
+ border-bottom: 1px solid #e8e8e8;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ .ant-table-tbody>tr {
+ &:hover>td {
+ background-color: #f5f5f5 !important;
+ }
+
+ >td {
+ border-bottom: 1px solid #f0f0f0;
+ }
+ }
+ }
+
+ .paginationWrapper {
+ margin-top: 20px;
+ text-align: right;
+
+ .ant-pagination {
+ .ant-pagination-total-text {
+ color: #666;
+ }
+
+ .ant-pagination-item {
+ border-radius: 4px;
+
+ &.ant-pagination-item-active {
+ background-color: #2d5cf6;
+ border-color: #2d5cf6;
+ }
+ }
+
+ .ant-pagination-prev,
+ .ant-pagination-next {
+ border-radius: 4px;
+ }
+ }
+ }
+ }
+ }
+}
+
+.mainContent {
+ // padding: 12px;
+
+ .contentCard {
+ border: none !important;
+ .ant-card-head {
+ .ant-card-head-title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ }
+ }
+ }
+
+ .actionBar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+
+ .totalInfo {
+ color: #666;
+ font-size: 14px;
+ }
+ }
+
+ :global {
+ .ant-card-body {
+ padding: 12px 24px 0px 24px;
+ }
+ }
+}
+
+
+@keyframes scroll {
+ 0% {
+ transform: translateX(0);
+ }
+
+ 100% {
+ transform: translateX(-50%);
+ }
+}
+
+// 响应式设计
+@media (max-width: 1200px) {
+ .staffInfoContainer {
+ .mainContent {
+ .searchCard {
+ .ant-row {
+ .ant-col {
+ margin-bottom: 16px;
+ }
+ }
+ }
+ }
+ }
+}
+
+// 自定义主题色
+.ant-btn-primary {
+ background-color: #2d5cf6;
+ border-color: #2d5cf6;
+
+ &:hover,
+ &:focus {
+ background-color: #4c7bff;
+ border-color: #4c7bff;
+ }
+}
+
+.ant-tree .ant-tree-node-selected {
+ background-color: #e6f7ff !important;
+}
+
+.ant-select-focused .ant-select-selector,
+.ant-input-affix-wrapper-focused,
+.ant-input:focus,
+.ant-input-focused {
+ border-color: #2d5cf6 !important;
+ box-shadow: 0 0 0 2px rgba(45, 92, 246, 0.2) !important;
+}
+
+// 标签样式优化
+.ant-tag {
+ border-radius: 4px;
+ font-size: 12px;
+}
+
+// 按钮组间距调整
+.ant-space-item {
+ .ant-btn+.ant-btn {
+ margin-left: 8px;
+ }
+}
+
+// 表格链接按钮样式 - 限制在当前容器内,避免影响其他页面
+.staffInfoContainer .ant-btn-link {
+ padding: 0 4px;
+ font-size: 12px;
}
\ No newline at end of file
diff --git a/src/pages/salarymanage_salarydata/form/SalaryDataAdd.js b/src/pages/salarymanage_salarydata/form/SalaryDataAdd.js
new file mode 100644
index 0000000..3628160
--- /dev/null
+++ b/src/pages/salarymanage_salarydata/form/SalaryDataAdd.js
@@ -0,0 +1,197 @@
+import React, { PureComponent } from 'react';
+import {
+ Modal,
+ Form,
+ Input,
+ Select,
+ InputNumber,
+ Row,
+ Col,
+ message
+} from 'antd';
+import {
+ UserOutlined,
+ PhoneOutlined,
+ TeamOutlined,
+ ApartmentOutlined
+} from '@ant-design/icons';
+
+const { Option } = Select;
+
+class SalaryDataAdd extends PureComponent {
+ constructor(props) {
+ super(props);
+ this.formRef = React.createRef();
+ }
+
+ // 提交表单
+ handleSubmit = (values) => {
+ console.log('新增人员信息:', values);
+
+ // 这里可以调用API接口保存数据
+ // 模拟保存成功
+ message.success('新增人员成功!');
+
+ // 重置表单
+ this.formRef.current?.resetFields();
+
+ // 调用父组件的回调函数
+ if (this.props.onSuccess) {
+ this.props.onSuccess(values);
+ }
+
+ // 关闭弹窗
+ this.handleCancel();
+ };
+
+ // 取消操作
+ handleCancel = () => {
+ // 重置表单
+ this.formRef.current?.resetFields();
+
+ // 调用父组件的关闭回调
+ if (this.props.onCancel) {
+ this.props.onCancel();
+ }
+ };
+
+ render() {
+ const { visible, loading = false } = this.props;
+
+ return (
+ this.formRef.current?.submit()}
+ onCancel={this.handleCancel}
+ width={600}
+ confirmLoading={loading}
+ destroyOnClose={true}
+ maskClosable={false}
+ >
+
+
+ );
+ }
+}
+
+export default SalaryDataAdd;
diff --git a/src/pages/salarymanage_salarydata/form/SalaryDataRenderSimpleForm.js b/src/pages/salarymanage_salarydata/form/SalaryDataRenderSimpleForm.js
new file mode 100644
index 0000000..7a664cd
--- /dev/null
+++ b/src/pages/salarymanage_salarydata/form/SalaryDataRenderSimpleForm.js
@@ -0,0 +1,104 @@
+import React from 'react';
+import { Button, Col, Form, Input, Row, Select, Space, DatePicker } from 'antd';
+import { ClearOutlined, SearchOutlined } from '@ant-design/icons';
+import styles from "../SalaryData.less";
+
+const { Option } = Select;
+const SalaryDataRenderSimpleForm = (props) => {
+ const [form] = Form.useForm();
+ const { handleSearch, handleFormReset, params } = props;
+
+ React.useEffect(() => {
+ form.setFieldsValue({
+ name: params?.name,
+ phone: params?.phone,
+ month: params?.month,
+ });
+ }, [params]);
+
+ const onFinish = values => {
+ const searchParams = {
+ ...values,
+ month: values.month ? values.month.format('YYYY-MM') : undefined,
+ };
+ handleSearch && handleSearch(searchParams);
+ };
+
+ const myhandleFormReset = () => {
+ form.resetFields();
+ handleFormReset && handleFormReset();
+ };
+
+ return (
+
+
+
+ );
+
+};
+
+export default SalaryDataRenderSimpleForm;
diff --git a/src/pages/salarymanage_salarydata/form/SalaryDataRenderSimpleForm.less b/src/pages/salarymanage_salarydata/form/SalaryDataRenderSimpleForm.less
new file mode 100644
index 0000000..e69de29
diff --git a/src/pages/salarymanage_salarydetail/SalaryDetail.js b/src/pages/salarymanage_salarydetail/SalaryDetail.js
new file mode 100644
index 0000000..b7a026a
--- /dev/null
+++ b/src/pages/salarymanage_salarydetail/SalaryDetail.js
@@ -0,0 +1,395 @@
+
+import React, { PureComponent } from 'react';
+import { Card, Avatar, Breadcrumb, Tag, Descriptions, Table, Row, Col, Button, Divider, Progress, Statistic } from 'antd';
+import { UserOutlined, DollarCircleOutlined, FileTextOutlined, CalendarOutlined, BarChartOutlined, ArrowLeftOutlined, EditOutlined, PrinterOutlined, FileExcelOutlined, RiseOutlined, FallOutlined } from '@ant-design/icons';
+import { history } from 'umi';
+import styles from './SalaryDetail.less';
+import girlAvatar from './girl.png';
+
+// 薪资明细表数据
+const salaryDetailData = [
+ { key: '1', item: '基本工资', standard: '¥10,000', should: '¥10,000', deduct: '-', actual: '¥10,000', remark: '-' },
+ { key: '2', item: '绩效工资', standard: '¥6,000', should: '¥5,500', deduct: '¥500', actual: '¥5,500', remark: 'KPI未达标' },
+ { key: '3', item: '岗位津贴', standard: '¥1,200', should: '¥1,200', deduct: '-', actual: '¥1,200', remark: '主管级津贴' },
+ { key: '4', item: '餐补', standard: '¥600', should: '¥600', deduct: '-', actual: '¥600', remark: '22个工作日' },
+ { key: '5', item: '交通补贴', standard: '¥800', should: '¥800', deduct: '-', actual: '¥800', remark: '-' },
+ { key: '6', item: '通讯补贴', standard: '¥300', should: '¥300', deduct: '-', actual: '¥300', remark: '-' },
+ { key: '7', item: '加班费', standard: '-', should: '¥1,100', deduct: '-', actual: '¥1,100', remark: '国庆加班3天' },
+ { key: '8', item: '小计', standard: '-', should: '¥19,500', deduct: '¥500', actual: '¥19,000', remark: '-', isSubtotal: true },
+ { key: '9', item: '养老保险', standard: '8%', should: '-', deduct: '¥800', actual: '-¥800', remark: '个人缴纳' },
+ { key: '10', item: '医疗保险', standard: '2%', should: '-', deduct: '¥200', actual: '-¥200', remark: '个人缴纳' },
+ { key: '11', item: '失业保险', standard: '0.5%', should: '-', deduct: '¥50', actual: '-¥50', remark: '个人缴纳' },
+ { key: '12', item: '住房公积金', standard: '12%', should: '-', deduct: '¥1,200', actual: '-¥1,200', remark: '个人缴纳' },
+ { key: '13', item: '个人所得税', standard: '-', should: '-', deduct: '¥1,430', actual: '-¥1,430', remark: '累计预扣法' },
+ { key: '14', item: '合计扣减', standard: '-', should: '-', deduct: '¥3,680', actual: '-¥3,680', remark: '-', isSubtotal: true },
+ { key: '15', item: '实发工资', standard: '-', should: '-', deduct: '-', actual: '¥15,320', remark: '-', isTotal: true },
+];
+
+// 考勤数据
+const attendanceData = [
+ { type: '出勤', count: 21, color: '#52c41a' },
+ { type: '迟到/早退', count: 1, color: '#faad14' },
+ { type: '缺勤', count: 0.5, color: '#ff4d4f' },
+ { type: '加班', count: 3, color: '#1890ff' },
+];
+class SalaryDetail extends PureComponent {
+ handleGoBack = () => {
+ // 跳转到薪酬管理页面
+ history.push('/topnavbar00/salarymanage/salarydata');
+ };
+
+ render() {
+ return (
+
+
+ {/* 面包屑导航 */}
+
+
+ {/* 返回按钮 */}
+ }
+ onClick={this.handleGoBack}
+ style={{ marginBottom: '24px', padding: 0 }}
+ >
+ 返回
+
+
+ {/* 个人信息卡片 */}
+
+
+
+
+
+
+
+
+ 林雅婷
+ 员工ID: HR20230085
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }>
+ 编辑信息
+
+ }>
+ 导出资料
+
+ }>
+ 打印档案
+
+
+
+
+
+
+ {/* 薪资概览 */}
+
+
+
+
+
税前工资
+ +5% 上月
+
+
+ ¥18,500
+ /月
+
+
+
年度累计: ¥203,500
+
上月工资: ¥17,600
+
+
+
+
+
+
+
税后工资
+ +4.8% 上月
+
+
+ ¥15,320
+ /月
+
+
+
年度累计: ¥168,520
+
上月工资: ¥14,620
+
+
+
+
+
+ 薪资构成
+
+
+
+
+
+
+
+ {/* 工资明细表 */}
+
+
+
2023年10月工资明细
+
+ (
+
+ {text}
+
+ )
+ },
+ {
+ title: '标准',
+ dataIndex: 'standard',
+ key: 'standard',
+ render: (text, record) => (
+
+ {text}
+
+ )
+ },
+ {
+ title: '应发',
+ dataIndex: 'should',
+ key: 'should',
+ render: (text, record) => (
+
+ {text}
+
+ )
+ },
+ {
+ title: '扣减',
+ dataIndex: 'deduct',
+ key: 'deduct',
+ render: (text, record) => (
+
+ {text}
+
+ )
+ },
+ {
+ title: '实发',
+ dataIndex: 'actual',
+ key: 'actual',
+ render: (text, record) => (
+
+ {text}
+
+ )
+ },
+ {
+ title: '备注',
+ dataIndex: 'remark',
+ key: 'remark',
+ render: (text, record) => (
+
+ {text}
+
+ )
+ },
+ ]}
+ dataSource={salaryDetailData}
+ pagination={false}
+ size="small"
+ rowClassName={(record) => {
+ if (record.isTotal) return 'salary-total-row';
+ if (record.isSubtotal) return 'salary-subtotal-row';
+ return 'salary-normal-row';
+ }}
+ />
+
+
+ {/* 考勤记录 */}
+
+
+
2023年10月考勤记录
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 10-01 出勤
+ 10-02 出勤
+ 10-03 出勤
+ 10-04 迟到
+ 10-05 出勤
+ 10-06 出勤
+ 10-07 出勤
+ 10-08 出勤
+ 10-09 出勤
+ 10-10 出勤
+ 10-11 出勤
+ 10-12 出勤
+ 10-13 出勤
+ 10-14 出勤
+ 10-15 出勤
+ 10-16 出勤
+ 10-17 出勤
+ 10-18 出勤
+ 10-19 出勤
+ 10-20 出勤
+ 10-21 缺勤
+ 10-22 出勤
+ 10-23 出勤
+ 10-24 出勤
+ 10-25 出勤
+ 10-26 出勤
+ 10-27 出勤
+ 10-28 出勤
+ 10-29 出勤
+ 10-30 出勤
+ 10-31 出勤
+
+
+
+
+
+ );
+ }
+}
+
+export default SalaryDetail;
diff --git a/src/pages/salarymanage_salarydetail/SalaryDetail.less b/src/pages/salarymanage_salarydetail/SalaryDetail.less
new file mode 100644
index 0000000..7979a1d
--- /dev/null
+++ b/src/pages/salarymanage_salarydetail/SalaryDetail.less
@@ -0,0 +1,489 @@
+
+.particularsContainer {
+ background: #f5f6fa;
+ min-width: 70vw;
+ min-height: 100vh;
+ max-height: 100vh;
+ overflow-y: auto;
+ overflow-x: hidden;
+ padding: 16px 24px 32px 24px;
+}
+
+// 最外层容器样式
+:global(.salary-detail-main-container) {
+ // 设置固定高度以启用滚动,考虑padding
+ height: 100vh;
+ overflow-x: hidden; // 防止横向滚动条
+ overflow-y: auto; // 允许纵向滚动
+ box-sizing: border-box; // 包含padding在高度计算内
+
+ // 滚动条样式
+ &::-webkit-scrollbar {
+ width: 8px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: rgba(0,0,0,0.05);
+ border-radius: 4px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: rgba(0,0,0,0.2);
+ border-radius: 4px;
+
+ &:hover {
+ background: rgba(0,0,0,0.3);
+ }
+ }
+}
+
+// 最外层卡片样式优化
+:global(.salary-detail-card) {
+ // Card高度自适应,让外层容器处理滚动
+ width: 100%;
+ margin-bottom: 24px; // 确保底部有足够间距,不被截断
+
+ // Card body 样式优化
+ .ant-card-body {
+ padding: 24px;
+ padding-bottom: 32px; // 增加底部padding,确保内容不被截断
+
+ // 确保内容之间有适当的间距
+ > * {
+ margin-bottom: 24px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+ }
+
+ // 表格在容器内的优化
+ .ant-table-wrapper {
+ // 确保表格可以水平滚动
+ overflow-x: auto;
+ margin-bottom: 16px; // 添加底部间距
+
+ // 表格滚动条样式
+ &::-webkit-scrollbar {
+ height: 6px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: #f1f1f1;
+ border-radius: 3px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: #c1c1c1;
+ border-radius: 3px;
+
+ &:hover {
+ background: #a8a8a8;
+ }
+ }
+
+ .ant-table-container {
+ overflow: visible;
+ }
+
+ .ant-table {
+ overflow: visible;
+ // 确保表格最小宽度
+ min-width: 800px;
+ }
+ }
+
+ // 考勤标签区域优化
+ .attendance-tags {
+ margin-top: 16px;
+ margin-bottom: 24px; // 确保底部有足够空间
+
+ .ant-tag {
+ margin-bottom: 8px; // 为换行的标签添加底部间距
+ }
+ }
+}
+
+// 薪资相关样式
+:global(.salary-total-row) {
+ background: #1890ff !important;
+
+ td {
+ background: #1890ff !important;
+ color: #fff !important;
+ font-weight: bold !important;
+ }
+
+ &:hover td {
+ background: #40a9ff !important;
+ }
+}
+
+:global(.salary-subtotal-row) {
+ background: #f5f5f5 !important;
+
+ td {
+ background: #f5f5f5 !important;
+ font-weight: 500 !important;
+ }
+
+ &:hover td {
+ background: #e6f7ff !important;
+ }
+}
+
+:global(.salary-normal-row) {
+ &:hover td {
+ background: #fafafa !important;
+ }
+}
+
+.announcementBar {
+ background: #fff;
+ box-shadow: 0 2px 8px #f0f1f2;
+ padding: 8px 24px;
+ font-size: 14px;
+ color: #666;
+ display: flex;
+ align-items: center;
+ gap: 24px;
+}
+.announcementTitle {
+ color: #2d5cf6;
+ font-weight: 500;
+ margin-right: 8px;
+}
+.announcementItem {
+ margin-right: 16px;
+}
+
+.breadcrumb {
+ margin: 24px 0 8px 0;
+ font-size: 13px;
+}
+.backBtn {
+ margin-bottom: 12px;
+}
+
+.profileCard {
+ margin-bottom: 24px;
+ border-radius: 12px;
+ box-shadow: 0 2px 8px #f0f1f2;
+}
+.avatarCol {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.profileInfoHeader {
+ padding: 8px 0;
+}
+.nameSection {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 16px;
+}
+.profileName {
+ font-size: 24px;
+ font-weight: bold;
+ color: #222;
+}
+.profileDept {
+ color: #888;
+ font-size: 15px;
+ margin-left: 4px;
+}
+.profileDetails {
+ margin-top: 8px;
+}
+.detailItem {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+.detailLabel {
+ font-size: 13px;
+ color: #888;
+ font-weight: 500;
+}
+.detailValue {
+ font-size: 15px;
+ color: #333;
+ font-weight: 600;
+}
+.statusTag {
+ margin: 0;
+ font-weight: 500;
+}
+.actionCol {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.actionButtons {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ width: 100%;
+ max-width: 120px;
+}
+.editBtn {
+ width: 100%;
+ height: 40px;
+ border-radius: 8px;
+ font-weight: 500;
+}
+.printBtn {
+ width: 100%;
+ height: 40px;
+ border-radius: 8px;
+ font-weight: 500;
+}
+
+@media (max-width: 768px) {
+ .actionButtons {
+ flex-direction: row;
+ max-width: none;
+ }
+ .nameSection {
+ flex-wrap: wrap;
+ gap: 8px;
+ }
+
+ // 移动端优化
+ :global(.salary-detail-main-container) {
+ padding: 16px !important;
+ }
+
+ :global(.salary-detail-card) {
+ // 移动端Card样式
+ .ant-card-body {
+ padding: 16px;
+ padding-bottom: 24px; // 移动端也需要底部间距
+ }
+
+ // 移动端表格水平滚动
+ .ant-table-wrapper {
+ .ant-table {
+ min-width: 600px; // 移动端最小宽度
+ }
+ }
+
+ // 移动端考勤标签优化
+ .attendance-tags {
+ margin-bottom: 20px;
+ }
+ }
+}
+
+.infoCard {
+ margin-bottom: 24px;
+ border-radius: 12px;
+ box-shadow: 0 2px 8px #f0f1f2;
+
+ // 表格包装器样式
+ .tableWrapper {
+ margin-top: 16px;
+ border-radius: 8px;
+ overflow: hidden;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
+
+ // 自定义滚动条样式
+ &::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: #f5f5f5;
+ border-radius: 3px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: #d9d9d9;
+ border-radius: 3px;
+
+ &:hover {
+ background: #bfbfbf;
+ }
+ }
+ }
+
+ // 自定义表格样式
+ .customTable {
+ .ant-table-wrapper {
+ .ant-table-thead > tr > th {
+ background-color: #2c3e50 !important;
+ color: #ecf0f1 !important;
+ font-weight: 600;
+ border-bottom: 1px solid #34495e;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ text-align: center;
+ font-size: 14px;
+ padding: 16px 12px;
+ letter-spacing: 0.5px;
+ }
+
+ .ant-table-tbody > tr {
+ &:hover > td {
+ background-color: #f8f9ff !important;
+ }
+
+ &:nth-child(even) {
+ background-color: #fafbfc;
+ }
+
+ > td {
+ border-bottom: 1px solid #e8e8e8;
+ padding: 14px 12px;
+ font-size: 13px;
+ color: #333;
+ vertical-align: top;
+ line-height: 1.6;
+ }
+ }
+
+ .ant-table {
+ border-radius: 8px;
+ overflow: hidden;
+ border: 1px solid #e8e8e8;
+ }
+
+ .ant-table-container {
+ border-radius: 8px;
+ }
+ }
+ }
+
+ // 表格通用样式(为了向后兼容)
+ .ant-table-wrapper {
+ .ant-table-thead > tr > th {
+ background-color: #2c3e50 !important;
+ color: #ecf0f1 !important;
+ font-weight: 600;
+ border-bottom: 1px solid #34495e;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ text-align: center;
+ font-size: 13px;
+ }
+
+ .ant-table-tbody > tr {
+ &:hover > td {
+ background-color: #f8f9ff !important;
+ }
+
+ > td {
+ border-bottom: 1px solid #e8e8e8;
+ padding: 12px 8px;
+ font-size: 13px;
+ color: #333;
+ vertical-align: top;
+ }
+ }
+
+ .ant-table {
+ border-radius: 8px;
+ overflow: hidden;
+ }
+ }
+}
+
+.careerTimeline {
+ margin-top: 16px;
+ padding-left: 12px;
+ border-left: 2px solid #2d5cf6;
+}
+.timelineItem {
+ position: relative;
+ margin-bottom: 32px;
+ padding-left: 24px;
+}
+.timelineDot {
+ position: absolute;
+ left: -13px;
+ top: 8px;
+ width: 12px;
+ height: 12px;
+ background: #d9d9d9;
+ border-radius: 50%;
+ border: 2px solid #fff;
+ box-shadow: 0 0 0 2px #2d5cf6;
+}
+.timelineDotActive {
+ background: #2d5cf6;
+ box-shadow: 0 0 0 2px #2d5cf6;
+}
+.timelineContent {
+ margin-left: 8px;
+}
+.timelineHeader {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-weight: 500;
+ font-size: 16px;
+}
+.timelineCompany {
+ color: #222;
+}
+.timelinePosition {
+ color: #555;
+ font-size: 15px;
+ margin-top: 2px;
+}
+.timelineDuration {
+ color: #888;
+ font-size: 13px;
+ margin-top: 2px;
+}
+.timelineDescription {
+ color: #444;
+ font-size: 14px;
+ margin-top: 4px;
+}
+
+.orgChartWrapper {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: #f5f6fa;
+ border-radius: 8px;
+ padding: 16px;
+ min-height: 180px;
+}
+.orgChartImg {
+ max-width: 100%;
+ max-height: 220px;
+ object-fit: contain;
+}
+
+.footer {
+ margin-top: 32px;
+ text-align: center;
+ color: #888;
+ font-size: 13px;
+}
+.footerLinks {
+ margin-bottom: 8px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 16px;
+ justify-content: center;
+}
+.footerLinks a {
+ color: #888;
+ text-decoration: none;
+ transition: color 0.2s;
+}
+.footerLinks a:hover {
+ color: #2d5cf6;
+}
+.footerCopyright {
+ margin-top: 4px;
+}
diff --git a/src/pages/salarymanage_salaryruleset/SalaryRuleset.js b/src/pages/salarymanage_salaryruleset/SalaryRuleset.js
index 2f23f6e..070fed7 100644
--- a/src/pages/salarymanage_salaryruleset/SalaryRuleset.js
+++ b/src/pages/salarymanage_salaryruleset/SalaryRuleset.js
@@ -1,14 +1,636 @@
-import React, {Fragment, PureComponent} from 'react';
+import React, { PureComponent } from 'react';
+import {
+ Card,
+ Tree,
+ Input,
+ Button,
+ DatePicker,
+ Select,
+ Table,
+ Form,
+ Row,
+ Col,
+ Space,
+ Divider,
+ message
+} from 'antd';
+import {
+ SaveOutlined,
+ CloseOutlined,
+ PlusOutlined,
+ DeleteOutlined,
+ ExpandOutlined,
+ FilterOutlined,
+ DownOutlined,
+ UserOutlined,
+ TeamOutlined,
+ ApartmentOutlined,
+ SyncOutlined,
+ DollarOutlined,
+ SettingOutlined
+} from '@ant-design/icons';
+import dayjs from 'dayjs';
import styles from './SalaryRuleset.less';
+
+const { Option } = Select;
+const { TextArea } = Input;
+
class SalaryRuleset extends PureComponent {
- render() {
+ constructor(props) {
+ super(props);
+ this.state = {
+ selectedOrgKeys: [],
+ expandedKeys: ['0-0', '0-0-0', '0-0-1', '0-0-2'],
+ form: {
+ ruleName: '',
+ applicableOrg: '集团公司/北京分公司',
+ effectiveDate: dayjs('2023-07-01'),
+ socialBase: '员工基本工资',
+ fundBase: '员工基本工资',
+ taxThreshold: '5000',
+ specialDeduction: '自动计算'
+ },
+ salaryItems: [
+ {
+ key: '1',
+ name: '基本工资',
+ type: '固定工资',
+ taxType: '税前',
+ formula: '岗位工资基数 * 职级系数'
+ },
+ {
+ key: '2',
+ name: '绩效奖金',
+ type: '浮动工资',
+ taxType: '税前',
+ formula: '基本工资 * 绩效系数'
+ },
+ {
+ key: '3',
+ name: '交通补贴',
+ type: '补贴',
+ taxType: '税后',
+ formula: '固定金额 500'
+ }
+ ],
+ insuranceRates: [
+ { key: '1', name: '养老保险', companyRate: '16%', personalRate: '8%' },
+ { key: '2', name: '医疗保险', companyRate: '9.5%', personalRate: '2%' },
+ { key: '3', name: '失业保险', companyRate: '0.5%', personalRate: '0.5%' },
+ { key: '4', name: '公积金', companyRate: '12%', personalRate: '12%' }
+ ],
+ organizationData: [
+ {
+ title: '飞利信科技有限公司',
+ key: '0-0',
+ count: 356,
+ children: [
+ {
+ title: '技术部',
+ key: '0-0-0',
+ count: 120,
+ children: [
+ { title: '前端组', key: '0-0-0-0', count: 45 },
+ { title: '后端组', key: '0-0-0-1', count: 52 },
+ { title: '测试组', key: '0-0-0-2', count: 23 }
+ ],
+ },
+ {
+ title: '产品部',
+ key: '0-0-1',
+ count: 68,
+ children: [
+ { title: '产品设计组', key: '0-0-1-0', count: 28 },
+ { title: '用户体验组', key: '0-0-1-1', count: 25 },
+ { title: '产品运营组', key: '0-0-1-2', count: 15 }
+ ],
+ },
+ {
+ title: '运营部',
+ key: '0-0-2',
+ count: 52,
+ children: [
+ { title: '市场营销组', key: '0-0-2-0', count: 22 },
+ { title: '客户服务组', key: '0-0-2-1', count: 18 },
+ { title: '商务合作组', key: '0-0-2-2', count: 12 }
+ ],
+ },
+ {
+ title: '财务部',
+ key: '0-0-3',
+ count: 32,
+ children: [
+ { title: '会计组', key: '0-0-3-0', count: 18 },
+ { title: '审计组', key: '0-0-3-1', count: 14 }
+ ],
+ },
+ {
+ title: '人事部',
+ key: '0-0-4',
+ count: 84,
+ children: [
+ { title: 'HR专员组', key: '0-0-4-0', count: 25 },
+ { title: '招聘组', key: '0-0-4-1', count: 22 },
+ { title: '培训组', key: '0-0-4-2', count: 18 },
+ { title: '薪酬组', key: '0-0-4-3', count: 19 }
+ ],
+ },
+ ],
+ },
+ ]
+ };
+ }
+
+ // 获取处理后的树形数据
+ getTreeData = () => {
+ const { organizationData } = this.state;
+
+ const processNode = (node) => ({
+ key: node.key,
+ title: (
+
+ {this.getNodeIcon(node.title)}
+ {node.title}
+ ({node.count})
+
+ ),
+ children: node.children ? node.children.map(processNode) : undefined
+ });
+
+ return organizationData.map(processNode);
+ };
+
+ // 获取节点图标
+ getNodeIcon = (title) => {
+ if (title.includes('公司') || title.includes('集团')) return ;
+ if (title.includes('技术') || title.includes('开发') || title.includes('测试')) return ;
+ if (title.includes('产品') || title.includes('设计') || title.includes('体验')) return ;
+ if (title.includes('运营') || title.includes('市场') || title.includes('客户') || title.includes('商务')) return ;
+ if (title.includes('财务') || title.includes('会计') || title.includes('审计')) return ;
+ if (title.includes('人事') || title.includes('HR') || title.includes('招聘') || title.includes('培训') || title.includes('薪酬')) return ;
+ return ;
+ };
+
+ // 刷新树数据
+ handleTreeRefresh = () => {
+ // 这里可以添加刷新树数据的逻辑
+ };
+
+ // 展开/收缩所有节点
+ handleTreeToggle = () => {
+ const { expandedKeys, organizationData } = this.state;
+ if (expandedKeys.length > 0) {
+ // 收缩所有节点
+ this.setState({ expandedKeys: [] });
+ } else {
+ // 展开所有节点
+ const getAllKeys = (nodes) => {
+ let keys = [];
+ nodes.forEach(node => {
+ keys.push(node.key);
+ if (node.children) {
+ keys = keys.concat(getAllKeys(node.children));
+ }
+ });
+ return keys;
+ };
+ this.setState({ expandedKeys: getAllKeys(organizationData) });
+ }
+ };
+
+ // 薪酬项表格列配置
+ salaryColumns = [
+ {
+ title: '薪酬项名称',
+ dataIndex: 'name',
+ key: 'name',
+ width: 150,
+ render: (text, record, index) => (
+ this.handleSalaryItemChange(index, 'name', e.target.value)}
+ />
+ )
+ },
+ {
+ title: '类型',
+ dataIndex: 'type',
+ key: 'type',
+ width: 120,
+ render: (text, record, index) => (
+
+ )
+ },
+ {
+ title: '计税方式',
+ dataIndex: 'taxType',
+ key: 'taxType',
+ width: 120,
+ render: (text, record, index) => (
+
+ )
+ },
+ {
+ title: '计算公式',
+ dataIndex: 'formula',
+ key: 'formula',
+ width: 250,
+ render: (text, record, index) => (
+ this.handleSalaryItemChange(index, 'formula', e.target.value)}
+ />
+ )
+ },
+ {
+ title: '操作',
+ key: 'action',
+ width: 80,
+ render: (_, record, index) => (
+ }
+ onClick={() => this.deleteSalaryItem(index)}
+ />
+ )
+ }
+ ];
+
+ // 保险缴费比例表格列配置
+ insuranceColumns = [
+ {
+ title: '项目',
+ dataIndex: 'name',
+ key: 'name',
+ width: 120
+ },
+ {
+ title: '单位比例',
+ dataIndex: 'companyRate',
+ key: 'companyRate',
+ width: 120,
+ render: (text, record, index) => (
+ this.handleInsuranceRateChange(index, 'companyRate', e.target.value)}
+ />
+ )
+ },
+ {
+ title: '个人比例',
+ dataIndex: 'personalRate',
+ key: 'personalRate',
+ width: 120,
+ render: (text, record, index) => (
+ this.handleInsuranceRateChange(index, 'personalRate', e.target.value)}
+ />
+ )
+ }
+ ];
+
+ // 组织树选择
+ onTreeSelect = (selectedKeys, info) => {
+ console.log('选中节点:', selectedKeys, info);
+ this.setState({ selectedOrgKeys: selectedKeys });
+ if (selectedKeys.length > 0) {
+ const selectedNode = info.node;
+ // 从节点的title中提取组织名称
+ const orgName = selectedNode.title.props ?
+ selectedNode.title.props.children[1].props.children :
+ selectedNode.title;
+ this.handleFormChange('applicableOrg', orgName);
+ }
+ };
- return (
- <>
-
- >
- )
+ // 树展开/收缩
+ onTreeExpand = (expandedKeys) => {
+ this.setState({ expandedKeys });
+ };
+
+ // 表单字段变化
+ handleFormChange = (field, value) => {
+ this.setState(prevState => ({
+ form: {
+ ...prevState.form,
+ [field]: value
+ }
+ }));
+ };
+
+ // 薪酬项变化
+ handleSalaryItemChange = (index, field, value) => {
+ const { salaryItems } = this.state;
+ const newItems = [...salaryItems];
+ newItems[index][field] = value;
+ this.setState({ salaryItems: newItems });
+ };
+
+ // 删除薪酬项
+ deleteSalaryItem = (index) => {
+ const { salaryItems } = this.state;
+ const newItems = salaryItems.filter((_, i) => i !== index);
+ this.setState({ salaryItems: newItems });
+ message.success('已删除薪酬项');
+ };
+
+ // 添加薪酬项
+ addSalaryItem = () => {
+ const { salaryItems } = this.state;
+ const newItem = {
+ key: Date.now().toString(),
+ name: '',
+ type: '固定工资',
+ taxType: '税前',
+ formula: ''
+ };
+ this.setState({ salaryItems: [...salaryItems, newItem] });
+ };
+
+ // 保险费率变化
+ handleInsuranceRateChange = (index, field, value) => {
+ const { insuranceRates } = this.state;
+ const newRates = [...insuranceRates];
+ newRates[index][field] = value;
+ this.setState({ insuranceRates: newRates });
+ };
+
+ // 保存规则
+ handleSave = () => {
+ const { form, salaryItems, insuranceRates } = this.state;
+
+ if (!form.ruleName.trim()) {
+ message.error('请输入规则名称');
+ return;
}
+
+ // 这里应该调用API保存数据
+ console.log('保存数据:', {
+ form,
+ salaryItems,
+ insuranceRates
+ });
+
+ message.success('薪酬规则保存成功');
+ };
+
+ // 取消操作
+ handleCancel = () => {
+ // 重置表单或返回上一页
+ message.info('已取消操作');
+ };
+
+ render() {
+ const { selectedOrgKeys, expandedKeys, form, salaryItems, insuranceRates } = this.state;
+
+ return (
+
+
+
+
+ {/* 左侧组织架构树 */}
+
+
+
+
+ 组织架构
+
+
+ }
+ size="small"
+ style={{ color: '#1890ff' }}
+ onClick={this.handleTreeRefresh}
+ title="刷新"
+ />
+ }
+ size="small"
+ style={{ color: '#1890ff' }}
+ onClick={this.handleTreeToggle}
+ title={expandedKeys.length > 0 ? "收缩全部" : "展开全部"}
+ />
+
+
+ }
+ className={styles.treeCard}
+ >
+
+
+
+
+
+
+ {/* 右侧主要内容 */}
+
+
+ {/* 头部操作区 */}
+
+
薪酬计算规则设置
+
+ }
+ onClick={this.handleSave}
+ >
+ 保存规则
+
+ }
+ onClick={this.handleCancel}
+ >
+ 取消
+
+
+
+
+ {/* 表单内容区 */}
+
+ {/* 基本信息 */}
+
+
+
+
+
+ this.handleFormChange('ruleName', e.target.value)}
+ />
+
+
+
+
+
+
+
+
+
+
+
+ this.handleFormChange('effectiveDate', date)}
+ />
+
+
+
+
+
+ {/* 薪酬项设置 */}
+
}
+ onClick={this.addSalaryItem}
+ >
+ 添加薪酬项
+
+ }
+ >
+
+
+
+ {/* 社保公积金设置 */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 个税设置 */}
+
+
+
+
+
+ this.handleFormChange('taxThreshold', e.target.value)}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
}
-export default SalaryRuleset
+export default SalaryRuleset;
\ No newline at end of file
diff --git a/src/pages/salarymanage_salaryruleset/SalaryRuleset.less b/src/pages/salarymanage_salaryruleset/SalaryRuleset.less
index d5bff80..cecaad4 100644
--- a/src/pages/salarymanage_salaryruleset/SalaryRuleset.less
+++ b/src/pages/salarymanage_salaryruleset/SalaryRuleset.less
@@ -1,10 +1,547 @@
@import '~@/utils/utils.less';
-.frameContent {
- width: 100%;
+// 容器样式 - 参考SalaryData的staffInfoContainer
+.container {
+ min-height: 100vh;
+ height: 100vh;
+ overflow: hidden;
+ // background-color: #f5f6fa;
+}
+
+// 主内容区域 - 参考SalaryData的mainContent布局
+.mainContent {
height: 100vh;
- border: none;
- display: block;
- margin: 0;
- padding: 0;
+ overflow: hidden;
+ :global {
+ .ant-card-body {
+ padding: 12px 24px;
+ }
+ }
+ .contentCard {
+ height: 100%;
+ border: none !important;
+
+ .ant-card-body {
+ padding: 12px 24px;
+ height: calc(100vh - 24px);
+ overflow: hidden;
+ }
+
+ .ant-card-head {
+ .ant-card-head-title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ }
+ }
+ }
+}
+
+// 组织架构树卡片样式 - 对应左侧Col span={5},完全参考SalaryData样式
+.treeCard {
+ height: 600px;
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+
+ .treeHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 14px;
+ font-weight: 500;
+ }
+
+ .treeContainer {
+ height: 520px;
+ overflow-y: auto;
+
+ /* 使用细滚动条,浅色调 */
+ &::-webkit-scrollbar {
+ width: 6px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.02);
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: rgba(0, 0, 0, 0.15);
+ border-radius: 3px;
+
+ &:hover {
+ background: rgba(0, 0, 0, 0.25);
+ }
+ }
+
+ .orgTree {
+ .ant-tree-treenode {
+ padding: 2px 0;
+
+ .ant-tree-node-content-wrapper {
+ border-radius: 4px;
+ transition: all 0.3s ease;
+ padding: 4px 8px;
+
+ &:hover {
+ background-color: #f0f9ff;
+ transform: translateX(2px);
+ }
+
+ &.ant-tree-node-selected {
+ background-color: #e6f7ff !important;
+ box-shadow: 0 2px 8px rgba(24, 144, 255, 0.2);
+ }
+ }
+
+ .ant-tree-iconEle {
+ margin-right: 8px;
+ }
+
+ .ant-tree-title {
+ font-size: 14px;
+ }
+ }
+ }
+ }
+}
+
+/* Tree节点标题样式 */
+.tree-node-title {
+ display: flex;
+ align-items: center;
+ width: 100%;
+
+ .node-title {
+ flex: 1;
+ font-size: 14px;
+ margin-left: 8px;
+ color: #333;
+ }
+
+ .node-count {
+ color: #999;
+ font-size: 12px;
+ margin-left: auto;
+ background: #f0f0f0;
+ padding: 1px 6px;
+ border-radius: 10px;
+ min-width: 20px;
+ text-align: center;
+ }
+}
+
+// 主要内容卡片样式 - 对应右侧Col span={19},优化滚动显示
+.mainCard {
+ height: calc(100vh - 60px); // 减少预留空间,最大化可用高度
+ min-height: 700px; // 增加最小高度确保内容充分显示
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+ overflow: hidden; // 确保卡片本身不产生滚动条
+
+ .ant-card-body {
+ padding: 0;
+ height: 100%;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ }
+}
+
+// 头部区域
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 24px;
+ border-bottom: 1px solid #f0f0f0;
+ flex-shrink: 0;
+ height: 64px; // 固定头部高度
+
+ h2 {
+ margin: 0;
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ }
+}
+
+// 内容区域 - 完全展示所有内容,超出时显示滚动条
+.content {
+ flex: 1;
+ padding: 20px 24px 28px 24px; // 增加上下内边距,提升整体视觉效果
+ overflow-y: auto;
+ overflow-x: hidden;
+ min-height: 0; // 关键:允许flex子元素收缩
+ height: calc(100vh - 160px); // 减去头部高度和其他空间,强制设置高度
+ max-height: calc(100vh - 160px); // 明确最大高度,确保滚动条出现
+
+ /* 使用细滚动条,浅色调 */
+ &::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.02);
+ border-radius: 4px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: rgba(0, 0, 0, 0.18);
+ border-radius: 4px;
+
+ &:hover {
+ background: rgba(0, 0, 0, 0.28);
+ }
+
+ &:active {
+ background: rgba(0, 0, 0, 0.35);
+ }
+ }
+
+ /* 滚动条角落 */
+ &::-webkit-scrollbar-corner {
+ background: rgba(0, 0, 0, 0.02);
+ }
+
+ // Firefox滚动条设置为细和浅色
+ scrollbar-width: thin;
+ scrollbar-color: rgba(0, 0, 0, 0.18) rgba(0, 0, 0, 0.02);
+
+ // 第一个卡片顶部间距
+ > :first-child {
+ margin-top: 0;
+ }
+
+ // 最后一个卡片底部间距
+ > :last-child {
+ margin-bottom: 0;
+ }
+}
+
+// 表单卡片 - 确保内容完全显示
+.formCard {
+ margin-bottom: 20px; // 增加卡片间距,提升视觉层次感
+ border-radius: 8px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+ width: 100%;
+
+ .ant-card-head {
+ border-bottom: 1px solid #f0f0f0;
+ padding: 12px 20px; // 增加头部内边距
+ min-height: 44px; // 增加头部高度
+ flex-shrink: 0; // 防止头部被压缩
+
+ .ant-card-head-title {
+ font-size: 15px;
+ font-weight: 600;
+ color: #333;
+ line-height: 20px;
+ }
+ }
+
+ .ant-card-body {
+ padding: 20px; // 增加内边距提升视觉效果
+
+ // 确保表格和其他内容正确显示
+ .ant-table-wrapper {
+ margin: 0;
+ }
+
+ .ant-row {
+ margin-bottom: 0;
+
+ .ant-col {
+ margin-bottom: 16px; // 增加列间距
+ }
+ }
+ }
+
+ &:last-child {
+ margin-bottom: 24px; // 最后一个卡片底部留一些空间
+ }
+}
+
+// 表单项
+.formItem {
+ margin-bottom: 16px; // 增加表单项间距
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ label {
+ display: block;
+ font-weight: 500;
+ color: #333;
+ font-size: 14px;
+ margin-bottom: 6px; // 增加标签和输入框间距
+ }
+}
+
+// 薪酬项表格 - 支持滚动显示
+.salaryTable {
+ width: 100%;
+ overflow-x: auto; // 水平滚动支持
+
+ /* 使用细滚动条,浅色调 */
+ &::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.02);
+ border-radius: 3px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: rgba(0, 0, 0, 0.15);
+ border-radius: 3px;
+
+ &:hover {
+ background: rgba(0, 0, 0, 0.25);
+ }
+ }
+
+ .ant-table {
+ min-width: 600px; // 确保表格最小宽度
+ }
+
+ .ant-table-thead > tr > th {
+ background-color: #fafafa;
+ font-weight: 600;
+ color: #333;
+ border-bottom: 1px solid #e8e8e8;
+ white-space: nowrap; // 防止表头文字换行
+ }
+
+ .ant-table-tbody > tr {
+ &:hover > td {
+ background-color: #f5f5f5 !important;
+ }
+
+ > td {
+ border-bottom: 1px solid #f0f0f0;
+ white-space: nowrap; // 防止单元格内容换行影响布局
+ }
+ }
+}
+
+// 社保公积金设置
+.socialRow {
+ margin-bottom: 20px; // 增加社保行间距
+}
+
+// 保险表格 - 支持滚动显示
+.insuranceTable {
+ margin-top: 20px; // 增加表格顶部间距
+ margin-bottom: 8px; // 增加表格底部间距
+ width: 100%;
+ overflow-x: auto; // 水平滚动支持
+
+ /* 使用细滚动条,浅色调 */
+ &::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.02);
+ border-radius: 3px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: rgba(0, 0, 0, 0.15);
+ border-radius: 3px;
+
+ &:hover {
+ background: rgba(0, 0, 0, 0.25);
+ }
+ }
+
+ h4 {
+ margin-bottom: 12px; // 增加标题下间距
+ font-size: 14px;
+ font-weight: 600;
+ color: #333;
+ }
+
+ .ant-table {
+ min-width: 500px; // 确保表格最小宽度
+ }
+
+ .ant-table-thead > tr > th {
+ background-color: #fafafa;
+ font-weight: 600;
+ white-space: nowrap; // 防止表头文字换行
+ color: #333;
+ border-bottom: 1px solid #e8e8e8;
+ }
+
+ .ant-table-tbody > tr {
+ > td {
+ border-bottom: 1px solid #f0f0f0;
+ }
+ }
+}
+
+// 滚动动画
+@keyframes scroll {
+ 0% {
+ transform: translateX(0);
+ }
+ 100% {
+ transform: translateX(-50%);
+ }
+}
+
+// 响应式设计
+@media (max-width: 1200px) {
+ .mainContent {
+ flex-direction: column;
+ height: auto;
+ min-height: calc(100vh - 32px);
+ // padding: 8px;
+ gap: 8px;
+ }
+
+ // 响应式样式:小屏幕下组织架构树卡片高度调整
+ .treeCard {
+ height: 300px;
+ }
+
+ // 响应式样式:小屏幕下主要内容卡片高度和滚动优化
+ .mainCard {
+ height: auto;
+ max-height: 80vh; // 使用视口高度的80%,提供更多滚动空间
+ min-height: 500px;
+
+ .content {
+ max-height: 65vh; // 确保内容区域不会过高
+
+ // 小屏幕下使用更细更浅的滚动条
+ &::-webkit-scrollbar {
+ width: 4px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.01);
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: rgba(0, 0, 0, 0.12);
+ border-radius: 2px;
+
+ &:hover {
+ background: rgba(0, 0, 0, 0.20);
+ }
+ }
+ }
+ }
+
+ // 响应式表格滚动条
+ .salaryTable,
+ .insuranceTable {
+ &::-webkit-scrollbar {
+ height: 4px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.01);
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: rgba(0, 0, 0, 0.12);
+ border-radius: 2px;
+
+ &:hover {
+ background: rgba(0, 0, 0, 0.20);
+ }
+ }
+ }
+}
+
+// 页面特定样式覆盖 - 限制在container内部,避免影响其他页面
+.container {
+ // 自定义主题色 - 只影响当前页面
+ .ant-btn-primary {
+ background: linear-gradient(135deg, #2d5cf6 0%, #4c7bff 100%);
+ border: none;
+ border-radius: 6px;
+ font-weight: 500;
+ font-size: 14px;
+ box-shadow: 0 2px 8px rgba(45, 92, 246, 0.3);
+ transition: all 0.3s ease;
+
+ &:hover,
+ &:focus {
+ background: linear-gradient(135deg, #4c7bff 0%, #6b8fff 100%);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(45, 92, 246, 0.4);
+ }
+
+ &:active {
+ transform: translateY(0);
+ }
+ }
+
+ .ant-btn:not(.ant-btn-primary) {
+ background: #fff;
+ border: 1px solid #d9d9d9;
+ color: #666;
+ border-radius: 6px;
+ font-weight: 500;
+ font-size: 14px;
+ transition: all 0.3s ease;
+
+ &:hover,
+ &:focus {
+ border-color: #4c7bff;
+ color: #2d5cf6;
+ }
+ }
+
+ .ant-input,
+ .ant-select-selector,
+ .ant-picker {
+ border-radius: 6px;
+ border: 1px solid #d9d9d9;
+ transition: all 0.3s ease;
+
+ &:hover {
+ border-color: #4c7bff;
+ }
+
+ &:focus,
+ &.ant-select-focused .ant-select-selector,
+ &.ant-picker-focused {
+ border-color: #2d5cf6;
+ box-shadow: 0 0 0 2px rgba(45, 92, 246, 0.2);
+ }
+ }
+
+ .ant-select-selection-placeholder,
+ .ant-input::placeholder,
+ .ant-picker-input input::placeholder {
+ color: #bfbfbf;
+ }
+
+ .ant-tree .ant-tree-node-selected {
+ background-color: #e6f7ff !important;
+ }
+
+ // 链接按钮样式,限制在当前容器内
+ .ant-btn-link {
+ padding: 0 4px;
+ font-size: 12px;
+ color: #ff4d4f;
+
+ &:hover,
+ &:focus {
+ color: #ff7875;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/pages/topnavbar/TopNavBar.js b/src/pages/topnavbar/TopNavBar.js
index 5e9950a..8fa9631 100644
--- a/src/pages/topnavbar/TopNavBar.js
+++ b/src/pages/topnavbar/TopNavBar.js
@@ -18,6 +18,11 @@ const menuItem = [
key: '/topnavbar00/multidstatistics/staffstatistics',
// icon: ,
},
+ {
+ label: '绩效仪表盘',
+ key: '/topnavbar00/multidstatistics/performance',
+ // icon: ,
+ },
]
},
{
@@ -31,26 +36,55 @@ const menuItem = [
// icon: ,
},
{
- label: '人员履历',
- key: '/topnavbar00/staffmanage/staffresume',
+ label: '入职管理',
+ key: '/topnavbar00/staffmanage/onboarding',
+ // icon: ,
+ },
+ {
+ label: '入职登记',
+ key: '/topnavbar00/staffmanage/onboardingregister',
+ // icon: ,
+ },
+ {
+ label: '入职审批',
+ key: '/topnavbar00/staffmanage/onboardingapproval',
// icon: ,
},
{
- label: '人员调整',
- key: '/topnavbar00/staffmanage/staffadjust',
+ label: '异动审批',
+ key: '/topnavbar00/staffmanage/resignation',
// icon: ,
},
{
- label: '工作流',
- key: '/topnavbar00/staffmanage/workflow',
+ label: '培训管理',
+ key: '/topnavbar00/staffmanage/training',
// icon: ,
},
{
- label: '',
- // label: '详情页',
- key: '/topnavbar00/staffmanage/particulars',
+ label: '培训计划',
+ key: '/topnavbar00/staffmanage/trainingplan',
// icon: ,
},
+ // {
+ // label: '人员履历',
+ // key: '/topnavbar00/staffmanage/staffresume',
+ // // icon: ,
+ // },
+ // {
+ // label: '人员调整',
+ // key: '/topnavbar00/staffmanage/staffadjust',
+ // // icon: ,
+ // },
+ // {
+ // label: '工作流',
+ // key: '/topnavbar00/staffmanage/workflow',
+ // // icon: ,
+ // },
+ // {
+ // label: '详情页',
+ // key: '/topnavbar00/staffmanage/particulars',
+ // // icon: ,
+ // },
]
},
{
@@ -63,6 +97,11 @@ const menuItem = [
key: '/topnavbar00/organmanage/organchart',
// icon: ,
},
+ {
+ label: '汇报关系',
+ key: '/topnavbar00/organmanage/reportrelation',
+ // icon: ,
+ },
{
label: '部门维护',
key: '/topnavbar00/organmanage/deptmaintain',
@@ -79,6 +118,11 @@ const menuItem = [
label: '考勤数据',
key: '/topnavbar00/attendancemanage/attendancedata',
// icon: ,
+ },
+ {
+ label: '考勤详情及排查',
+ key: '/topnavbar00/attendancemanage/attendancedetails',
+ // icon: ,
}
]
},
@@ -93,12 +137,12 @@ const menuItem = [
// icon: ,
},
{
- label: '绩效规则设置',
+ label: '绩效参数设置',
key: '/topnavbar00/performancemanage/performanceruleset',
// icon: ,
},
{
- label: '绩效评定',
+ label: '绩效设定',
key: '/topnavbar00/performancemanage/performanceassess',
// icon: ,
}
@@ -159,15 +203,15 @@ const menuItem = [
]
},
{
- label: '知识库',
+ label: '知识库管理',
key: '/topnavbar00/knowledgebase',
// icon: ,
children: [
- {
- label: '问答知识库',
- key: '/topnavbar00/knowledgebase/qaknowledgebase',
- // icon: ,
- },
+ // {
+ // label: '问答知识库',
+ // key: '/topnavbar00/knowledgebase/qaknowledgebase',
+ // // icon: ,
+ // },
{
label: '文档知识库',
key: '/topnavbar00/knowledgebase/docknowledgebase',
@@ -181,7 +225,7 @@ const menuItem = [
// icon: ,
children: [
{
- label: '数据字段',
+ label: '数据字典',
key: '/topnavbar00/backendmanage/datadict',
// icon: ,
},
@@ -237,6 +281,7 @@ const TopNavBar = (props) => {
const setRouteActive = value => {
const curKey = value.key
+ console.log(curKey, '6666666666666')
const activeKeys = menuItems?.filter(item => curKey.indexOf(item.key) !== -1)[0]?.key ?? ''
setActiveKey(activeKeys)