diff --git a/config/routes.js b/config/routes.js
index a1007da..f894a1c 100644
--- a/config/routes.js
+++ b/config/routes.js
@@ -31,6 +31,12 @@ export default [
name: 'staffstatistics',
component: './multi-d-statistics/MultiDStatistics',
},
+ {
+ path: '/topnavbar00/multidstatistics/performance',
+ // icon: 'bank',
+ name: 'performance',
+ component: './performance/Performance',
+ },
],
},
// 人员管理
@@ -45,6 +51,42 @@ export default [
name: 'staffinfo',
component: './staffmanage_staffinfo/StaffInfo',
},
+ {
+ path: '/topnavbar00/staffmanage/onboarding',
+ // icon: 'bank',
+ name: 'onboarding',
+ component: './staffmanage_onboarding/OnBoarding',
+ },
+ {
+ path: '/topnavbar00/staffmanage/onboardingregister',
+ // icon: 'bank',
+ name: 'onboardingregister',
+ component: './staffmanage_onboardingregister/OnBoardingRegister',
+ },
+ {
+ path: '/topnavbar00/staffmanage/onboardingapproval',
+ // icon: 'bank',
+ name: 'onboardingapproval',
+ component: './staffmanage_onboardingapproval/OnBoardingApproval',
+ },
+ {
+ path: '/topnavbar00/staffmanage/resignation',
+ // icon: 'bank',
+ name: 'resignation',
+ component: './staffmanage_resignation/Resignation',
+ },
+ {
+ path: '/topnavbar00/staffmanage/training',
+ // icon: 'bank',
+ name: 'training',
+ component: './staffmanage_training/Training',
+ },
+ {
+ path: '/topnavbar00/staffmanage/trainingplan',
+ // icon: 'bank',
+ name: 'trainingplan',
+ component: './staffmanage_trainingplan/TrainingPlan',
+ },
{
path: '/topnavbar00/staffmanage/staffresume',
// icon: 'bank',
@@ -63,7 +105,7 @@ export default [
name: 'staffresume',
component: './staffmanage_staffresume/StaffResume',
},
- {
+ {
path: '/topnavbar00/staffmanage/particulars',
// icon: 'bank',
name: 'particulars',
@@ -83,6 +125,12 @@ export default [
name: 'organchart',
component: './organmanage_organchart/OrganChart',
},
+ {
+ path: '/topnavbar00/organmanage/reportrelation',
+ // icon: 'bank',
+ name: 'reportrelation',
+ component: './organmanage_reportrelation/ReportRelation',
+ },
{
path: '/topnavbar00/organmanage/deptmaintain',
// icon: 'bank',
@@ -101,7 +149,13 @@ export default [
path: '/topnavbar00/attendancemanage/attendancedata',
// icon: 'bank',
name: 'attendancedata',
- component: './attendancemanage_attendancedata/attendancemanageAttendancedata',
+ component: './attendancemanage_attendancedata/AttendancemanageAttendancedata',
+ },
+ {
+ path: '/topnavbar00/attendancemanage/attendancedetails',
+ // icon: 'bank',
+ name: 'attendancedetails',
+ component: './attendancemanage_attendancedetails/Attendancedetails',
},
],
},
@@ -149,6 +203,12 @@ export default [
name: 'salaryruleset',
component: './salarymanage_salaryruleset/SalaryRuleset',
},
+ {
+ path: '/topnavbar00/salarymanage/salarydetail',
+ // icon: 'bank',
+ name: 'salarydetail',
+ component: './salarymanage_salarydetail/SalaryDetail',
+ },
],
},
//效率管理
diff --git a/src/global.less b/src/global.less
index 3a71f32..6143476 100644
--- a/src/global.less
+++ b/src/global.less
@@ -31,7 +31,7 @@
}
.ant-btn-primary {
- background: rgba(36, 114, 214, 1);
+ // background: rgba(36, 114, 214, 1);
}
// top导航样式开始
diff --git a/src/pages/salarymanage_salarydetail/girl.png b/src/pages/salarymanage_salarydetail/girl.png
new file mode 100644
index 0000000..44ff8f5
Binary files /dev/null and b/src/pages/salarymanage_salarydetail/girl.png differ
diff --git a/src/pages/staffmanage_onboarding/OnBoarding.js b/src/pages/staffmanage_onboarding/OnBoarding.js
new file mode 100644
index 0000000..48fd601
--- /dev/null
+++ b/src/pages/staffmanage_onboarding/OnBoarding.js
@@ -0,0 +1,838 @@
+import React, { PureComponent } from 'react';
+import {
+ Card,
+ Tree,
+ Input,
+ Button,
+ DatePicker,
+ Select,
+ Row,
+ Col,
+ Space,
+ Radio,
+ Checkbox,
+ InputNumber,
+ Avatar,
+ message,
+ Typography
+} from 'antd';
+import {
+ SaveOutlined,
+ CloseOutlined,
+ PlusCircleOutlined,
+ DeleteOutlined,
+ ExpandOutlined,
+ UserOutlined,
+ TeamOutlined,
+ ApartmentOutlined,
+ SyncOutlined,
+ DollarOutlined,
+ SettingOutlined,
+ CalendarOutlined,
+ UsergroupAddOutlined,
+ UserSwitchOutlined,
+ StopOutlined,
+ CheckCircleOutlined,
+ CloseCircleOutlined,
+ ContactsOutlined,
+} from '@ant-design/icons';
+import dayjs from 'dayjs';
+import styles from './OnBoarding.less';
+
+const { Option } = Select;
+const { TextArea } = Input;
+const { Title } = Typography;
+class OnBoarding extends PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ selectedOrgKeys: ['0-0-4'], // 默认选中人事部
+ expandedKeys: ['0-0', '0-0-0', '0-0-1', '0-0-2'],
+ // 入职计划表单数据
+ onboardingForm: {
+ // 入职部门信息
+ departmentName: '人力资源部',
+ departmentManager: '张明远',
+ currentStaff: '24/30',
+
+ // 计划时段设置
+ planStartDate: dayjs('2023-09-01'),
+ planEndDate: dayjs('2023-09-30'),
+
+ // 可入职岗位设置
+ availablePositions: [
+ { id: 1, name: '人力资源专员', count: 3, checked: true },
+ { id: 2, name: '薪酬福利专员', count: 2, checked: true },
+ { id: 3, name: '培训发展专员', count: 1, checked: false },
+ { id: 4, name: '招聘专员', count: 2, checked: false }
+ ],
+
+ // 计划入职人数
+ totalPlan: 5,
+ confirmedCount: 2,
+ remainingCount: 3,
+
+ // 人员要求
+ ageRange: '22-30岁',
+ gender: '不限',
+ education: '本科及以上',
+ workYears: '1-3年',
+ otherRequirements: '',
+
+ // 黑名单
+ blacklist: [
+ { id: 1, name: '林晓梅', idCard: '310***19880512****', avatar: null },
+ { id: 2, name: '王建国', idCard: '420***19900215****', avatar: null }
+ ],
+
+ // 白名单
+ whitelist: [
+ { id: 1, name: '陈思思', recommender: '张明远', avatar: null }
+ ]
+ },
+ 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 });
+
+ if (selectedKeys.length > 0) {
+ const selectedKey = selectedKeys[0];
+ const findNodeByKey = (nodes, key) => {
+ for (let node of nodes) {
+ if (node.key === key) {
+ return node;
+ }
+ if (node.children) {
+ const found = findNodeByKey(node.children, key);
+ if (found) return found;
+ }
+ }
+ return null;
+ };
+
+ const selectedNode = findNodeByKey(this.state.organizationData, selectedKey);
+ if (selectedNode) {
+ this.handleOnboardingFormChange('departmentName', selectedNode.title);
+ const departmentInfo = this.getDepartmentInfo(selectedNode.title);
+ this.setState(prevState => ({
+ onboardingForm: {
+ ...prevState.onboardingForm,
+ ...departmentInfo
+ }
+ }));
+ }
+ }
+ };
+
+ // 获取部门信息
+ getDepartmentInfo = (departmentName) => {
+ const departmentMap = {
+ '技术部': {
+ departmentManager: '李技术',
+ currentStaff: '115/120',
+ availablePositions: [
+ { id: 1, name: '前端工程师', count: 2, checked: true },
+ { id: 2, name: '后端工程师', count: 3, checked: true },
+ { id: 3, name: '测试工程师', count: 1, checked: false }
+ ]
+ },
+ '产品部': {
+ departmentManager: '王产品',
+ currentStaff: '65/68',
+ availablePositions: [
+ { id: 1, name: '产品经理', count: 1, checked: true },
+ { id: 2, name: '产品设计师', count: 2, checked: true }
+ ]
+ },
+ '人事部': {
+ departmentManager: '张明远',
+ currentStaff: '24/30',
+ availablePositions: [
+ { id: 1, name: '人力资源专员', count: 3, checked: true },
+ { id: 2, name: '薪酬福利专员', count: 2, checked: true },
+ { id: 3, name: '培训发展专员', count: 1, checked: false },
+ { id: 4, name: '招聘专员', count: 2, checked: false }
+ ]
+ },
+ '财务部': {
+ departmentManager: '赵财务',
+ currentStaff: '30/32',
+ availablePositions: [
+ { id: 1, name: '会计', count: 1, checked: true },
+ { id: 2, name: '出纳', count: 1, checked: false }
+ ]
+ },
+ '运营部': {
+ departmentManager: '钱运营',
+ currentStaff: '48/52',
+ availablePositions: [
+ { id: 1, name: '市场专员', count: 2, checked: true },
+ { id: 2, name: '客服专员', count: 1, checked: true }
+ ]
+ }
+ };
+
+ return departmentMap[departmentName] || {
+ departmentManager: '未知',
+ currentStaff: '0/0',
+ availablePositions: []
+ };
+ };
+
+ // 树展开/收缩
+ onTreeExpand = (expandedKeys) => {
+ this.setState({ expandedKeys });
+ };
+
+ // 入职表单字段变化
+ handleOnboardingFormChange = (field, value) => {
+ this.setState(prevState => ({
+ onboardingForm: {
+ ...prevState.onboardingForm,
+ [field]: value
+ }
+ }));
+ };
+
+ // 岗位选择变化
+ handlePositionChange = (id, checked) => {
+ const { onboardingForm } = this.state;
+ const newPositions = onboardingForm.availablePositions.map(pos =>
+ pos.id === id ? { ...pos, checked } : pos
+ );
+ this.setState({
+ onboardingForm: {
+ ...onboardingForm,
+ availablePositions: newPositions
+ }
+ });
+ };
+
+ // 添加新岗位
+ addNewPosition = () => {
+ const { onboardingForm } = this.state;
+ const newPosition = {
+ id: Date.now(),
+ name: '新岗位',
+ count: 1,
+ checked: false
+ };
+ this.setState({
+ onboardingForm: {
+ ...onboardingForm,
+ availablePositions: [...onboardingForm.availablePositions, newPosition]
+ }
+ });
+ message.success('新岗位添加成功');
+ };
+
+ // 移除黑名单人员
+ removeFromBlacklist = (id) => {
+ const { onboardingForm } = this.state;
+ const newBlacklist = onboardingForm.blacklist.filter(person => person.id !== id);
+ this.setState({
+ onboardingForm: {
+ ...onboardingForm,
+ blacklist: newBlacklist
+ }
+ });
+ message.success('已移除黑名单人员');
+ };
+
+ // 移除白名单人员
+ removeFromWhitelist = (id) => {
+ const { onboardingForm } = this.state;
+ const newWhitelist = onboardingForm.whitelist.filter(person => person.id !== id);
+ this.setState({
+ onboardingForm: {
+ ...onboardingForm,
+ whitelist: newWhitelist
+ }
+ });
+ message.success('已移除白名单人员');
+ };
+
+ // 添加黑名单人员
+ addToBlacklist = () => {
+ message.info('功能开发中,敬请期待');
+ };
+
+ // 添加白名单人员
+ addToWhitelist = () => {
+ message.info('功能开发中,敬请期待');
+ };
+
+ // 保存设置
+ handleSave = () => {
+ const { onboardingForm } = this.state;
+
+ if (!onboardingForm.departmentName) {
+ message.error('请选择部门');
+ return;
+ }
+
+ if (!onboardingForm.planStartDate || !onboardingForm.planEndDate) {
+ message.error('请选择计划时段');
+ return;
+ }
+
+ if (onboardingForm.planStartDate.isAfter(onboardingForm.planEndDate)) {
+ message.error('开始日期不能晚于结束日期');
+ return;
+ }
+
+ if (onboardingForm.totalPlan <= 0) {
+ message.error('计划总人数必须大于0');
+ return;
+ }
+
+ console.log('保存入职计划设置:', onboardingForm);
+ message.success('入职计划设置保存成功');
+ };
+
+ // 取消操作
+ handleCancel = () => {
+ message.info('已取消操作');
+ };
+
+ render() {
+ const { selectedOrgKeys, expandedKeys, onboardingForm } = 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}
+ >
+
+
+
+
+
+
+ {/* 右侧入职计划设置 - 替换为HTML内容 */}
+
+
+ {/* 头部标题 */}
+
+
+ 入职计划设置
+
+
+ }
+ onClick={this.handleSave}
+ >
+ 保存设置
+
+ }
+ onClick={this.handleCancel}
+ >
+ 取消
+
+
+
+
+ {/* 表单内容区 */}
+
+ {/* 入职部门信息 */}
+
+
+ 入职部门信息
+
+ }
+ size="small"
+ className={styles.formCard}
+ >
+
+
+
+
+
+ {onboardingForm.departmentName}
+
+
+
+
+
+
+
+ {onboardingForm.departmentManager}
+
+
+
+
+
+
+
+ {onboardingForm.currentStaff}
+
+
+
+
+
+
+ {/* 计划时段设置 */}
+
+
+ 计划时段设置
+
+ }
+ size="small"
+ className={styles.formCard}
+ >
+
+
+
+
+ this.handleOnboardingFormChange('planStartDate', date)}
+ placeholder="选择开始日期"
+ />
+
+
+
+
+
+ this.handleOnboardingFormChange('planEndDate', date)}
+ placeholder="选择结束日期"
+ />
+
+
+
+
+
+ {/* 可入职岗位设置 */}
+
+
+ 可入职岗位设置
+
+ }
+ size="small"
+ className={styles.formCard}
+ >
+
+ {onboardingForm.availablePositions.map(position => (
+
+ this.handlePositionChange(position.id, e.target.checked)}
+ >
+ {position.name} ({position.count}人)
+
+
+ ))}
+
}
+ onClick={this.addNewPosition}
+ className={styles.addButton}
+ >
+ 添加其他岗位
+
+
+
+
+ {/* 计划入职人数 */}
+
+
+ 计划入职人数
+
+ }
+ size="small"
+ className={styles.formCard}
+ >
+
+
+
+
+ this.handleOnboardingFormChange('totalPlan', value)}
+ placeholder="请输入计划总人数"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 人员要求 */}
+
+
+ 人员要求
+
+ }
+ size="small"
+ className={styles.formCard}
+ >
+
+
+
+ this.handleOnboardingFormChange('ageRange', e.target.value)}
+ >
+ 22-30岁
+ 30-35岁
+ 不限
+
+
+
+
+
+ this.handleOnboardingFormChange('gender', e.target.value)}
+ >
+ 男
+ 女
+ 不限
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 黑名单设置 */}
+
+
+ 黑名单设置
+
+ }
+ size="small"
+ className={styles.formCard}
+ >
+
+ {onboardingForm.blacklist.map(person => (
+
+
+
: undefined}
+ />
+
+
{person.name}
+
身份证: {person.idCard}
+
+
+
}
+ onClick={() => this.removeFromBlacklist(person.id)}
+ >
+ 移除
+
+
+ ))}
+
}
+ onClick={this.addToBlacklist}
+ className={styles.addButton}
+ >
+ 添加黑名单人员
+
+
+
+
+ {/* 白名单设置 */}
+
+
+ 白名单设置
+
+ }
+ size="small"
+ className={styles.formCard}
+ >
+
+ {onboardingForm.whitelist.map(person => (
+
+
+
: undefined}
+ />
+
+
{person.name}
+
推荐人: {person.recommender}
+
+
+
}
+ onClick={() => this.removeFromWhitelist(person.id)}
+ >
+ 移除
+
+
+ ))}
+
}
+ onClick={this.addToWhitelist}
+ className={styles.addButton}
+ >
+ 添加白名单人员
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default OnBoarding;
\ No newline at end of file
diff --git a/src/pages/staffmanage_onboarding/OnBoarding.less b/src/pages/staffmanage_onboarding/OnBoarding.less
new file mode 100644
index 0000000..68e4e2c
--- /dev/null
+++ b/src/pages/staffmanage_onboarding/OnBoarding.less
@@ -0,0 +1,366 @@
+@import '~@/utils/utils.less';
+
+// 容器样式
+.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;
+ }
+ }
+}
+
+// 组织架构树卡片样式
+.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节点标题样式 */
+.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;
+ }
+}
+
+// 头部区域
+.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 - 180px); // 减去头部高度和其他空间,强制设置高度
+ max-height: calc(100vh - 180px); // 明确最大高度,确保滚动条出现
+
+ &::-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);
+}
+
+// 表单卡片
+.formCard {
+ margin-bottom: 20px;
+ border-radius: 8px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .ant-card-head {
+ border-bottom: 1px solid #f0f0f0;
+
+ .ant-card-head-title {
+ font-size: 15px;
+ font-weight: 600;
+ color: #333;
+ }
+ }
+
+ .ant-card-body {
+ padding: 20px;
+ }
+}
+
+// 表单项
+.formItem {
+ margin-bottom: 16px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ label {
+ // display: block;
+ font-weight: 500;
+ color: #333;
+ font-size: 14px;
+ margin-bottom: 6px;
+ }
+}
+
+.readOnlyField {
+ background-color: #f9fafb;
+ padding: 8px 12px;
+ border-radius: 6px;
+ border: 1px solid #e5e7eb;
+ color: #374151;
+ font-size: 14px;
+}
+
+/* 岗位列表 */
+.positionList {
+ .positionItem {
+ margin-bottom: 12px;
+ }
+
+ .addButton {
+ color: #2d5cf6;
+ padding: 0;
+ height: auto;
+ font-size: 14px;
+
+ &:hover {
+ color: #1d4ed8;
+ }
+ }
+}
+
+/* 人员要求区域 */
+.requirementSection {
+ .formItem {
+ margin-bottom: 20px;
+
+ .ant-radio-group {
+ display: flex;
+ gap: 16px;
+ }
+ }
+}
+
+/* 人员列表 */
+.personList {
+ .personItem {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 12px;
+ border: 1px solid #e5e7eb;
+ border-radius: 6px;
+ margin-bottom: 12px;
+ transition: all 0.2s;
+
+ &:hover {
+ border-color: #d1d5db;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.06);
+ }
+ }
+
+ .personInfo {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ }
+
+ .personDetails {
+ .personName {
+ font-size: 14px;
+ font-weight: 500;
+ color: #262626;
+ margin-bottom: 2px;
+ }
+
+ .personMeta {
+ font-size: 12px;
+ color: #6b7280;
+ }
+ }
+
+ .addButton {
+ color: #2d5cf6;
+ padding: 0;
+ height: auto;
+ font-size: 14px;
+
+ &:hover {
+ color: #1d4ed8;
+ }
+ }
+}
+
+// 响应式设计
+@media (max-width: 1200px) {
+ .treeCard {
+ height: 300px;
+ }
+
+ .mainCard {
+ height: auto;
+ max-height: 80vh;
+ min-height: 500px;
+
+ .content {
+ max-height: 65vh;
+
+ &::-webkit-scrollbar {
+ width: 4px;
+ }
+ }
+ }
+}
+
+// 页面特定样式
+.container {
+ .ant-btn-primary {
+ background: linear-gradient(135deg, #2d5cf6 0%, #4c7bff 100%);
+ border: none;
+ border-radius: 6px;
+ font-weight: 500;
+ 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);
+ }
+ }
+
+ .ant-btn:not(.ant-btn-primary) {
+ border-radius: 6px;
+ transition: all 0.3s ease;
+
+ &:hover,
+ &:focus {
+ border-color: #4c7bff;
+ color: #2d5cf6;
+ }
+ }
+
+ .ant-input,
+ .ant-select-selector,
+ .ant-picker {
+ border-radius: 6px;
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pages/staffmanage_onboardingapproval/OnBoardingApproval.js b/src/pages/staffmanage_onboardingapproval/OnBoardingApproval.js
new file mode 100644
index 0000000..0d9922b
--- /dev/null
+++ b/src/pages/staffmanage_onboardingapproval/OnBoardingApproval.js
@@ -0,0 +1,585 @@
+import React, { PureComponent } from 'react';
+import {
+ Card,
+ Tree,
+ Input,
+ Button,
+ DatePicker,
+ Select,
+ Row,
+ Col,
+ Space,
+ Radio,
+ Checkbox,
+ InputNumber,
+ Avatar,
+ message,
+ Timeline,
+ Tag,
+ Typography,
+ Divider
+} 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
+} from '@ant-design/icons';
+import dayjs from 'dayjs';
+import styles from './OnBoardingApproval.less';
+
+const { Option } = Select;
+const { TextArea } = Input;
+const { Title, Text, Paragraph } = Typography;
+
+class OnBoardingApproval extends PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ selectedOrgKeys: ['0-0-4'], // 默认选中人事部
+ expandedKeys: ['0-0', '0-0-0', '0-0-1', '0-0-2'],
+ // 审批详情数据
+ approvalData: {
+ applicant: {
+ name: '张三',
+ gender: '男',
+ age: 28,
+ phone: '138****5678',
+ department: '产品中心 - 用户体验部',
+ position: '高级产品经理',
+ joinDate: '2023-11-15',
+ salary: '¥18,000/月',
+ status: '审批中',
+ avatar: '/img/avatar.jpg',
+ idCard: '/img/card.jpg'
+ },
+ education: [
+ {
+ period: '2014.09 - 2018.06',
+ school: '北京大学',
+ major: '信息管理与信息系统',
+ degree: '本科'
+ },
+ {
+ period: '2018.09 - 2021.06',
+ school: '清华大学',
+ major: '工商管理',
+ degree: '硕士'
+ }
+ ],
+ workExperience: [
+ {
+ period: '2021.07 - 2023.04',
+ company: '字节跳动',
+ position: '产品经理'
+ },
+ {
+ period: '2023.05 - 2023.10',
+ company: '腾讯科技',
+ position: '高级产品经理'
+ }
+ ],
+ approvalFlow: [
+ {
+ step: '提交申请',
+ status: 'completed',
+ operator: '张三',
+ time: '2023-10-20 09:30',
+ comment: '申请人张三提交了入职申请'
+ },
+ {
+ step: '部门初审',
+ status: 'completed',
+ operator: '张伟',
+ time: '2023-10-21 14:15',
+ comment: '部门经理张伟审核通过',
+ opinion: '候选人专业背景和工作经验符合岗位要求,建议录用'
+ },
+ {
+ step: '部门复审',
+ status: 'completed',
+ operator: '李强',
+ time: '2023-10-23 10:45',
+ comment: '产品总监李强审核通过',
+ opinion: '面试表现优秀,专业能力突出,同意录用'
+ },
+ {
+ step: 'HR审核',
+ status: 'completed',
+ operator: '王芳',
+ time: '2023-10-25 16:20',
+ comment: 'HRBP王芳审核通过',
+ opinion: '背景调查无异常,薪资符合公司标准,同意录用'
+ },
+ {
+ step: '总经理审批',
+ status: 'processing',
+ operator: '赵明',
+ time: '待审批',
+ comment: '等待总经理赵明审批'
+ },
+ {
+ step: '入职生效',
+ status: 'waiting',
+ time: '待处理',
+ comment: '审批通过后自动生效'
+ },
+ {
+ step: '审批完结',
+ status: 'waiting',
+ time: '待处理',
+ comment: '流程自动归档'
+ }
+ ]
+ },
+ 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 });
+ };
+
+ // 审批通过
+ handleApprove = () => {
+ message.success('审批通过,已提交到下一审批环节');
+ };
+
+ // 审批拒绝
+ handleReject = () => {
+ message.error('审批已拒绝');
+ };
+
+ // 打印
+ handlePrint = () => {
+ window.print();
+ };
+
+ // 获取审批状态图标
+ getStatusIcon = (status) => {
+ switch (status) {
+ case 'completed':
+ return ;
+ case 'processing':
+ return ;
+ case 'waiting':
+ return ;
+ default:
+ return ;
+ }
+ };
+
+ // 获取状态标签
+ getStatusTag = (status) => {
+ switch (status) {
+ case '审批中':
+ return 审批中;
+ case '已通过':
+ return 已通过;
+ case '已拒绝':
+ return 已拒绝;
+ default:
+ return {status};
+ }
+ };
+
+ render() {
+ const { selectedOrgKeys, expandedKeys, approvalData } = this.state;
+ const { applicant, education, workExperience, approvalFlow } = approvalData;
+
+ 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.handleApprove}
+ // className={styles.approveBtn}
+ >
+ 通过
+
+ }
+ onClick={this.handleReject}
+ // className={styles.rejectBtn}
+ >
+ 拒绝
+
+ }
+ onClick={this.handlePrint}
+ >
+ 打印
+
+
+
+
+ {/* 滚动内容区 */}
+
+ {/* 基本信息卡片 */}
+
+
+ {/* 申请人照片 */}
+
+
+

