Browse Source

(+)员工沟通统计前端页面搭建+api模型

master
Archer_cdm 2 years ago
parent
commit
e75e8758dd
  1. 65
      api/app/lib/models/employee_communicate.js
  2. 2
      web/client/index.ejs
  3. 2
      web/client/index.html
  4. 14
      web/client/src/layout/components/header/contant.js
  5. 38
      web/client/src/sections/humanAffairs/containers/communication/detailModal.js
  6. 354
      web/client/src/sections/humanAffairs/containers/communication/employeeCommunication.jsx
  7. 4
      web/client/src/sections/humanAffairs/containers/index.js
  8. 2
      web/client/src/sections/humanAffairs/containers/personalTrainRecord.jsx
  9. 20
      web/client/src/sections/humanAffairs/nav-item.jsx
  10. 26
      web/client/src/sections/humanAffairs/routes.js

65
api/app/lib/models/employee_communicate.js

@ -0,0 +1,65 @@
/* eslint-disable*/
'use strict';
module.exports = dc => {
const DataTypes = dc.ORM;
const sequelize = dc.orm;
const EmployeeCommunicate = sequelize.define("employeeCommunicate", {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
field: "id",
autoIncrement: true,
},
personalName: {
type: DataTypes.STRING,
allowNull: false,
field: "personalname",
},
job: {
type: DataTypes.STRING,
allowNull: false,
field: "job",
},
departmentName: {
type: DataTypes.STRING,
allowNull: false,
field: "departmentname",
},
communicateDate: {
type: DataTypes.DATE,
allowNull: false,
field: "communicatedate",
},
communicateContent: {
type: DataTypes.STRING,
allowNull: false,
field: "communicatecontent",
},
communicateResult: {
type: DataTypes.STRING,
allowNull: false,
field: "communicateresult",
},
valuation: {
type: DataTypes.STRING,
allowNull: false,
field: "valuation",
},
communicateCondition: {
type: DataTypes.STRING,
allowNull: false,
field: "communicatecondition",
},
nextPlan: {
type: DataTypes.STRING,
allowNull: true,
field: "nextplan",
}
}, {
tableName: "employee_communicate",
});
dc.models.EmployeeCommunicate = EmployeeCommunicate;
return EmployeeCommunicate;
};

2
web/client/index.ejs

@ -9,7 +9,7 @@
<!-- <link rel="shortcut icon" href="/assets/images/favicon.ico"> -->
<script src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_20231_17.1b35f8b651eb2d07e0f77dc62cd2e4e4.es5.js"></script>
<script src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_20231_18.354a37219f3fd496296a61830b1acb13.es5.js"></script>
</head>

2
web/client/index.html

@ -7,7 +7,7 @@
<!-- <link rel="shortcut icon" href="/assets/images/favicon.ico"> -->
<script src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_20231_17.1b35f8b651eb2d07e0f77dc62cd2e4e4.es5.js"></script>
<script src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_20231_18.354a37219f3fd496296a61830b1acb13.es5.js"></script>
</head>
<body>

14
web/client/src/layout/components/header/contant.js