+
申请人照片
+
+
+
+ {/* 身份证照片 */}
+
+
+

+
身份证正反面
+
+
+
+ {/* 基本信息 */}
+
+
+
+
+
+ 申请人姓名:
+ {applicant.name}
+
+
+
+
+ 性别:
+ {applicant.gender}
+
+
+
+
+ 年龄:
+ {applicant.age}
+
+
+
+
+ 联系电话:
+ {applicant.phone}
+
+
+
+
+ 申请部门:
+ {applicant.department}
+
+
+
+
+ 职位:
+ {applicant.position}
+
+
+
+
+ 入职日期:
+ {applicant.joinDate}
+
+
+
+
+ 薪资待遇:
+ {applicant.salary}
+
+
+
+
+ 审批状态:
+ {this.getStatusTag(applicant.status)}
+
+
+
+
+
+
+
+
+ {/* 教育背景 */}
+
+
+ {education.map((edu, index) => (
+
+
+ {edu.period}
+
+
+ {edu.school}
+
+
+ {edu.major}
+
+
+ {edu.degree}
+
+
+ ))}
+
+
+
+ {/* 工作经历 */}
+
+
+ {workExperience.map((work, index) => (
+
+
+ {work.period}
+
+
+ {work.company}
+
+
+ {work.position}
+
+
+ ))}
+
+
+
+ {/* 审批流程 */}
+
+
+ {approvalFlow.map((flow, index) => (
+
+
+
+ {flow.step}
+ {flow.time}
+
+
+ {flow.comment}
+
+ {flow.opinion && (
+
+ 意见:{flow.opinion}
+
+ )}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default OnBoardingApproval;
\ No newline at end of file
diff --git a/src/pages/staffmanage_onboardingapproval/OnBoardingApproval.less b/src/pages/staffmanage_onboardingapproval/OnBoardingApproval.less
new file mode 100644
index 0000000..91bcf19
--- /dev/null
+++ b/src/pages/staffmanage_onboardingapproval/OnBoardingApproval.less
@@ -0,0 +1,375 @@
+@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;
+ }
+}
+
+// 头部区域
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 24px;
+ border-bottom: 1px solid #f0f0f0;
+ flex-shrink: 0;
+ height: 72px;
+ // background: #fafafa;
+}
+
+// 内容区域
+.content {
+ flex: 1;
+ padding: 20px 24px 28px 24px;
+ overflow-y: auto;
+ overflow-x: hidden;
+ min-height: 0; // 关键:允许flex子元素收缩
+ height: calc(100vh - 180px); // 减去头部高度和其他空间,强制设置高度
+ max-height: calc(100vh - 180px); // 明确最大高度,确保滚动条出现
+ &::-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);
+}
+
+// 按钮样式
+.approveBtn {
+ background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
+ border: none;
+ box-shadow: 0 2px 8px rgba(82, 196, 26, 0.3);
+
+ &:hover {
+ background: linear-gradient(135deg, #73d13d 0%, #95de64 100%);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(82, 196, 26, 0.4);
+ }
+}
+
+.rejectBtn {
+ box-shadow: 0 2px 8px rgba(255, 77, 79, 0.3);
+
+ &:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(255, 77, 79, 0.4);
+ }
+}
+
+// 信息卡片
+.infoCard {
+ margin-bottom: 20px;
+ border-radius: 8px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .ant-card-head {
+ border-bottom: 1px solid #f0f0f0;
+
+ .ant-card-head-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ }
+ }
+
+ .ant-card-body {
+ padding: 20px;
+ }
+}
+
+// 照片容器
+.photoContainer {
+ text-align: center;
+
+ .applicantPhoto,
+ .idCardPhoto {
+ width: 100%;
+ max-width: 200px;
+ height: auto;
+ border-radius: 8px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ margin-bottom: 8px;
+ }
+
+ .photoLabel {
+ display: block;
+ font-size: 12px;
+ color: #666;
+ }
+}
+
+// 基本信息
+.basicInfo {
+ .infoItem {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+
+ .infoLabel {
+ color: #666;
+ font-size: 14px;
+ }
+
+ .infoValue {
+ font-weight: 500;
+ color: #333;
+ font-size: 14px;
+ }
+ }
+}
+
+// 背景信息区域
+.backgroundSection {
+ background: #fafafa;
+ border-radius: 8px;
+ padding: 16px;
+
+ .backgroundRow {
+ padding: 8px 0;
+ border-bottom: 1px solid #f0f0f0;
+
+ &:last-child {
+ border-bottom: none;
+ }
+ }
+}
+
+// 审批流程时间线
+.approvalTimeline {
+ .timelineItem {
+ padding-bottom: 24px;
+
+ .timelineContent {
+ background: #fafafa;
+ border-radius: 8px;
+ padding: 16px;
+ margin-left: 16px;
+ border: 1px solid #f0f0f0;
+
+ .timelineHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+
+ .stepTitle {
+ font-size: 16px;
+ color: #333;
+ }
+
+ .stepTime {
+ font-size: 12px;
+ color: #999;
+ }
+ }
+
+ .stepComment {
+ margin: 0;
+ color: #666;
+ font-size: 14px;
+ }
+
+ .stepOpinion {
+ margin-top: 8px;
+ padding: 8px 12px;
+ background: #e6f7ff;
+ border-radius: 4px;
+ border-left: 3px solid #1890ff;
+
+ span {
+ color: #1890ff;
+ font-size: 13px;
+ }
+ }
+ }
+ }
+}
+
+// 状态图标
+.completedIcon {
+ color: #52c41a;
+ font-size: 16px;
+}
+
+.processingIcon {
+ color: #faad14;
+ font-size: 16px;
+}
+
+.waitingIcon {
+ color: #d9d9d9;
+ font-size: 16px;
+}
+
+// 响应式设计
+@media (max-width: 1200px) {
+ .treeCard {
+ height: 300px;
+ }
+
+ .mainCard {
+ height: auto;
+ max-height: 80vh;
+ min-height: 500px;
+
+ .content {
+ max-height: 65vh;
+
+ &::-webkit-scrollbar {
+ width: 4px;
+ }
+ }
+ }
+
+ .photoContainer {
+ margin-bottom: 16px;
+ }
+}
+
+// 页面特定样式
+.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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pages/staffmanage_onboardingregister/OnBoardingRegister.js b/src/pages/staffmanage_onboardingregister/OnBoardingRegister.js
new file mode 100644
index 0000000..476b74c
--- /dev/null
+++ b/src/pages/staffmanage_onboardingregister/OnBoardingRegister.js
@@ -0,0 +1,345 @@
+import React, { PureComponent } from 'react';
+import {
+ Card,
+ Form,
+ Input,
+ Button,
+ Upload,
+ message,
+ Avatar,
+ Row,
+ Col,
+ Space,
+ Typography
+} from 'antd';
+import {
+ CameraOutlined,
+ UserOutlined,
+ IdcardOutlined,
+ PlusOutlined,
+ CheckCircleOutlined
+} from '@ant-design/icons';
+import styles from './OnBoardingRegister.less';
+
+const { TextArea } = Input;
+const { Title, Text } = Typography;
+
+class OnBoardingRegister extends PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ faceVerified: false,
+ idCardFront: null,
+ idCardBack: null,
+ formData: {
+ department: '技术研发部',
+ position: '前端开发工程师',
+ joinDate: '2025-07-02',
+ name: '',
+ age: '',
+ phone: '',
+ idNumber: '',
+ remark: ''
+ }
+ };
+ }
+
+ // 刷脸认证
+ handleFaceVerification = () => {
+ // 模拟刷脸认证
+ setTimeout(() => {
+ this.setState({ faceVerified: true });
+ message.success('刷脸认证成功');
+ }, 2000);
+ message.loading('正在进行刷脸认证...', 2);
+ };
+
+ // 处理表单字段变化
+ handleFormChange = (field, value) => {
+ this.setState(prevState => ({
+ formData: {
+ ...prevState.formData,
+ [field]: value
+ }
+ }));
+ };
+
+ // 处理身份证上传
+ handleIdCardUpload = (type, info) => {
+ if (info.file.status === 'uploading') {
+ return;
+ }
+ if (info.file.status === 'done') {
+ // 获取上传结果
+ const imageUrl = URL.createObjectURL(info.file.originFileObj);
+ this.setState({
+ [type]: imageUrl
+ });
+ message.success(`身份证${type === 'idCardFront' ? '正面' : '反面'}上传成功`);
+ }
+ };
+
+ // 自定义上传
+ customUpload = ({ file, onSuccess }) => {
+ setTimeout(() => {
+ onSuccess("ok");
+ }, 0);
+ };
+
+ // 提交表单
+ handleSubmit = () => {
+ const { formData, faceVerified, idCardFront, idCardBack } = this.state;
+
+ if (!faceVerified) {
+ message.error('请先完成刷脸认证');
+ return;
+ }
+
+ if (!formData.name) {
+ message.error('请输入姓名');
+ return;
+ }
+
+ if (!formData.age) {
+ message.error('请输入年龄');
+ return;
+ }
+
+ if (!formData.phone) {
+ message.error('请输入手机号');
+ return;
+ }
+
+ if (!formData.idNumber) {
+ message.error('请输入身份证号');
+ return;
+ }
+
+ if (!idCardFront || !idCardBack) {
+ message.error('请上传身份证正反面照片');
+ return;
+ }
+
+ console.log('提交入职申请数据:', {
+ ...formData,
+ faceVerified,
+ idCardFront,
+ idCardBack
+ });
+
+ message.success('入职申请提交成功,请等待审核');
+ };
+
+ render() {
+ const { faceVerified, idCardFront, idCardBack, formData } = this.state;
+
+ return (
+
+
+
+ 人员入职登记
+
+
+
+ {/* 刷脸认证区域 */}
+
+
+ {faceVerified ? (
+
} className={styles.faceAvatar} />
+ ) : (
+
+
+
+ )}
+ {faceVerified && (
+
+ )}
+
+
+
+
+ {/* 表单区域 */}
+
+
+
+ this.handleIdCardUpload('idCardFront', info)}
+ >
+ {idCardFront ? (
+
+ ) : (
+
+ )}
+
+
+
+ this.handleIdCardUpload('idCardBack', info)}
+ >
+ {idCardBack ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ {/* 备注 */}
+
+
+
+ {/* 提交按钮 */}
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default OnBoardingRegister;
\ No newline at end of file
diff --git a/src/pages/staffmanage_onboardingregister/OnBoardingRegister.less b/src/pages/staffmanage_onboardingregister/OnBoardingRegister.less
new file mode 100644
index 0000000..5f81d25
--- /dev/null
+++ b/src/pages/staffmanage_onboardingregister/OnBoardingRegister.less
@@ -0,0 +1,411 @@
+@import '~@/utils/utils.less';
+
+.container {
+ min-height: 90vh;
+ height: 90vh; // 固定高度
+ background-color: #ffffff;
+ padding: 24px;
+ overflow: hidden; // 容器不滚动
+}
+
+.content {
+ max-width: 960px;
+ margin: 0 auto;
+ height: 100%; // 占满容器高度
+ overflow-y: auto; // 添加垂直滚动
+ overflow-x: hidden;
+ padding-right: 8px; // 为滚动条留出空间
+
+ /* 自定义滚动条样式 */
+ &::-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);
+ }
+
+ &:active {
+ background: rgba(0, 0, 0, 0.35);
+ }
+ }
+
+ /* Firefox滚动条 */
+ scrollbar-width: thin;
+ scrollbar-color: rgba(0, 0, 0, 0.18) rgba(0, 0, 0, 0.02);
+}
+
+.title {
+ text-align: center;
+ margin-bottom: 32px !important;
+ color: #333;
+ font-weight: 600;
+ flex-shrink: 0; // 标题不收缩
+}
+
+.formCard {
+ background: white;
+ border-radius: 12px;
+ // 增强阴影效果,添加多层阴影
+ box-shadow:
+ 0 2px 8px rgba(0, 0, 0, 0.06),
+ 0 4px 16px rgba(0, 0, 0, 0.08),
+ 0 8px 32px rgba(0, 0, 0, 0.04);
+ border: 1px solid rgba(0, 0, 0, 0.06); // 添加边框增强立体感
+ margin-bottom: 24px; // 底部留出空间
+ transition: all 0.3s ease; // 添加过渡效果
+
+ // 悬停时增强阴影
+ // &:hover {
+ // box-shadow:
+ // 0 4px 12px rgba(0, 0, 0, 0.08),
+ // 0 8px 24px rgba(0, 0, 0, 0.12),
+ // 0 16px 48px rgba(0, 0, 0, 0.06);
+ // transform: translateY(-2px); // 轻微上浮效果
+ // }
+
+ .ant-card-body {
+ padding: 40px;
+ }
+}
+
+/* 刷脸认证区域 */
+.faceSection {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ margin-bottom: 40px;
+}
+
+.faceContainer {
+ position: relative;
+ margin-bottom: 16px;
+}
+
+.faceUpload {
+ width: 120px;
+ height: 120px;
+ border: 2px dashed #d9d9d9;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: #fafafa;
+ transition: all 0.3s ease;
+ // 为刷脸上传区域添加轻微阴影
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
+
+ &:hover {
+ border-color: #2d5cf6;
+ background-color: #f0f9ff;
+ box-shadow: 0 4px 8px rgba(45, 92, 246, 0.1); // 悬停时显示主题色阴影
+ }
+}
+
+.cameraIcon {
+ font-size: 36px;
+ color: #bfbfbf;
+}
+
+.faceAvatar {
+ background-color: #2d5cf6 !important;
+ border: 3px solid #52c41a;
+ // 为头像添加阴影
+ box-shadow: 0 4px 12px rgba(45, 92, 246, 0.2);
+}
+
+.verifiedIcon {
+ position: absolute;
+ bottom: 8px;
+ right: 8px;
+ font-size: 24px;
+ color: #52c41a;
+ background: white;
+ border-radius: 50%;
+ padding: 2px;
+ // 为认证图标添加阴影
+ box-shadow: 0 2px 6px rgba(82, 196, 26, 0.3);
+}
+
+.faceButton {
+ background: linear-gradient(135deg, #2d5cf6 0%, #4c7bff 100%);
+ border: none;
+ border-radius: 6px;
+ font-weight: 500;
+ height: 36px;
+ padding: 0 20px;
+ // 增强按钮阴影
+ box-shadow: 0 3px 8px rgba(45, 92, 246, 0.3);
+ transition: all 0.3s ease;
+
+ &:hover {
+ background: linear-gradient(135deg, #4c7bff 0%, #6b8fff 100%);
+ transform: translateY(-1px);
+ box-shadow: 0 6px 16px rgba(45, 92, 246, 0.4);
+ }
+
+ &:disabled {
+ background: #52c41a;
+ transform: none;
+ box-shadow: 0 2px 6px rgba(82, 196, 26, 0.2);
+ cursor: not-allowed;
+ }
+}
+
+/* 表单样式 */
+.form {
+ .ant-form-item-label > label {
+ font-weight: 500;
+ color: #333;
+ }
+}
+
+.readOnlyInput {
+ background-color: #f5f5f5 !important;
+ color: #666 !important;
+ cursor: not-allowed;
+}
+
+.requiredField {
+ .ant-form-item-label > label::after {
+ content: "*";
+ color: #ff4d4f;
+ margin-left: 4px;
+ }
+}
+
+/* 身份证上传 */
+.idCardUpload {
+ .ant-upload {
+ width: 100% !important;
+ height: 120px !important;
+ border: 2px dashed #d9d9d9;
+ border-radius: 8px;
+ background-color: #fafafa;
+ transition: all 0.3s ease;
+ // 为上传区域添加阴影
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
+
+ &:hover {
+ border-color: #2d5cf6;
+ background-color: #f0f9ff;
+ box-shadow: 0 4px 8px rgba(45, 92, 246, 0.1);
+ }
+ }
+
+ .ant-upload-select {
+ width: 100% !important;
+ height: 100% !important;
+ }
+}
+
+.uploadContent {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ padding: 16px;
+}
+
+.uploadIcon {
+ font-size: 32px;
+ color: #bfbfbf;
+ margin-bottom: 8px;
+}
+
+.uploadText {
+ font-size: 12px;
+ color: #999;
+ margin-bottom: 8px;
+}
+
+.uploadButton {
+ background-color: #f5f6fa;
+ border: 1px solid #d9d9d9;
+ color: #666;
+ border-radius: 4px;
+ font-size: 12px;
+ // 为小按钮添加轻微阴影
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
+ transition: all 0.3s ease;
+
+ &:hover {
+ border-color: #2d5cf6;
+ color: #2d5cf6;
+ box-shadow: 0 2px 6px rgba(45, 92, 246, 0.15);
+ }
+}
+
+.idCardImage {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ border-radius: 6px;
+ // 为上传的图片添加阴影
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+/* 提交区域 */
+.submitSection {
+ margin-top: 32px;
+ margin-bottom: 0 !important;
+}
+
+.submitButton {
+ background: linear-gradient(135deg, #2d5cf6 0%, #4c7bff 100%);
+ border: none;
+ border-radius: 8px;
+ font-weight: 600;
+ font-size: 16px;
+ height: 48px;
+ // 增强提交按钮阴影
+ box-shadow:
+ 0 4px 12px rgba(45, 92, 246, 0.3),
+ 0 2px 6px rgba(45, 92, 246, 0.2);
+ transition: all 0.3s ease;
+
+ &:hover {
+ background: linear-gradient(135deg, #4c7bff 0%, #6b8fff 100%);
+ transform: translateY(-2px);
+ box-shadow:
+ 0 8px 24px rgba(45, 92, 246, 0.4),
+ 0 4px 12px rgba(45, 92, 246, 0.3);
+ }
+
+ &:active {
+ transform: translateY(0);
+ box-shadow:
+ 0 2px 8px rgba(45, 92, 246, 0.3),
+ 0 1px 4px rgba(45, 92, 246, 0.2);
+ }
+}
+
+/* 输入框样式 */
+.container {
+ .ant-input,
+ .ant-input-number,
+ .ant-select-selector {
+ border-radius: 6px;
+ border: 1px solid #d9d9d9;
+ transition: all 0.3s ease;
+ // 为输入框添加轻微阴影
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
+
+ &:hover {
+ border-color: #4c7bff;
+ box-shadow: 0 2px 6px rgba(76, 123, 255, 0.1);
+ }
+
+ &:focus,
+ &.ant-input-focused,
+ &.ant-select-focused .ant-select-selector {
+ border-color: #2d5cf6;
+ box-shadow:
+ 0 0 0 2px rgba(45, 92, 246, 0.2),
+ 0 2px 8px rgba(45, 92, 246, 0.15);
+ }
+ }
+
+ .ant-input::placeholder {
+ color: #bfbfbf;
+ }
+}
+
+/* 响应式设计 */
+@media (max-width: 768px) {
+ .container {
+ padding: 16px;
+ height: 90vh; // 保持固定高度
+ }
+
+ .content {
+ /* 移动端使用更细的滚动条 */
+ &::-webkit-scrollbar {
+ width: 4px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: rgba(0, 0, 0, 0.12);
+ border-radius: 2px;
+
+ &:hover {
+ background: rgba(0, 0, 0, 0.20);
+ }
+ }
+ }
+
+ .formCard {
+ // 移动端减少阴影强度
+ box-shadow:
+ 0 1px 4px rgba(0, 0, 0, 0.06),
+ 0 2px 8px rgba(0, 0, 0, 0.08);
+
+ &:hover {
+ transform: none; // 移动端取消悬停上浮效果
+ box-shadow:
+ 0 1px 4px rgba(0, 0, 0, 0.06),
+ 0 2px 8px rgba(0, 0, 0, 0.08);
+ }
+
+ .ant-card-body {
+ padding: 24px 20px;
+ }
+ }
+
+ .faceUpload {
+ width: 100px;
+ height: 100px;
+ }
+
+ .cameraIcon {
+ font-size: 28px;
+ }
+
+ .uploadContent {
+ padding: 12px;
+ }
+
+ .uploadIcon {
+ font-size: 24px;
+ }
+
+ .idCardUpload .ant-upload {
+ height: 100px !important;
+ }
+}
+
+@media (max-width: 576px) {
+ .container {
+ padding: 12px; // 进一步减少内边距
+ }
+
+ .content {
+ padding-right: 4px; // 滚动条空间更小
+ }
+
+ .title {
+ font-size: 20px !important;
+ margin-bottom: 24px !important; // 减少标题下边距
+ }
+
+ .formCard {
+ .ant-card-body {
+ padding: 20px 16px;
+ }
+ }
+
+ .submitButton {
+ font-size: 14px;
+ height: 44px;
+ }
+}
\ No newline at end of file
diff --git a/src/pages/staffmanage_resignation/Resignation.js b/src/pages/staffmanage_resignation/Resignation.js
new file mode 100644
index 0000000..e7523a9
--- /dev/null
+++ b/src/pages/staffmanage_resignation/Resignation.js
@@ -0,0 +1,540 @@
+import React, { PureComponent } from 'react';
+import {
+ Card,
+ Tree,
+ Input,
+ Button,
+ DatePicker,
+ Select,
+ Row,
+ Col,
+ Space,
+ Radio,
+ Checkbox,
+ InputNumber,
+ Avatar,
+ message,
+ Timeline,
+ Tag,
+ Typography,
+ Divider
+} 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
+} from '@ant-design/icons';
+import dayjs from 'dayjs';
+import styles from './Resignation.less';
+
+const { Option } = Select;
+const { TextArea } = Input;
+const { Title, Text, Paragraph } = Typography;
+
+class Resignation extends PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ selectedOrgKeys: ['0-0-4'], // 默认选中人事部
+ expandedKeys: ['0-0', '0-0-0', '0-0-1', '0-0-2'],
+ // 员工异动审批数据
+ transferData: {
+ employee: {
+ name: '林志强',
+ employeeId: 'EMP20230085',
+ avatar: '/img/avatar1.jpg',
+ idCardFront: '/img/card.jpg',
+ idCardBack: '/img/card.jpg',
+ originalDepartment: '前端开发部',
+ newDepartment: '产品设计部',
+ originalPosition: '高级前端工程师',
+ newPosition: '产品设计师',
+ transferType: '部门调动',
+ transferDate: '2023-06-15',
+ reason: '根据公司人才发展规划,结合员工个人职业发展意愿,经双方协商一致,同意调动。',
+ status: '审批中'
+ },
+ approvalFlow: [
+ {
+ step: '提交申请',
+ status: 'completed',
+ operator: '林志强',
+ time: '2023-06-05 09:30',
+ comment: '申请人:林志强',
+ opinion: ''
+ },
+ {
+ step: '部门经理审批',
+ status: 'completed',
+ operator: '王建国(前端开发部经理)',
+ time: '2023-06-05 14:15',
+ comment: '审批人:王建国(前端开发部经理)',
+ opinion: '同意调动,该员工在产品设计方面有潜力。'
+ },
+ {
+ step: '技术总监审批',
+ status: 'completed',
+ operator: '张伟(技术中心总监)',
+ time: '2023-06-06 10:45',
+ comment: '审批人:张伟(技术中心总监)',
+ opinion: '同意,符合技术人才发展规划。'
+ },
+ {
+ step: 'HR审批',
+ status: 'completed',
+ operator: '李芳(人力资源部经理)',
+ time: '2023-06-07 11:20',
+ comment: '审批人:李芳(人力资源部经理)',
+ opinion: '已核实相关情况,同意调动。'
+ },
+ {
+ step: '总经理审批',
+ status: 'completed',
+ operator: '陈明(总经理)',
+ time: '2023-06-08 16:30',
+ comment: '审批人:陈明(总经理)',
+ opinion: '同意执行。'
+ },
+ {
+ step: '入职生效',
+ status: 'processing',
+ operator: '',
+ time: '待处理',
+ comment: '计划生效日期:2023-06-15',
+ opinion: ''
+ },
+ {
+ step: '审批完结',
+ status: 'waiting',
+ operator: '',
+ time: '未开始',
+ comment: '',
+ opinion: ''
+ }
+ ]
+ },
+ 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 });
+ };
+
+ // 同意审批
+ handleApprove = () => {
+ message.success('同意审批,已提交到下一环节');
+ };
+
+ // 拒绝审批
+ handleReject = () => {
+ message.error('拒绝审批');
+ };
+
+ // 打印
+ handlePrint = () => {
+ window.print();
+ };
+
+ // 获取审批状态图标
+ getStatusIcon = (status) => {
+ switch (status) {
+ case 'completed':
+ return ;
+ case 'processing':
+ return ;
+ case 'waiting':
+ return ;
+ default:
+ return ;
+ }
+ };
+
+ // 获取状态标签
+ getStatusTag = (status) => {
+ switch (status) {
+ case '审批中':
+ return 审批中;
+ case '已通过':
+ return 已通过;
+ case '已拒绝':
+ return 已拒绝;
+ default:
+ return {status};
+ }
+ };
+
+ render() {
+ const { selectedOrgKeys, expandedKeys, transferData } = this.state;
+ const { employee, approvalFlow } = transferData;
+
+ 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.handleApprove}
+ // className={styles.approveBtn}
+ >
+ 同意
+
+ }
+ onClick={this.handleReject}
+ // className={styles.rejectBtn}
+ >
+ 拒绝
+
+ }
+ onClick={this.handlePrint}
+ >
+ 打印
+
+
+
+
+ {/* 滚动内容区 */}
+
+ {/* 基本信息卡片 */}
+
+
+ {/* 员工照片 */}
+
+
+

+
员工照片
+
+
+
+ {/* 基本信息 */}
+
+
+
+
+
+ 员工姓名:
+ {employee.name}
+
+
+
+
+ 员工编号:
+ {employee.employeeId}
+
+
+
+
+ 原部门:
+ {employee.originalDepartment}
+
+
+
+
+ 新部门:
+ {employee.newDepartment}
+
+
+
+
+ 原岗位:
+ {employee.originalPosition}
+
+
+
+
+ 新岗位:
+ {employee.newPosition}
+
+
+
+
+ 异动类型:
+ {employee.transferType}
+
+
+
+
+ 异动时间:
+ {employee.transferDate}
+
+
+
+
+ 异动原因:
+ {employee.reason}
+
+
+
+
+
+
+
+
+ {/* 证件信息 */}
+
+
+
+
+
身份证正面
+

+
+
+
+
+
身份证反面
+

+
+
+
+
+
+ {/* 审批流程 */}
+
+
+ {approvalFlow.map((flow, index) => (
+
+
+
+ {flow.step}
+ {flow.time}
+
+
+ {flow.comment}
+
+ {flow.opinion && (
+
+ 审批意见:{flow.opinion}
+
+ )}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default Resignation;
\ No newline at end of file
diff --git a/src/pages/staffmanage_resignation/Resignation.less b/src/pages/staffmanage_resignation/Resignation.less
new file mode 100644
index 0000000..c2c8601
--- /dev/null
+++ b/src/pages/staffmanage_resignation/Resignation.less
@@ -0,0 +1,408 @@
+@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;
+ }
+}
+
+// 头部区域
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 24px;
+ border-bottom: 1px solid #f0f0f0;
+ flex-shrink: 0;
+ height: 72px;
+ // background: #fafafa;
+}
+
+// 内容区域
+.content {
+ flex: 1;
+ padding: 20px 24px 28px 24px;
+ overflow-y: auto;
+ overflow-x: hidden;
+ min-height: 0; // 关键:允许flex子元素收缩
+ height: calc(100vh - 180px); // 减去头部高度和其他空间,强制设置高度
+ max-height: calc(100vh - 180px); // 明确最大高度,确保滚动条出现
+ &::-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);
+}
+
+// 按钮样式
+.approveBtn {
+ background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%);
+ border: none;
+ box-shadow: 0 2px 8px rgba(82, 196, 26, 0.3);
+
+ &:hover {
+ background: linear-gradient(135deg, #73d13d 0%, #95de64 100%);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(82, 196, 26, 0.4);
+ }
+}
+
+.rejectBtn {
+ box-shadow: 0 2px 8px rgba(255, 77, 79, 0.3);
+
+ &:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(255, 77, 79, 0.4);
+ }
+}
+
+// 信息卡片
+.infoCard {
+ margin-bottom: 20px;
+ border-radius: 8px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .ant-card-head {
+ border-bottom: 1px solid #f0f0f0;
+
+ .ant-card-head-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ }
+ }
+
+ .ant-card-body {
+ padding: 20px;
+ }
+}
+
+// 照片容器
+.photoContainer {
+ text-align: center;
+
+ .employeePhoto {
+ width: 96px;
+ height: 96px;
+ border-radius: 50%;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ margin-bottom: 8px;
+ object-fit: cover;
+ }
+
+ .photoLabel {
+ display: block;
+ font-size: 12px;
+ color: #666;
+ }
+}
+
+// 身份证容器
+.idCardContainer {
+ text-align: center;
+
+ .photoLabel {
+ display: block;
+ font-size: 12px;
+ color: #666;
+ margin-bottom: 8px;
+ }
+
+ .idCardPhoto {
+ width: 100%;
+ max-width: 280px; // 限制最大宽度
+ height: 140px; // 从 192px 改为 140px
+ border-radius: 8px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ object-fit: cover;
+ }
+}
+
+// 基本信息
+.basicInfo {
+ .infoItem {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 8px;
+
+ .infoLabel {
+ color: #666;
+ font-size: 14px;
+ flex-shrink: 0;
+ margin-right: 8px;
+ }
+
+ .infoValue {
+ font-weight: 500;
+ color: #333;
+ font-size: 14px;
+ text-align: right;
+ flex: 1;
+ word-break: break-all;
+ }
+ }
+}
+
+// 审批流程时间线
+.approvalTimeline {
+ .timelineItem {
+ padding-bottom: 24px;
+
+ .timelineContent {
+ background: #fafafa;
+ border-radius: 8px;
+ padding: 16px;
+ margin-left: 16px;
+ border: 1px solid #f0f0f0;
+
+ .timelineHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+
+ .stepTitle {
+ font-size: 16px;
+ color: #333;
+ }
+
+ .stepTime {
+ font-size: 12px;
+ color: #999;
+ }
+ }
+
+ .stepComment {
+ margin: 0;
+ color: #666;
+ font-size: 14px;
+ }
+
+ .stepOpinion {
+ margin-top: 8px;
+ padding: 8px 12px;
+ background: #e6f7ff;
+ border-radius: 4px;
+ border-left: 3px solid #1890ff;
+
+ span {
+ color: #1890ff;
+ font-size: 13px;
+ }
+ }
+ }
+ }
+}
+
+// 状态图标
+.completedIcon {
+ color: #52c41a;
+ font-size: 16px;
+}
+
+.processingIcon {
+ color: #faad14;
+ font-size: 16px;
+}
+
+.waitingIcon {
+ color: #d9d9d9;
+ font-size: 16px;
+}
+
+// 响应式设计
+@media (max-width: 1200px) {
+ .treeCard {
+ height: 300px;
+ }
+
+ .mainCard {
+ height: auto;
+ max-height: 80vh;
+ min-height: 500px;
+
+ .content {
+ max-height: 65vh;
+
+ &::-webkit-scrollbar {
+ width: 4px;
+ }
+ }
+ }
+
+ .photoContainer {
+ margin-bottom: 16px;
+
+ .employeePhoto {
+ width: 80px;
+ height: 80px;
+ }
+ }
+
+ .idCardContainer {
+ margin-bottom: 16px;
+
+ .idCardPhoto {
+ max-width: 240px; // 移动端进一步限制宽度
+ height: 120px; // 移动端从 150px 改为 120px
+ }
+ }
+}
+
+@media (max-width: 768px) {
+ .idCardContainer {
+ .idCardPhoto {
+ max-width: 200px; // 小屏幕设备进一步缩小
+ height: 100px; // 小屏幕设备高度调整
+ }
+ }
+}
+
+// 页面特定样式
+.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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pages/staffmanage_staffinfo/StaffInfo.js b/src/pages/staffmanage_staffinfo/StaffInfo.js
index 1739111..959cbf4 100644
--- a/src/pages/staffmanage_staffinfo/StaffInfo.js
+++ b/src/pages/staffmanage_staffinfo/StaffInfo.js
@@ -25,7 +25,7 @@ import {
} from '@ant-design/icons';
import styles from './StaffInfo.less';
import StandardTable from '@/components/StandardTable';
-import StaffInfoAdd from './form/StaffInfoadd';
+import StaffInfoAdd from './form/StaffInfoAdd';
import StaffInfoRenderSimpleForm from "./form/StaffInfoRenderSimpleForm" //表单
const { Option } = Select;
@@ -220,6 +220,7 @@ class StaffInfo extends PureComponent {
title: '操作',
key: 'action',
width: 80,
+ align: 'center',
render: (_, record) => (
}
- size="large"
- className={styles.addButton}
+ size="middle"
+ // className={styles.addButton}
onClick={this.showAddModal}
>
新增
@@ -516,7 +517,7 @@ class StaffInfo extends PureComponent {
columns={this.defaultColumns}
dataSource={tableData}
pagination={false}
- scroll={{ x: 1000,y: 600 }}
+ // scroll={{ x: 1000,y: 600 }}
size="small"
selectedRows={[]}
/>
diff --git a/src/pages/staffmanage_staffinfo/StaffInfo.less b/src/pages/staffmanage_staffinfo/StaffInfo.less
index 0837d7c..bde85d9 100644
--- a/src/pages/staffmanage_staffinfo/StaffInfo.less
+++ b/src/pages/staffmanage_staffinfo/StaffInfo.less
@@ -2,7 +2,8 @@
.staffInfoContainer {
min-height: 100vh;
- background-color: #f5f6fa;
+ height: 100vh;
+ // background-color: #f5f6fa;
.announcementBar {
background: #e6f7ff;
@@ -48,7 +49,7 @@
}
.mainContent {
- padding: 24px;
+ // padding: 12px;
.contentCard {
.ant-card-head {
@@ -154,68 +155,77 @@
.searchCard {
margin-bottom: 16px;
- background-color: #f9fbfc;
border-radius: 8px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
.ant-card-body {
- padding: 12px;
- background-color: #f9fbfc;
- border-radius: 6px;
+ padding: 16px;
}
- .ant-form-item-label {
- font-weight: 500;
-
- label {
- color: #ecf0f1 !important;
- }
- }
+ /* 搜索表单容器样式 */
+ .searchFormContainer {
+ .searchForm {
+ .ant-form-item {
+ margin-bottom: 16px;
- // 深色背景下的表单项样式
- .ant-input,
- .ant-select-selector {
- background-color: #34495e !important;
- border-color: #5a6c7d !important;
- color: #ecf0f1 !important;
+ .ant-form-item-label {
+ font-weight: 500;
+ color: #333;
+
+ label {
+ color: #333 !important;
+ font-size: 14px;
+ }
+ }
- &:hover {
- border-color: #7fb3d3 !important;
- }
+ .ant-input,
+ .ant-select-selector,
+ .ant-picker {
+ border-radius: 6px;
+ border: 1px solid #d9d9d9;
+ transition: all 0.3s ease;
- &:focus {
- border-color: #2d5cf6 !important;
- box-shadow: 0 0 0 2px rgba(45, 92, 246, 0.2) !important;
- }
- }
+ &:hover {
+ border-color: #4c7bff;
+ }
- .ant-select-selection-placeholder,
- .ant-input::placeholder {
- color: #95a5a6 !important;
- }
+ &: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);
+ }
+ }
- .anticon {
- color: #95a5a6 !important;
+ .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: 8px;
-
- font-weight: 600;
+ border-radius: 6px;
+ color: white;
+ font-weight: 500;
font-size: 14px;
- box-shadow: 0 4px 12px rgba(45, 92, 246, 0.3);
+ box-shadow: 0 2px 8px rgba(45, 92, 246, 0.3);
transition: all 0.3s ease;
- margin-top: -8px;
- height: 35px;
+ height: 32px;
padding: 0 16px;
&:hover,
&:focus {
background: linear-gradient(135deg, #4c7bff 0%, #6b8fff 100%);
- transform: translateY(-2px);
- box-shadow: 0 6px 20px rgba(45, 92, 246, 0.4);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(45, 92, 246, 0.4);
+ color: white;
}
&:active {
@@ -225,31 +235,39 @@
/* 重置按钮样式 */
.resetButton {
- background: linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%);
+ background: #fff;
border: 1px solid #d9d9d9;
color: #666;
- border-radius: 8px;
+ border-radius: 6px;
font-weight: 500;
font-size: 14px;
transition: all 0.3s ease;
- margin-top: -8px;
- height: 35px;
+ height: 32px;
padding: 0 16px;
-
&:hover,
&:focus {
- background: linear-gradient(135deg, #e8e8e8 0%, #dcdcdc 100%);
- border-color: #bfbfbf;
- color: #333;
- transform: translateY(-1px);
- box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1);
+ border-color: #4c7bff;
+ color: #2d5cf6;
}
.anticon {
color: #ff7875;
}
}
+
+ /* 展开按钮样式 */
+ .expandButton {
+ color: #2d5cf6;
+ font-size: 14px;
+ padding: 0 8px;
+ height: 32px;
+
+ &:hover,
+ &:focus {
+ color: #4c7bff;
+ }
+ }
}
.actionBar {
@@ -325,81 +343,6 @@
}
}
- .tableCard {
- border-radius: 8px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
-
- .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;
- }
- }
- }
- }
-
-
.tableCard {
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
@@ -462,7 +405,7 @@
border-radius: 4px;
&.ant-pagination-item-active {
- background-color: #fff;
+ background-color: #2d5cf6;
border-color: #2d5cf6;
}
}
@@ -477,14 +420,11 @@
}
}
-
-
-
-
.mainContent {
- padding: 12px;
+ // padding: 12px;
.contentCard {
+ border: none !important;
.ant-card-head {
.ant-card-head-title {
font-size: 18px;
@@ -494,144 +434,6 @@
}
}
- .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;
- background-color: #f9fbfc;
- border-radius: 8px;
-
- .ant-card-body {
- padding: 10px;
- background-color: #f9fbfc;
- border-radius: 6px;
- }
-
- .ant-form-item-label {
- font-weight: 500;
-
- label {
- color: #ecf0f1 !important;
- }
- }
-
- // 深色背景下的表单项样式
- .ant-input,
- .ant-select-selector {
- background-color: #34495e !important;
- border-color: #5a6c7d !important;
- color: #ecf0f1 !important;
-
- &:hover {
- border-color: #7fb3d3 !important;
- }
-
- &:focus {
- border-color: #2d5cf6 !important;
- box-shadow: 0 0 0 2px rgba(45, 92, 246, 0.2) !important;
- }
- }
-
- .ant-select-selection-placeholder,
- .ant-input::placeholder {
- color: #95a5a6 !important;
- }
-
- .anticon {
- color: #95a5a6 !important;
- }
- }
-
.actionBar {
display: flex;
justify-content: space-between;
@@ -644,14 +446,11 @@
}
}
-
-
:global {
.ant-card-body {
padding: 12px 24px 0px 24px;
}
}
-
}
diff --git a/src/pages/staffmanage_staffinfo/form/StaffInfoRenderSimpleForm.js b/src/pages/staffmanage_staffinfo/form/StaffInfoRenderSimpleForm.js
index a82322f..1f6f0d2 100644
--- a/src/pages/staffmanage_staffinfo/form/StaffInfoRenderSimpleForm.js
+++ b/src/pages/staffmanage_staffinfo/form/StaffInfoRenderSimpleForm.js
@@ -1,91 +1,89 @@
import React, { useState } from 'react';
-import { Button, Col, Form, Input, Row, message,Select, Space } from 'antd';
-import { ClearOutlined,SearchOutlined } from '@ant-design/icons';
+import { Button, Col, Form, Input, Row, message, Select, Space, DatePicker } from 'antd';
+import { ClearOutlined, SearchOutlined, ExpandOutlined, CompressOutlined } from '@ant-design/icons';
import styles from "../StaffInfo.less";
-const FormItem = Form.Item;
+const FormItem = Form.Item;
+const { Option } = Select;
const StaffInfoRenderSimpleForm = (props) => {
const [form] = Form.useForm();
- const [startDateOpen, setStartDateOpen] = useState(false);
- const [endDateOpen, setEndDateOpen] = useState(false);
+ const [expandForm, setExpandForm] = useState(false);
const { submitButtons, handleSearch, handleFormReset, toggleForm, params } = props;
+
React.useEffect(() => {
form.setFieldsValue({
- opername: params?.opername,
- opertype: params?.opertype,
+ name: params?.name,
+ phone: params?.phone,
+ idCard: params?.idCard,
+ department: params?.department,
+ position: params?.position,
+ status: params?.status,
});
}, [params]);
+
const onFinish = values => {
- const params = {
+ const searchParams = {
...values,
- startDate: values.startDate ? values.startDate.format('YYYY-MM-DD') : undefined,
- endDate: values.endDate ? values.endDate.format('YYYY-MM-DD') : undefined,
+ entryDateStart: values.entryDateStart ? values.entryDateStart.format('YYYY-MM-DD') : undefined,
+ entryDateEnd: values.entryDateEnd ? values.entryDateEnd.format('YYYY-MM-DD') : undefined,
};
- handleSearch(params);
+ handleSearch && handleSearch(searchParams);
};
const myhandleFormReset = () => {
form.resetFields();
- handleFormReset();
+ handleFormReset && handleFormReset();
+ };
+
+ const toggleExpandForm = () => {
+ setExpandForm(!expandForm);
};
const onEndDateChange = (value) => {
- const startDate = form.getFieldValue("startDate");
+ const startDate = form.getFieldValue("entryDateStart");
if (startDate && value && value.isBefore(startDate, "day")) {
message.error("结束日期不能小于开始日期!");
- // 清空
- form.setFieldsValue({ endDate: undefined });
+ form.setFieldsValue({ entryDateEnd: undefined });
}
- }
+ };
return (
-
+
);
diff --git a/src/pages/staffmanage_training/Training.js b/src/pages/staffmanage_training/Training.js
new file mode 100644
index 0000000..1de1e3e
--- /dev/null
+++ b/src/pages/staffmanage_training/Training.js
@@ -0,0 +1,631 @@
+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
+} 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
+} from '@ant-design/icons';
+import dayjs from 'dayjs';
+import styles from './Training.less';
+
+const { Option } = Select;
+const { TextArea } = Input;
+const { Title, Text, Paragraph } = Typography;
+const { RangePicker } = DatePicker;
+
+class Training extends PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ selectedOrgKeys: ['0-0-4'], // 默认选中人事部
+ expandedKeys: ['0-0', '0-0-0', '0-0-1', '0-0-2'],
+ searchKeyword: '',
+ // 培训数据
+ trainingData: {
+ basicInfo: {
+ name: '',
+ cover: null,
+ description: ''
+ },
+ details: {
+ startDate: null,
+ endDate: null,
+ cost: ''
+ }
+ },
+ // 可选课程
+ availableCourses: [
+ {
+ id: 1,
+ name: '高效沟通技巧',
+ instructor: '张明远',
+ duration: '2小时',
+ selected: false
+ },
+ {
+ id: 2,
+ name: '团队协作与领导力',
+ instructor: '李思雨',
+ duration: '3小时',
+ selected: false
+ },
+ {
+ id: 3,
+ name: '项目管理实战',
+ instructor: '王建国',
+ duration: '4小时',
+ selected: false
+ },
+ {
+ id: 4,
+ name: 'Excel高级应用',
+ instructor: '陈晓芳',
+ duration: '2.5小时',
+ selected: false
+ },
+ {
+ id: 5,
+ name: '商务礼仪与职业形象',
+ instructor: '赵雅芝',
+ duration: '2小时',
+ selected: false
+ }
+ ],
+ // 已选课程
+ selectedCourses: [
+ {
+ id: 1,
+ name: '高效沟通技巧',
+ instructor: '张明远',
+ duration: '2小时',
+ scheduleDate: null,
+ scheduleTime: null,
+ showSchedule: false
+ },
+ {
+ id: 2,
+ name: '团队协作与领导力',
+ instructor: '李思雨',
+ duration: '3小时',
+ scheduleDate: null,
+ scheduleTime: null,
+ showSchedule: false
+ }
+ ],
+ 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 });
+ };
+
+ // 新建培训
+ handleCreateTraining = () => {
+ message.success('新建培训');
+ };
+
+ // 选择课程
+ handleSelectCourse = (course) => {
+ const { selectedCourses, availableCourses } = this.state;
+
+ // 检查是否已经选择
+ const isSelected = selectedCourses.find(item => item.id === course.id);
+ if (isSelected) {
+ message.warning('该课程已经选择');
+ return;
+ }
+
+ const newSelectedCourse = {
+ ...course,
+ scheduleDate: null,
+ scheduleTime: null,
+ showSchedule: false
+ };
+
+ this.setState({
+ selectedCourses: [...selectedCourses, newSelectedCourse],
+ availableCourses: availableCourses.map(item =>
+ item.id === course.id ? { ...item, selected: true } : item
+ )
+ });
+ message.success('课程添加成功');
+ };
+
+ // 移除课程
+ handleRemoveCourse = (courseId) => {
+ const { selectedCourses, availableCourses } = this.state;
+
+ this.setState({
+ selectedCourses: selectedCourses.filter(item => item.id !== courseId),
+ availableCourses: availableCourses.map(item =>
+ item.id === courseId ? { ...item, selected: false } : item
+ )
+ });
+ message.success('课程移除成功');
+ };
+
+ // 设置课程时间
+ handleSetSchedule = (courseId) => {
+ const { selectedCourses } = this.state;
+ this.setState({
+ selectedCourses: selectedCourses.map(item =>
+ item.id === courseId ? { ...item, showSchedule: !item.showSchedule } : item
+ )
+ });
+ };
+
+ // 确认时间设置
+ handleConfirmSchedule = (courseId, date, time) => {
+ const { selectedCourses } = this.state;
+ this.setState({
+ selectedCourses: selectedCourses.map(item =>
+ item.id === courseId ? {
+ ...item,
+ scheduleDate: date,
+ scheduleTime: time,
+ showSchedule: false
+ } : item
+ )
+ });
+ message.success('时间设置成功');
+ };
+
+ // 搜索课程
+ handleSearchCourse = (value) => {
+ this.setState({ searchKeyword: value });
+ };
+
+ // 保存培训
+ handleSave = () => {
+ message.success('培训保存成功');
+ };
+
+ // 取消
+ handleCancel = () => {
+ message.info('取消操作');
+ };
+
+ // 封面上传
+ handleCoverUpload = (info) => {
+ if (info.file.status === 'done') {
+ message.success('封面上传成功');
+ }
+ };
+
+ // 过滤课程
+ getFilteredCourses = () => {
+ const { availableCourses, searchKeyword } = this.state;
+ if (!searchKeyword) return availableCourses;
+
+ return availableCourses.filter(course =>
+ course.name.toLowerCase().includes(searchKeyword.toLowerCase()) ||
+ course.instructor.toLowerCase().includes(searchKeyword.toLowerCase())
+ );
+ };
+
+ render() {
+ const { selectedOrgKeys, expandedKeys, selectedCourses, searchKeyword } = this.state;
+ const filteredCourses = this.getFilteredCourses();
+
+ 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.handleCreateTraining}
+ className={styles.createBtn}
+ >
+ 新建培训
+
+
+
+ {/* 滚动内容区 */}
+
+ {/* 培训基本信息 */}
+
+
+
+
+ 培训名称
+
+
+
+
+
+
+
+
+ 主题设计
+
+
+
+
+ {/* 培训详情 */}
+
+
+
+
+ 培训时间
+
+
+
+
+
+ 培训费用(元)
+
+
+
+
+
+
+ {/* 课程管理 */}
+
+
+ {/* 可选课程 */}
+
+
+
+ 可选课程
+ }
+ value={searchKeyword}
+ onChange={(e) => this.handleSearchCourse(e.target.value)}
+ className={styles.searchInput}
+ />
+
+
+ {filteredCourses.map(course => (
+
this.handleSelectCourse(course)}
+ >
+
+ {course.name}
+ 讲师: {course.instructor}
+
+
{course.duration}
+
+ ))}
+
+
+
+
+ {/* 已选课程 */}
+
+
+
已选课程
+
+ {selectedCourses.map(course => (
+
+
+
+ {course.name}
+ 讲师: {course.instructor}
+
+
+ {course.duration}
+ this.handleSetSchedule(course.id)}
+ className={styles.scheduleBtn}
+ >
+ 设置时间
+
+ }
+ onClick={() => this.handleRemoveCourse(course.id)}
+ className={styles.removeBtn}
+ />
+
+
+
+ {course.showSchedule && (
+
+
+ 上课时间:
+
+
+
+
+ this.handleConfirmSchedule(course.id)}
+ className={styles.confirmBtn}
+ >
+ 确认
+
+
+
+ )}
+
+ ))}
+
+
+
+
+
+
+ {/* 底部操作按钮 */}
+
+ 取消
+
+ 保存
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default Training;
\ No newline at end of file
diff --git a/src/pages/staffmanage_training/Training.less b/src/pages/staffmanage_training/Training.less
new file mode 100644
index 0000000..e648175
--- /dev/null
+++ b/src/pages/staffmanage_training/Training.less
@@ -0,0 +1,525 @@
+@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;
+ }
+}
+
+// 头部区域
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 16px 24px;
+ border-bottom: 1px solid #f0f0f0;
+ flex-shrink: 0;
+ height: 72px;
+ // background: #fafafa;
+}
+
+// 内容区域
+.content {
+ flex: 1;
+ padding: 20px 24px 28px 24px;
+ overflow-y: auto;
+ overflow-x: hidden;
+ min-height: 0; // 关键:允许flex子元素收缩
+ height: calc(100vh - 180px); // 减去头部高度和其他空间,强制设置高度
+ max-height: calc(100vh - 180px); // 明确最大高度,确保滚动条出现
+ &::-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);
+}
+
+// 按钮样式
+.createBtn {
+ background: linear-gradient(135deg, #2d5cf6 0%, #4c7bff 100%);
+ border: none;
+ box-shadow: 0 2px 8px rgba(45, 92, 246, 0.3);
+
+ &:hover {
+ background: linear-gradient(135deg, #4c7bff 0%, #6b8fff 100%);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(45, 92, 246, 0.4);
+ }
+}
+
+.saveBtn {
+ background: linear-gradient(135deg, #2d5cf6 0%, #4c7bff 100%);
+ border: none;
+ box-shadow: 0 2px 8px rgba(45, 92, 246, 0.3);
+
+ &:hover {
+ background: linear-gradient(135deg, #4c7bff 0%, #6b8fff 100%);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(45, 92, 246, 0.4);
+ }
+}
+
+// 区块卡片
+.sectionCard {
+ margin-bottom: 20px;
+ border-radius: 8px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .ant-card-head {
+ border-bottom: 1px solid #f0f0f0;
+
+ .ant-card-head-title {
+ font-size: 16px;
+ font-weight: 600;
+ color: #333;
+ }
+ }
+
+ .ant-card-body {
+ padding: 20px;
+ }
+}
+
+// 表单样式
+.formItem {
+ margin-bottom: 16px;
+
+ .formLabel {
+ display: block;
+ margin-bottom: 4px;
+ font-weight: 500;
+ color: #333;
+ font-size: 14px;
+ }
+
+ .formInput,
+ .formTextArea {
+ border-radius: 6px;
+ border: 1px solid #d9d9d9;
+ transition: all 0.3s ease;
+
+ &:hover {
+ border-color: #4c7bff;
+ }
+
+ &:focus,
+ &.ant-input-focused {
+ border-color: #2d5cf6;
+ box-shadow: 0 0 0 2px rgba(45, 92, 246, 0.2);
+ }
+ }
+}
+
+// 上传区域
+.uploadContainer {
+ display: flex;
+ align-items: center;
+
+ .coverUpload {
+ margin-right: 8px;
+
+ .uploadArea {
+ width: 64px;
+ height: 64px;
+ border: 1px dashed #d9d9d9;
+ border-radius: 8px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: #fafafa;
+ cursor: pointer;
+ transition: all 0.3s ease;
+
+ &:hover {
+ border-color: #2d5cf6;
+ background: #f0f9ff;
+ }
+
+ .uploadIcon {
+ font-size: 24px;
+ color: #bfbfbf;
+ }
+ }
+ }
+
+ .uploadText {
+ font-size: 12px;
+ color: #666;
+ }
+}
+
+// 课程管理区域
+.courseSection {
+ .courseSectionHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+
+ .courseSectionTitle {
+ font-weight: 500;
+ color: #333;
+ font-size: 14px;
+ }
+
+ .searchInput {
+ width: 128px;
+ border-radius: 4px;
+ }
+ }
+
+ .courseList {
+ height: 256px;
+ overflow-y: auto;
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ padding: 8px;
+ background: #fafafa;
+
+ &::-webkit-scrollbar {
+ width: 4px;
+ }
+
+ &::-webkit-scrollbar-track {
+ background: rgba(0, 0, 0, 0.02);
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: rgba(0, 0, 0, 0.15);
+ border-radius: 2px;
+
+ &:hover {
+ background: rgba(0, 0, 0, 0.25);
+ }
+ }
+ }
+}
+
+// 课程项
+.courseItem {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px 12px;
+ margin-bottom: 8px;
+ background: white;
+ border-radius: 6px;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ border: 1px solid transparent;
+
+ &:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ border-color: #e8e8e8;
+ }
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .courseInfo {
+ flex: 1;
+
+ .courseName {
+ display: block;
+ font-weight: 500;
+ color: #333;
+ font-size: 14px;
+ margin-bottom: 2px;
+ }
+
+ .courseInstructor {
+ font-size: 12px;
+ color: #666;
+ }
+ }
+
+ .courseDuration {
+ background: rgba(45, 92, 246, 0.1);
+ color: #2d5cf6;
+ border: none;
+ border-radius: 12px;
+ font-size: 12px;
+ padding: 2px 8px;
+ }
+}
+
+.courseItemSelected {
+ background: #f0f0f0 !important;
+ cursor: not-allowed;
+
+ &:hover {
+ transform: none;
+ box-shadow: none;
+ }
+}
+
+// 已选课程项
+.selectedCourseItem {
+ margin-bottom: 8px;
+ padding: 12px;
+ background: rgba(45, 92, 246, 0.05);
+ border-radius: 6px;
+ border: 1px solid rgba(45, 92, 246, 0.1);
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .courseItemHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 8px;
+
+ .courseActions {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+
+ .scheduleBtn {
+ color: #2d5cf6;
+ font-size: 12px;
+ padding: 0;
+ height: auto;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+
+ .removeBtn {
+ color: #ff4d4f;
+
+ &:hover {
+ background: rgba(255, 77, 79, 0.1);
+ }
+ }
+ }
+ }
+}
+
+// 时间设置区域
+.scheduleSettings {
+ background: #f5f5f5;
+ padding: 8px 12px;
+ border-radius: 4px;
+
+ .scheduleRow {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 8px;
+
+ .scheduleLabel {
+ font-size: 12px;
+ color: #666;
+ white-space: nowrap;
+ }
+
+ .dateInput {
+ flex: 1;
+ height: 28px;
+ font-size: 12px;
+ }
+
+ .timeInput {
+ width: 96px;
+ height: 28px;
+ font-size: 12px;
+ }
+ }
+
+ .scheduleActions {
+ display: flex;
+ justify-content: flex-end;
+
+ .confirmBtn {
+ height: 24px;
+ font-size: 12px;
+ padding: 0 12px;
+ }
+ }
+}
+
+// 底部操作按钮
+.actionButtons {
+ margin-top: 24px;
+ display: flex;
+ justify-content: flex-end;
+ gap: 12px;
+ padding-top: 20px;
+ border-top: 1px solid #f0f0f0;
+}
+
+// 响应式设计
+@media (max-width: 1200px) {
+ .treeCard {
+ height: 300px;
+ }
+
+ .mainCard {
+ height: auto;
+ max-height: 80vh;
+ min-height: 500px;
+
+ .content {
+ max-height: 65vh;
+
+ &::-webkit-scrollbar {
+ width: 4px;
+ }
+ }
+ }
+
+ .courseList {
+ height: 200px;
+ }
+}
+
+// 页面特定样式
+.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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/pages/staffmanage_trainingplan/TrainingPlan.js b/src/pages/staffmanage_trainingplan/TrainingPlan.js
new file mode 100644
index 0000000..9faa513
--- /dev/null
+++ b/src/pages/staffmanage_trainingplan/TrainingPlan.js
@@ -0,0 +1,599 @@
+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
+} from '@ant-design/icons';
+import dayjs from 'dayjs';
+import styles from './TrainingPlan.less';
+
+const { Option } = Select;
+const { TextArea } = Input;
+const { Title, Text, Paragraph } = Typography;
+const { RangePicker } = DatePicker;
+
+class TrainingPlan extends PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ selectedOrgKeys: ['0-0-4'], // 默认选中人事部
+ expandedKeys: ['0-0', '0-0-0', '0-0-1', '0-0-2'],
+ // 培训计划详情数据
+ trainingPlanData: {
+ id: 'TP2023Q3001',
+ title: '2023年第三季度新员工培训计划',
+ dateRange: '2023-07-01 至 2023-09-30',
+ participants: 86,
+ manager: '张明远',
+ status: '进行中',
+ completedCourses: 12,
+ totalCourses: 24,
+ participationRate: 87.5,
+ description: '本培训计划针对2023年第三季度新入职员工设计,包含公司文化、产品知识、业务流程等全方位培训内容。通过系统化的培训帮助新员工快速融入团队,掌握工作所需的基本技能和知识,提升工作效率和团队协作能力。'
+ },
+ // 课程列表数据
+ courseList: [
+ {
+ key: '1',
+ courseName: '公司文化与价值观',
+ instructor: '李华',
+ courseTime: '2023-07-03 09:00-12:00',
+ courseType: '现场',
+ courseUrl: '进入课程',
+ location: '总部大楼3层培训室A'
+ },
+ {
+ key: '2',
+ courseName: '产品知识入门',
+ instructor: '王伟',
+ courseTime: '2023-07-05 14:00-17:00',
+ courseType: '在线',
+ courseUrl: '进入课程',
+ location: 'https://meeting.zoom.us/123456'
+ },
+ {
+ key: '3',
+ courseName: '业务流程概述',
+ instructor: '张丽',
+ courseTime: '2023-07-07 09:00-12:00',
+ courseType: '现场',
+ courseUrl: '进入课程',
+ location: '总部大楼3层培训室B'
+ },
+ {
+ key: '4',
+ courseName: '团队协作与沟通',
+ instructor: '陈明',
+ courseTime: '2023-07-10 14:00-17:00',
+ courseType: '在线',
+ courseUrl: '进入课程',
+ location: 'https://meeting.zoom.us/789012'
+ },
+ {
+ key: '5',
+ courseName: '办公软件高级应用',
+ instructor: '刘芳',
+ courseTime: '2023-07-12 09:00-12:00',
+ courseType: '现场',
+ courseUrl: '进入课程',
+ location: '总部大楼3层培训室A'
+ }
+ ],
+ pagination: {
+ current: 1,
+ pageSize: 5,
+ total: 24
+ },
+ 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 });
+ };
+
+ // 编辑培训计划
+ handleEdit = () => {
+ message.success('编辑培训计划');
+ };
+
+ // 分享培训计划
+ handleShare = () => {
+ message.success('分享培训计划');
+ };
+
+ // 筛选课程
+ handleFilter = () => {
+ message.info('筛选课程');
+ };
+
+ // 导出课程
+ handleExport = () => {
+ message.success('导出课程列表');
+ };
+
+ // 添加课程
+ handleAddCourse = () => {
+ message.success('添加新课程');
+ };
+
+ // 编辑课程
+ handleEditCourse = (record) => {
+ message.success(`编辑课程: ${record.courseName}`);
+ };
+
+ // 删除课程
+ handleDeleteCourse = (record) => {
+ message.success(`删除课程: ${record.courseName}`);
+ };
+
+ // 进入课程
+ handleEnterCourse = (record) => {
+ message.success(`进入课程: ${record.courseName}`);
+ };
+
+ // 分页变化
+ handlePaginationChange = (page, pageSize) => {
+ this.setState({
+ pagination: {
+ ...this.state.pagination,
+ current: page,
+ pageSize: pageSize
+ }
+ });
+ };
+
+ // 获取课程类型标签
+ getCourseTypeTag = (type) => {
+ return type === '现场' ?
+ 现场 :
+ 在线;
+ };
+
+ render() {
+ const { selectedOrgKeys, expandedKeys, trainingPlanData, courseList, pagination } = this.state;
+
+ // 课程表格列配置
+ const columns = [
+ {
+ title: '课程名称',
+ dataIndex: 'courseName',
+ key: 'courseName',
+ render: (text) => {text}
+ },
+ {
+ title: '讲师',
+ dataIndex: 'instructor',
+ key: 'instructor'
+ },
+ {
+ title: '课程时间',
+ dataIndex: 'courseTime',
+ key: 'courseTime'
+ },
+ {
+ title: '课程类型',
+ dataIndex: 'courseType',
+ key: 'courseType',
+ render: (type) => this.getCourseTypeTag(type)
+ },
+ {
+ title: '课程入口',
+ dataIndex: 'courseUrl',
+ key: 'courseUrl',
+ render: (text, record) => (
+ this.handleEnterCourse(record)}
+ className={styles.enterBtn}
+ >
+ {text}
+
+ )
+ },
+ {
+ title: '课程地址',
+ dataIndex: 'location',
+ key: 'location'
+ },
+ {
+ title: '操作',
+ key: 'action',
+ render: (_, record) => (
+
+ }
+ onClick={() => this.handleEditCourse(record)}
+ className={styles.actionBtn}
+ />
+ }
+ onClick={() => this.handleDeleteCourse(record)}
+ className={styles.deleteBtn}
+ />
+
+ )
+ }
+ ];
+
+ 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}
+ >
+
+
+
+
+
+
+ {/* 右侧培训计划详情 */}
+
+
+ {/* 滚动内容区 */}
+
+ {/* 培训基本信息 */}
+
+
+
+
+ {trainingPlanData.title}
+
+
+
+
+
+ {trainingPlanData.dateRange}
+
+
+
+ 参与人数: {trainingPlanData.participants}人
+
+
+
+ 负责人: {trainingPlanData.manager}
+
+
+
+
+
+ }
+ onClick={this.handleEdit}
+ className={styles.editBtn}
+ >
+ 编辑
+
+ }
+ onClick={this.handleShare}
+ className={styles.shareBtn}
+ >
+ 分享
+
+
+
+
+ {/* 统计信息 */}
+
+
+
+
培训状态
+
+ {trainingPlanData.status}
+
+
+
+
+
+
已完成课程
+
+ {trainingPlanData.completedCourses}/{trainingPlanData.totalCourses}
+
+
+
+
+
+
平均参与率
+
+ {trainingPlanData.participationRate}%
+
+
+
+
+
+ {/* 培训描述 */}
+
+
培训描述
+
+ {trainingPlanData.description}
+
+
+
+
+ {/* 课程计划列表 */}
+
+
+
课程计划
+
+ }
+ onClick={this.handleFilter}
+ className={styles.filterBtn}
+ >
+ 筛选
+
+ }
+ onClick={this.handleExport}
+ className={styles.exportBtn}
+ >
+ 导出
+
+ }
+ onClick={this.handleAddCourse}
+ className={styles.addBtn}
+ >
+ 添加课程
+
+
+
+
+ {/* 课程表格 */}
+
+
({
+ className: styles.tableRow
+ })}
+ />
+
+ {/* 分页 */}
+
+
+ 显示 {((pagination.current - 1) * pagination.pageSize) + 1} 至{' '}
+ {Math.min(pagination.current * pagination.pageSize, pagination.total)} 项,
+ 共 {pagination.total} 项
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default TrainingPlan;
\ No newline at end of file
diff --git a/src/pages/staffmanage_trainingplan/TrainingPlan.less b/src/pages/staffmanage_trainingplan/TrainingPlan.less
new file mode 100644
index 0000000..7369b77
--- /dev/null
+++ b/src/pages/staffmanage_trainingplan/TrainingPlan.less
@@ -0,0 +1,455 @@
+@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);
+}
+
+// 信息卡片
+.infoCard {
+ margin-bottom: 24px;
+ border-radius: 8px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+
+ .ant-card-body {
+ padding: 24px;
+ }
+}
+
+.courseCard {
+ border-radius: 8px;
+ border: 1px solid #e8e8e8;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+
+ .ant-card-body {
+ padding: 24px;
+ }
+}
+
+// 培训计划头部
+.planHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 24px;
+
+ .planInfo {
+ flex: 1;
+
+ .planTitle {
+ margin: 0 0 8px 0 !important;
+ font-size: 24px !important;
+ font-weight: 700 !important;
+ color: #333 !important;
+ }
+
+ .planMeta {
+ .metaItem {
+ color: #666;
+ font-size: 14px;
+
+ .metaIcon {
+ margin-right: 4px;
+ color: #999;
+ }
+ }
+ }
+ }
+
+ .planActions {
+ display: flex;
+ gap: 12px;
+ }
+}
+
+// 按钮样式
+.editBtn {
+ background: linear-gradient(135deg, #2d5cf6 0%, #4c7bff 100%);
+ border: none;
+ box-shadow: 0 2px 8px rgba(45, 92, 246, 0.3);
+
+ &:hover {
+ background: linear-gradient(135deg, #4c7bff 0%, #6b8fff 100%);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(45, 92, 246, 0.4);
+ }
+}
+
+.shareBtn,
+.filterBtn,
+.exportBtn {
+ border-color: #d9d9d9;
+ transition: all 0.3s ease;
+
+ &:hover {
+ border-color: #2d5cf6;
+ color: #2d5cf6;
+ transform: translateY(-1px);
+ }
+}
+
+.addBtn {
+ background: linear-gradient(135deg, #2d5cf6 0%, #4c7bff 100%);
+ border: none;
+ box-shadow: 0 2px 8px rgba(45, 92, 246, 0.3);
+
+ &:hover {
+ background: linear-gradient(135deg, #4c7bff 0%, #6b8fff 100%);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(45, 92, 246, 0.4);
+ }
+}
+
+// 统计卡片
+.statsRow {
+ margin-bottom: 24px;
+
+ .statCard {
+ padding: 16px;
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ text-align: center;
+
+ .statLabel {
+ color: #666;
+ font-size: 12px;
+ margin-bottom: 4px;
+ }
+
+ .statValue {
+ font-weight: 600;
+ font-size: 16px;
+ color: #333;
+ }
+ }
+}
+
+// 描述区域
+.description {
+ .descTitle {
+ margin: 0 0 8px 0 !important;
+ font-size: 16px !important;
+ font-weight: 600 !important;
+ color: #333 !important;
+ }
+
+ .descContent {
+ margin: 0 !important;
+ color: #666;
+ font-size: 14px;
+ line-height: 1.6;
+ }
+}
+
+// 课程区域
+.courseHeader {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 24px;
+
+ .courseTitle {
+ margin: 0 !important;
+ font-size: 20px !important;
+ font-weight: 700 !important;
+ color: #333 !important;
+ }
+}
+
+// 表格样式
+.courseTable {
+ .table {
+ .ant-table-thead > tr > th {
+ background-color: #fafafa;
+ border-bottom: 1px solid #e8e8e8;
+ font-weight: 600;
+ color: #333;
+ font-size: 12px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ }
+
+ .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;
+ font-size: 14px;
+ color: #333;
+ padding: 12px 24px;
+ }
+ }
+ }
+}
+
+// 类型标签
+.typeTag {
+ border-radius: 12px;
+ font-size: 12px;
+ padding: 2px 8px;
+ border: none;
+}
+
+// 操作按钮
+.actionBtn {
+ color: #2d5cf6;
+
+ &:hover {
+ background: rgba(45, 92, 246, 0.1);
+ color: #2d5cf6;
+ }
+}
+
+.deleteBtn {
+ color: #ff4d4f;
+
+ &:hover {
+ background: rgba(255, 77, 79, 0.1);
+ color: #ff4d4f;
+ }
+}
+
+.enterBtn {
+ color: #2d5cf6;
+ padding: 0;
+ font-size: 14px;
+
+ &:hover {
+ text-decoration: underline;
+ }
+}
+
+// 分页容器
+.paginationContainer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: 24px;
+ padding-top: 20px;
+ 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;
+ }
+ }
+ }
+
+ .planHeader {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 16px;
+ }
+
+ .statsRow {
+ .statCard {
+ margin-bottom: 12px;
+ }
+ }
+
+ .courseHeader {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 16px;
+ }
+}
+
+// 页面特定样式
+.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;
+ }
+ }
+}
\ No newline at end of file