@ -79,14 +79,12 @@ const headerItems = [{
itemKey: "leaveManagement",
text: "假勤管理",
to: "/humanAffairs/employeeRelationship/leaveManagement/attendanceStatistics"
},
// {
// fatherKey: "employeeRelationship",
// itemKey: "communication",
// text: "员工沟通",
// to: "/humanAffairs/employeeRelationship/communication/employeeCommunication"
// }
]
}, {
fatherKey: "employeeRelationship",
itemKey: "communication",
text: "员工沟通",
to: "/humanAffairs/employeeRelationship/communication/employeeCommunication"
}]
}, {
itemKey: "archivesCenter",
text: "档案中心",

38
web/client/src/sections/humanAffairs/containers/communication/detailModal.js

@ -0,0 +1,38 @@
import React, { useEffect, useRef, useState } from 'react';
import { connect } from "react-redux";
import { Select, Modal, Form, Button, Toast } from "@douyinfe/semi-ui";
const DetailModal = (props) => {
const { dispatch, actions, user, onCancel, dataToDetail, close } = props;
const { humanAffairs } = actions;
const [options, setOptions] = useState([]);
//初始化
useEffect(() => {
}, []);
return (
<Modal title='员工沟通详情'
visible={true}
destroyOnClose
onCancel={onCancel}
footer={
<Button type="primary" onClick={onCancel}>
关闭
</Button>
}
>
</Modal >
)
}
function mapStateToProps(state) {
const { auth, global } = state;
return {
user: auth.user,
actions: global.actions,
apiRoot: global.apiRoot
};
}
export default connect(mapStateToProps)(DetailModal);

354
web/client/src/sections/humanAffairs/containers/communication/employeeCommunication.jsx

@ -1,24 +1,22 @@
import React, { useEffect, useRef, useState, useMemo } from 'react';
import { connect } from 'react-redux';
import moment from 'moment'
import { Select, Input, Button, Popconfirm, Radio, Tooltip, Table, Pagination, Skeleton } from '@douyinfe/semi-ui';
// import SalesMemberModal from './salesMemberModal'
// import ImportSalersModal from './importSalersModal'
import { Select, Input, Button, Tooltip, Table, Pagination, Skeleton, DatePicker } from '@douyinfe/semi-ui';
import { IconSearch } from '@douyinfe/semi-icons';
import DetailModal from './detailModal';
import { SkeletonScreen } from "$components";
import '../../style.less'
const EmployeeCommunication = (props) => {
const { dispatch, actions } = props
const { humanAffairs } = actions;
const [keywordTarget, setKeywordTarget] = useState('dep');
const [keywordTarget, setKeywordTarget] = useState('person');
const [keyword, setKeyword] = useState('');//
const [limits, setLimits] = useState()//
const [query, setQuery] = useState({ limit: 10, page: 0 }); //
// const [modalV, setModalV] = useState(false);
// const [dataToEdit, setDataToEdit] = useState(null);
const [tableData, setTableData] = useState([]);
// const [importModalV, setImportModalV] = useState(false);
const [modalV, setModalV] = useState(false);
const [dataToDetail, setDataToDetail] = useState(null);
const [tableData, setTableData] = useState([{ id: 1, personalName: '危大伟', job: '软件前端开发工程师', departmentName: '软件开发部,软件开发部,软件开发部,软件开发部,软件开发部', communicateDate: '2022-12-29 14:21:33', communicateContent: '阿巴阿巴阿巴阿巴阿巴阿巴阿巴阿巴阿巴阿巴阿巴阿巴阿巴', communicateResult: '啊啊啊啊啊啊啊啊啊啊是大大说所所所', valuation: '123当前时区无无群', communicateCondition: '驱蚊器翁群的法人', nextPlan: '呜呜呜呜呜呜呜呜呜呜呜我' }]);
const page = useRef(query.page);
function seachValueChange(value) {
setKeyword(value)
@ -57,143 +55,147 @@ const EmployeeCommunication = (props) => {
}
}
// const closeAndFetch = () => {
// setModalV(false)
// getMemberSearchList();
// }
// const starHeader = (header) => {
// return <div>
// <img src="/assets/images/hrImg/V.png" style={{ width: 14, height: 14 }} /> {header}
// </div>
// }
// const getMultis = (arrStr) => {//2
// return <div style={{ display: 'flex' }}>
// {
// arrStr.length ?
// arrStr.map((ite, idx) => {
// return (
// <div key={idx} style={{ display: 'flex' }}>
// {idx < 2 ?
// <div style={{ padding: '0px 4px 1px 4px', color: '#FFF', fontSize: 12, background: 'rgba(0,90,189,0.8)', borderRadius: 2, marginRight: 4 }}>
// {ite}
// </div> : ''
// }
// {
// arrStr.length > 2 && idx == 2 ?
// <Tooltip content={arrStr.join(',')} trigger="click" style={{ lineHeight: 2 }}>
// <div style={{ padding: '0px 4px 1px 4px ', color: 'rgba(0,90,189,0.8)', fontSize: 12, marginRight: 4, cursor: "pointer" }}>
// +{arrStr.length - 2}
// </div>
// </Tooltip>
// : ''
// }
// </div>
// )
// }) : '-'
// }
// </div>
// }
const columns = [{
title: '序号',
dataIndex: 'id',
key: 'id',
width: 60,
render: (text, record, index) => index + 1
}, {
title: '被沟通人',
dataIndex: 'personalName',
key: 'personalName',
width: 100
}, {
title: '岗位',
dataIndex: 'job',
key: 'job',
width: 100,
}, {
title: '部门',
dataIndex: 'departmentName',
key: 'departmentName',
width: 120,
render: (t, r) => {
return (<Tooltip content={r.departmentName} style={{ lineHeight: 2 }}>
<div style={{
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
width: 120
}}>{r.departmentName}</div>
</Tooltip>)
}
}, {
title: '沟通时间',
dataIndex: 'communicateDate',
key: 'communicateDate',
width: 130,
}, {
title: '沟通内容',
dataIndex: 'communicateContent',
key: 'communicateContent',
width: 140,
render: (t, r) => {
return (<Tooltip content={t}>
<div style={{
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
width: 120
}}>
{t}
</div>
</Tooltip>)
}
}, {
title: '沟通成果',
dataIndex: 'communicateResult',
key: 'communicateResult',
width: 120,
render: (t, r) => {
return (<Tooltip content={t}>
<div style={{
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
width: 120
}}>
{t}
</div>
</Tooltip>)
}
}, {
title: '对被沟通人近期表现的评价',
dataIndex: 'valuation',
key: 'valuation',
width: 120,
render: (t, r) => {
return (<Tooltip content={t}>
<div style={{
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
width: 120
}}>
{t}
</div>
</Tooltip>)
}
}, {
title: '沟通情况反馈',
dataIndex: 'communicateCondition',
key: 'communicateCondition',
width: 120,
render: (t, r) => {
return (<Tooltip content={t}>
<div style={{
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
width: 120
}}>
{t}
</div>
</Tooltip>)
}
}, {
title: '下一步工作计划或提升方向',
dataIndex: 'nextPlan',
key: 'nextPlan',
width: 120,
render: (t, r) => {
return (<Tooltip content={t}>
<div style={{
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
width: 120
}}>
{t}
</div>
</Tooltip>)
}
}, {
title: '操作',
dataIndex: 'action',
width: 120,
render: (text, record) => {
return <div>
<span style={{ color: '#1890FF', cursor: 'pointer' }} onClick={() => onDetail(record)}>查看详情</span>
</div>
}
}];
// const columns = [{
// title: '',
// dataIndex: 'id',
// key: 'id',
// width: 60,
// render: (text, record, index) => index + 1
// }, {
// title: starHeader(''),
// dataIndex: 'name',
// key: 'name',
// width: 80
// },
// {
// title: starHeader(''),
// dataIndex: 'department',
// key: 'department',
// width: 200,
// render: (text, r, index) => {
// let arrStr = text.map(t => t.name);
// return getMultis(arrStr);
// }
// }, {
// title: '(/)',
// dataIndex: 'provinces',
// key: 'provinces',
// width: 160,
// render: (text, record, index) => {
// return getMultis(text?.split('') || []);
// }
// }, {
// title: '()',
// dataIndex: 'cities',
// key: 'cities',
// width: 160,
// render: (text, record, index) => {
// return text ? getMultis(text?.split('') || []) : '-';
// }
// }, {
// title: '线',
// dataIndex: 'businessLines',
// key: 'businessLines',
// width: 140,
// render: (text, record, index) => {
// return text ? getMultis(text?.split('') || []) : '-';
// }
// }, {
// title: starHeader(''),
// dataIndex: 'post',
// key: 'post',
// width: 120,
// render: (text, record) => <span>{text || '-'}</span>
// }, {
// title: starHeader(''),
// dataIndex: 'hireDate',
// key: 'hireDate',
// width: 120,
// render: (text, record) => <span>{text || '-'}</span>
// }, {
// title: starHeader(''),
// dataIndex: 'regularDate',
// key: 'regularDate',
// width: 120,
// render: (text, record) => <span>{text || '-'}</span>
// }, {
// title: starHeader(''),
// dataIndex: 'workYears',
// key: 'workYears',
// width: 120,
// render: (_, r, index) => {
// return (r.hireDate ? <span style={{ color: '#1890FF' }}>{String(moment(new Date()).diff(r.hireDate, 'years', true)).substring(0, 3) + ''}</span> : '-')
// },
// }, {
// title: '',
// dataIndex: 'action',
// width: 120,
// render: (text, record) => {
// return <div>
// <span style={{ color: '#1890FF', cursor: 'pointer' }} onClick={() => onEdit(record)}></span>&nbsp;&nbsp;
// <Popconfirm
// title='' content="" position='topLeft'
// onConfirm={() => confirmDelete(record.pepUserId)} style={{ width: 330 }}
// > <span style={{ color: '#1890FF', cursor: 'pointer' }}></span></Popconfirm>
// </div>
// }
// }];
const onDetail = (data) => {
setModalV(true);
setDataToDetail(data);
}
// const onEdit = (data) => {
// setModalV(true);
// setDataToEdit(data);
// }
const closeAndFetch = () => {
setModalV(false)
}
// const confirmDelete = (pepUserId) => {
// dispatch(humanAffairs.delSalesMember({ pepUserId, msg: '' })).then(res => {
// if (res.success) {
// getMemberSearchList();
// }
// });
// }
const scroll = useMemo(() => ({}), []);
return (<div style={{ padding: '0px 12px' }}>
<div style={{ display: 'flex' }}>
@ -211,20 +213,14 @@ const EmployeeCommunication = (props) => {
<div style={{ marginLeft: 6, fontSize: 12, color: '#969799', fontFamily: "DINExp", }}>EMPLOYEE COMMUNICATION</div>
</div>
</div>
{/* <div style={{ margin: '18px 0px' }}>
<div style={{ margin: '18px 0px' }}>
<div style={{ display: 'flex', margin: '16px 0px', justifyContent: 'space-between' }}>
<div style={{ display: 'flex' }}>
<div style={{ padding: '6px 20px', background: '#0F7EFB', color: '#FFFFFF', fontSize: 14, cursor: "pointer", marginRight: 18 }}
onClick={() => {
setModalV(true);
setDataToEdit(null);
}}>
新增
</div>
<div>
<Select value={keywordTarget} onChange={setKeywordTarget} style={{ width: 100 }} >
<Select.Option value='dep'>部门</Select.Option>
<Select.Option value='place'>地区</Select.Option>
<Select value={keywordTarget} onChange={setKeywordTarget} style={{ width: 120 }} >
<Select.Option value='person'>被沟通人</Select.Option>
<Select.Option value='post'>岗位</Select.Option>
<Select.Option value='dept'>部门</Select.Option>
</Select>
</div>
<div style={{ margin: '0px 18px' }}>
@ -236,25 +232,23 @@ const EmployeeCommunication = (props) => {
onChange={seachValueChange}>
</Input>
</div>
<div style={{ marginRight: '18px' }}>
<span style={{ verticalAlign: 'sub', color: 'rgba(0, 0, 0, 0.65)', fontSize: '14px', fontWeight: 'bold' }}>沟通时间</span>
<DatePicker
initValue={[moment(new Date()).add(-1, 'y').format('YYYY-MM-DD'), moment(new Date()).format('YYYY-MM-DD')]}
field='entryTime' type="dateRange" density="compact" showClear style={{ width: 370, color: "#F9F9F9" }} />
</div>
<Button theme='solid' type='primary' style={{ width: 80, borderRadius: 2, height: 32, background: '#DBECFF', color: '#005ABD' }}
onClick={() => {
setQuery({ limit: 10, page: 0 })
}}>查询</Button>
</div>
<div style={{ display: 'flex', marginRight: 20 }}>
<div style={{ padding: '6px 20px', background: '#00BA85', color: '#FFFFFF', fontSize: 14, marginLeft: 18 }}>
<div style={{ padding: '6px 20px', background: '#0F7EFB', color: '#FFFFFF', fontSize: 14, marginLeft: 18, cursor: "pointer" }}>
导出
</div>
</div>
</div>
<div style={{ border: '1px solid #C7E1FF', background: '#F4F8FF', borderRadius: 2, height: 32, width: 669, padding: '8px 0px 7px 12px', display: 'flex', alignItems: 'center', color: '#0F7EFB', fontSize: 12 }}>
<img src="/assets/images/hrImg/!.png" alt="" style={{ width: 14, height: 14, marginRight: 8 }} />
表格中带有认证标识"
<img src="/assets/images/hrImg/V.png" alt="" style={{ width: 14, height: 14 }} />
"信息的为系统基础数据来源于项企PEP钉钉等系统其他数据均为导入或自定义数据
</div>
<div style={{ marginTop: 20 }}>
<Skeleton
// loading={loading}
@ -268,27 +262,6 @@ const EmployeeCommunication = (props) => {
bordered={false}
empty="暂无数据"
pagination={false}
onChange={({ sorter }) => {
if (sorter.key == 'userCode') {
if (sorter.sortOrder == 'descend') {
setOrder({ orderBy: 'code', orderDirection: 'DESC' })
} else {
setOrder({ orderBy: 'code', orderDirection: 'ASC' })
}
} else if (sorter.key == 'age') {
if (sorter.sortOrder == 'descend') {
setOrder({ orderBy: 'age', orderDirection: 'DESC' })
} else {
setOrder({ orderBy: 'age', orderDirection: 'ASC' })
}
} else {
if (sorter.sortOrder == 'descend') {
setOrder({ orderBy: 'hiredate', orderDirection: 'DESC' })
} else {
setOrder({ orderBy: 'hiredate', orderDirection: 'ASC' })
}
}
}}
onRow={handleRow}
scroll={scroll}
/>
@ -316,21 +289,14 @@ const EmployeeCommunication = (props) => {
</div>
</div>
</div>
</div> */}
</div>
{/* {
modalV ? <SalesMemberModal
dataToEdit={dataToEdit} getMultis={getMultis}
</div>
{
modalV ? <DetailModal
dataToDetail={dataToDetail}
close={() => closeAndFetch()}
onCancel={() => setModalV(false)} /> : ''
}
{
importModalV ? <ImportSalersModal
onCancel={() => {
setImportModalV(false);
getMemberSearchList();
}} /> : ''
} */}
</div >)
}

4
web/client/src/sections/humanAffairs/containers/index.js

@ -7,7 +7,7 @@ import DeptArchives from './deptArchives';
import AttendanceStatistics from './attendanceStatistics';
import LeaveStatistics from './leaveStatistics';
import OvertimeStatistics from './overtimeStatistics';
// import EmployeeCommunication from './communication/employeeCommunication';
import EmployeeCommunication from './communication/employeeCommunication';
//招聘
import AppointmentRecords from './appointmentRecords';
import PersonnelDistribution from './salersDistribution/personnelDistribution';
@ -44,5 +44,5 @@ export {
EmployeeAuth, FormMaintenance,
PersonnelFilesDetail,
PersonalTrainRecord,
// EmployeeCommunication
EmployeeCommunication
};

2
web/client/src/sections/humanAffairs/containers/personalTrainRecord.jsx

@ -120,7 +120,7 @@ const PersonalTrainRecord = (props) => {
</div>
<div style={{ margin: '18px 0px' }}>
<div style={{ display: 'flex', margin: '16px 0px', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', margin: '16px 0px', justifyContent: 'flex-end' }}>
<div style={{ display: 'flex', marginRight: 20 }}>
<div style={{ padding: '6px 20px', background: '#0F7EFB', color: '#FFFFFF', fontSize: 14, cursor: "pointer" }}
onClick={() => { setImportModalV(true); }}

20
web/client/src/sections/humanAffairs/nav-item.jsx

@ -38,17 +38,15 @@ export function getNavItem(user, dispatch) {
}, {
itemKey: 'overtimeStatistics', to: '/humanAffairs/employeeRelationship/leaveManagement/overtimeStatistics', text: '加班统计'
}]
},
// {
// itemKey: 'communication',
// text: '',
// icon: <iconpark-icon style={{ width: 20, height: 20 }} name="iconcbbumen"></iconpark-icon>,
// to: '/humanAffairs/employeeRelationship/communication/employeeCommunication',
// items: [{
// itemKey: 'employeeCommunication', to: '/humanAffairs/employeeRelationship/communication/employeeCommunication', text: ''
// }]
// },
{
}, {
itemKey: 'communication',
text: '员工沟通',
icon: <iconpark-icon style={{ width: 20, height: 20 }} name="iconyuangonggoutong"></iconpark-icon>,
to: '/humanAffairs/employeeRelationship/communication/employeeCommunication',
items: [{
itemKey: 'employeeCommunication', to: '/humanAffairs/employeeRelationship/communication/employeeCommunication', text: '员工沟通统计'
}]
}, {
itemKey: 'recruitRecord',
text: '招聘记录',
icon: <iconpark-icon style={{ width: 20, height: 20 }} name="iconcbzhaopin"></iconpark-icon>,

26
web/client/src/sections/humanAffairs/routes.js

@ -11,7 +11,7 @@ import {
PersonnelFilesDetail,
PersonalTrainRecord,
DepartmentTrainRecord,
// EmployeeCommunication
EmployeeCommunication
} from './containers';
export default [{
@ -77,19 +77,17 @@ export default [{
component: OvertimeStatistics,
breadcrumb: '加班统计',
}]
},
// {
// path: '/communication',
// key: 'communication',
// breadcrumb: '员工沟通',
// childRoutes: [{
// path: '/employeeCommunication',
// key: 'employeeCommunication',
// component: EmployeeCommunication,
// breadcrumb: '员工沟通统计',
// }]
// }
]
}, {
path: '/communication',
key: 'communication',
breadcrumb: '员工沟通',
childRoutes: [{
path: '/employeeCommunication',
key: 'employeeCommunication',
component: EmployeeCommunication,
breadcrumb: '员工沟通统计',
}]
}]
}, {
path: '/recruit',
key: 'recruit',

Loading…
Cancel
Save