This commit is contained in:
罗松柏 2022-11-03 09:47:31 +08:00
parent 64311d8e06
commit c2696e5b1c
106 changed files with 25351 additions and 2033 deletions

13260
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,15 +7,22 @@
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"antd": "^4.20.7",
"axios": "^0.27.2",
"babel-plugin-import": "^1.13.5",
"customize-cra": "^1.0.0",
"echarts": "^5.3.3",
"echarts-for-react": "^3.0.2",
"less": "^4.1.2",
"less-loader": "^11.0.0",
"pubsub-js": "^1.9.4",
"react": "^18.1.0",
"react-app-rewired": "^2.2.1",
"react-dom": "^18.1.0",
"react-pdf": "^5.7.2",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"serve": "^14.0.1",
"store": "^2.0.12",
"web-vitals": "^2.1.0"
},
"scripts": {

BIN
public/images/1.pdf Normal file

Binary file not shown.

BIN
public/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -9,6 +9,7 @@
name="description"
content="Web site created using create-react-app"
/>
<title>乐谷人事管理系统</title>
</head>
<body>
@ -16,4 +17,5 @@
<div id="root"></div>
</body>
<script src="http://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
</html>

View File

@ -12,26 +12,44 @@
.trigger:hover {
color: #1890ff;
}
.appcontent { min-height: 280px; overflow-y: auto;}
.divdisplay { display: flex; }
.appcontent { min-height: 280px; overflow-y: auto; position: relative;}
.divdisplay { display: flex; align-items: center; }
.divdisplay2 { display: flex; }
.logo_box {
display: flex;
align-items: center;
margin: 16px;
}
.logo_box .logo {
height: 40px;
width: 40px;
height: 48px;
width: 48px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
}
.logo img { width: 100%; border-radius: 50%; }
.logo_box .logo_txt {
height: 40px;
height: 48px;
display: none;
}
.logo_box .logo_txt2 {
height: 48px;
animation:txttran 1s 1;
}
@keyframes txttran {
from {opacity: 0; display: block;}
to {opacity: 1;}
}
.logo_box .logo_txt p {
margin-bottom: 0;
margin-left: 10px;
}
.logo_box .logo_txt2 p {
margin-bottom: 0;
margin-left: 10px;
}
.logo_box .introduction {
font-size: 12px;
color: #c1c5cc;
@ -94,19 +112,36 @@
background:transparent;
}
.backgroundcolor .ant-tree{ background-color: rgba(255, 255, 255, 0) !important; }
.fontweight{font-weight: bold;}
.padding24 { padding: 24px; }
.margin24 { margin: 24px; }
.margintop5{ margin-top: 5px; }
.margintop20{ margin-top: 20px; }
.margintop30 { margin-top: 30px; }
.margintop50 { margin-top: 50px; }
.margin10 {margin: 0 10px;}
.marginbottom10 { margin-bottom: 10px; }
.marginleft6{ margin-left: 6px; }
.marginright5 { margin-right: 5px;}
.marginright6{ margin-right: 6px; }
.marginright10{ margin-right: 10px; }
.marginright24{ margin-right: 24px; }
.cursor { cursor: pointer; }
.font-size12 { font-size: 12px; margin-bottom: 0;}
.font-size14 {font-size: 14px;}
.fontsize28 { font-size: 28px; }
.fontsize24 { font-size: 24px; }
.fontsize22 { font-size: 22px; }
.lineheight24{line-height: 24px;}
.color0 { color: #575d6a; }
.color1 { color: #75777d; }
.color2 { color: #5c5f6c; }
@ -115,6 +150,19 @@
.coloc5 { color: #312b2e; }
.color6 { color: #f47f76; }
.color7 { color: #5b524b; }
.color8 { color: #f59f00; }
.border3 { border-color: #f49300 !important; }
.border6 { border-color: #f47f76 !important; }
.textalign { text-align: right; }
.textaligncenter { text-align: center; }
.transform90 { transform:rotate(90deg); }
.Empty-box2 {
margin: 30px 0;
}

View File

@ -1,7 +1,9 @@
import React from 'react'
import GlobalRouter from './routes/GlobalRouter'
import { pdfjs } from 'react-pdf';
import './App.css'
pdfjs.GlobalWorkerOptions.workerSrc = `https://cdn.bootcdn.net/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
// console.log(pdfjs.version)
// const App = () => {
// return <GlobalRouter/>
// }

View File

@ -1,8 +1,11 @@
import React, { useState, useEffect } from 'react'
import { Layout, Menu } from 'antd'
import { Layout, Menu, Image } from 'antd'
import { Outlet, useNavigate, useLocation } from 'react-router-dom'
import NavHeader from './components/NavHeader'
import HeadLeftTtile from './components/HeadLeftTtile'
import PubSub from 'pubsub-js'
import memoryUtils from './util/memoryUtils'
import storageUtils from './util/storageUtils'
import './App.css'
import {
AppstoreOutlined,
@ -12,17 +15,22 @@ import {
ShoppingOutlined,
PieChartOutlined,
SettingOutlined,
FilterOutlined,
} from '@ant-design/icons'
// import GlobalRouter from './routes/GlobalRouter'
const { Header, Sider, Content } = Layout
// localStorageuserInfo
function AppLayOut() {
const location = useLocation()
const navigate = useNavigate()
const [collapsed, setCollapsed] = useState(true)
const [headtitle, setHeadtitle] = useState('总览')
const [headnav,setHeadnav] = useState('')
const [defaultSelectedKeys,setdefaultSelectedKeys] = useState('/')
const [headnav, setHeadnav] = useState('')
const [defaultSelectedKeys, setdefaultSelectedKeys] = useState('/')
//
var userInfo = storageUtils.getUser()
memoryUtils.userInfo = userInfo
//
const click = (e) => {
@ -31,8 +39,7 @@ function AppLayOut() {
const select = (e) => {
const { title } = e.item.props
// console.log(title)
setHeadtitle(title)
PubSub.publish('headtitle', { headtitle: title })
}
//
@ -42,47 +49,21 @@ function AppLayOut() {
//
const handlMouseLeave = () => {
console.log(11111)
setCollapsed(true)
}
useEffect(() => {
// return ()=>{
const pathname = location.pathname;
const pathurl = pathname.split('/')[2];
const pathname = location.pathname
const pathurl = pathname.split('/')[2]
setHeadnav(pathurl)
setdefaultSelectedKeys(pathname)
setdefaultSelectedKeys(pathurl ? '/admin/' + pathurl : pathname)
// }
// const routes = matchRoutes(GlobalRouter, location.pathname);
// console.log(routes);
}, [location.pathname])
return (
<Layout>
<Sider
trigger={null}
collapsible
collapsed={collapsed}
onMouseLeave={handlMouseLeave}
onMouseEnter={handlMouseEnter}
>
{/* <div className="logo" /> */}
<div className="logo_box">
<div className="logo" />
<div
className="logo_txt"
style={{ display: collapsed ? 'none' : 'block' }}
>
<p className="introduction">Moka</p>
<p className="role">用人经理端</p>
</div>
</div>
<Menu
theme="dark"
mode="inline"
selectedKeys={defaultSelectedKeys}
defaultSelectedKeys={defaultSelectedKeys}
items={[
const hr_items = [
{
key: '/',
icon: <AppstoreOutlined />,
@ -125,7 +106,118 @@ function AppLayOut() {
label: '设置',
title: '设置',
},
]}
]
const manager_items = [
{
key: '/',
icon: <AppstoreOutlined />,
label: '总览',
title: '总览',
},
{
key: '/admin/Interview_manager',
icon: <CalendarOutlined />,
label: '经理-面试安排',
title: '经理-面试安排',
},
{
key: '/admin/resumescreen',
icon: <FilterOutlined />,
label: '简历筛选',
title: '简历筛选',
},
{
key: '/admin/setup',
icon: <SettingOutlined />,
label: '设置',
title: '设置',
},
]
const root_items = [
{
key: '/',
icon: <AppstoreOutlined />,
label: '总览',
title: '总览',
},
{
key: '/admin/resumescreen',
icon: <FilterOutlined />,
label: '简历筛选',
title: '简历筛选',
},
{
key: '/admin/Interview_manager',
icon: <CalendarOutlined />,
label: '经理-面试安排',
title: '经理-面试安排',
},
{
key: '/admin/candidate',
icon: <UsergroupAddOutlined />,
label: '候选人管理',
title: '候选人管理',
},
{
key: '/admin/interview',
icon: <CalendarOutlined />,
label: '面试安排',
title: '面试安排',
},
{
key: '/admin/position',
icon: <ShoppingOutlined />,
label: '职位管理',
title: '职位管理',
},
{
key: '/admin/talentpool',
icon: <RestOutlined />,
label: '人才库',
title: '人才库',
},
{
key: '/admin/reportforms',
icon: <PieChartOutlined />,
label: '报表中心',
title: '报表中心',
},
{
key: '/admin/setup',
icon: <SettingOutlined />,
label: '设置',
title: '设置',
},
]
return (
<Layout>
<Sider
trigger={null}
collapsible
collapsed={collapsed}
onMouseLeave={handlMouseLeave}
onMouseEnter={handlMouseEnter}
>
{/* <div className="logo" /> */}
<div className="logo_box">
<div className="logo">
<img src='https://legu-cdn-source.obs.cn-east-2.myhuaweicloud.com/hrms/216.png'/>
</div>
<div className={collapsed ? 'logo_txt' : 'logo_txt2'}>
<p className="introduction">Legu</p>
<p className="role">{userInfo.rank === 1? 'HR端':'经理端'}</p>
</div>
</div>
<Menu
theme="dark"
mode="inline"
selectedKeys={defaultSelectedKeys}
defaultSelectedKeys={defaultSelectedKeys}
items={userInfo.rank === 1 ? hr_items : userInfo.rank === 2? manager_items: root_items}
onClick={click}
onSelect={select}
/>
@ -137,12 +229,10 @@ function AppLayOut() {
padding: 0,
}}
>
<HeadLeftTtile headtitle={headtitle} />
<NavHeader headnav={headnav}/>
<HeadLeftTtile />
<NavHeader userInfo={userInfo} />
</Header>
<Content
className='appcontent'
>
<Content className="appcontent">
<Outlet />
</Content>
</Layout>

View File

@ -0,0 +1,68 @@
import React from 'react'
import { Form, Modal, Select } from 'antd'
const { Option } = Select
export default function Addperson({ visible, onCancel, onCreate, data }) {
const [form] = Form.useForm()
let title = '招聘协助人'
switch (data.type) {
case 'principal':
title = '负责人'
break
case 'patronn':
title = '协助人'
break
case 'hr_name':
title = '用人经理'
break
default:
title = '面试官'
}
return (
<div>
<Modal
visible={visible}
title={'添加' + title}
okText="确认"
cancelText="取消"
onCancel={onCancel}
onOk={() => {
form
.validateFields()
.then((values) => {
form.resetFields();
onCreate(data.list[values['userinfo']]);
})
.catch((info) => {
console.log('Validate Failed:', info);
});
}}
>
<Form form={form} layout="vertical" name="form_in_modal">
<Form.Item
name="userinfo"
label={'选择' + title}
rules={[
{
required: true,
message:
'请选择',
},
]}
>
<Select>
{data['list'].map((item,key) => {
return item.nickname != 0 ?(
<Option value={key} key={key}>
{item.nickname}
</Option>
):''
})}
</Select>
</Form.Item>
</Form>
</Modal>
</div>
)
}

View File

@ -0,0 +1,76 @@
.PositionResponsble-box {
margin: 0 120px;
min-height: 300px;
display: flex;
justify-content: space-between;
}
.PositionResponsble-list {
width: calc(100% - 308px);
}
.PositionResponsble-type-box {
width: 276px;
height: 170px;
background-color: #fff;
}
.PositionResponsble-list-box {
background-color: #fff;
padding: 24px;
border-bottom: 1px solid #dfe1e5;
}
.PositionResponsble-list-box .title{
font-size: 16px;
color: #575d6a;
font-weight: bold;
}
.PositionResponsble-list-box .tips-box { font-size: 14px; color: #89909e; margin-top: 14px; }
.user-list-box {
display: flex;
align-items: center;
border-radius: 4px;
overflow: hidden;
cursor: pointer;
margin-right: 14px;
}
.PositionResponsble-user-list-box {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.user-list-box .surname-box {
padding: 5px 8px;
background-color: #f06595;
font-size: 14px;
color: #fff;
}
.user-list-box .name-box {
padding: 4px 8px;
background-color: #f4f4f5;
color: #292c32;
font-size: 14px;
border-radius: 0 0px 0px 0;
border-right: 1px solid #b6b6b6;
}
.user-list-box .delete-box {
padding: 4px 8px;
background-color: #f4f4f5;
color: #292c32;
font-size: 14px;
border-radius: 0 4px 4px 0;
color: rgb(230, 47, 47);
}
.PositionResponsble-user-list-box button { margin-left: 0px; border: 0; background-color: #f4f4f5; border-radius: 4px; padding: 0 10px;}
.PositionResponsble-type-box .title{
font-size: 15px;
}
.PositionResponsble-type-box .type-box {
display: flex;
justify-content: space-between;
align-items: center;
}
.PositionResponsble-type-box .type-box label { width: 160px; }

View File

@ -0,0 +1,272 @@
import React, { useState, useEffect } from 'react'
import { Button, Switch, message } from 'antd'
import Addperson from '../Addperson'
import {
EditFilled,
PlusOutlined,
InfoCircleOutlined,
DeleteFilled,
} from '@ant-design/icons'
import './index.css'
import { getname } from '../../../util/requestURL'
export default function PositionResponsble({ updataPosition, postdata }) {
const [visible, setVisible] = useState(false)
const [userlist, setUserlist] = useState({ type: 0, list: [] })
const [personlist, setPersonlist] = useState({
principal: postdata['principal'] || [],
patronn: postdata['patronn'] || [],
hr_name: postdata['hr_name'] || [],
interview: postdata['interview'] || [],
})
// let gatuserlist = []
// useEffect(() => {
// console.log(11111)
// getname().then(
// (res) => {
// gatuserlist = res.data
// },
// (error) => {
// message.error('')
// }
// )
// }, [visible])
const onCreate = (values) => {
const newPersonlist = JSON.parse(JSON.stringify(personlist))
if (userlist['type'] !== 'principal') {
newPersonlist[userlist['type']].push(values)
} else {
newPersonlist['principal'][0] = values
}
setVisible(false)
setPersonlist((values) => {
return newPersonlist
})
updataPosition(newPersonlist)
}
const handelAddUser = (type) => {
//type 1: 2: 3 4
// console.log(type)
getname().then(
(res) => {
const gatuserlist = res.data
const data = {
type: type,
list: [],
}
if (type === 'hr_name') {
for (let i in gatuserlist) {
if (gatuserlist[i]['rank'] == 2) {
data['list'].push(gatuserlist[i])
}
}
} else if (type === 'patronn') {
for (let i in gatuserlist) {
if (gatuserlist[i]['rank'] == 1) {
data['list'].push(gatuserlist[i])
}
}
} else {
data['list'] = gatuserlist
}
console.log(data)
setUserlist(data)
setVisible(true)
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
}
const handeldelete = (type, index) => {
const newpersonlist = personlist[type].filter((item, key) => {
return index !== key
})
setPersonlist({ ...personlist, [type]: newpersonlist })
updataPosition({ [type]: newpersonlist })
}
return (
<div className="PositionResponsble-box">
<Addperson
visible={visible}
onCreate={onCreate}
data={userlist}
onCancel={() => {
setVisible(false)
}}
/>
<div className="PositionResponsble-list">
<div className="PositionResponsble-list-box">
<div className="title">招聘负责人</div>
<p className="tips-box">
负责该职位的招聘拥有对该职位的操作权限
</p>
<div className="PositionResponsble-user-list-box">
{personlist['principal'].map((item, key) => {
return (
<div className="user-list-box" key={key}>
<div className="surname-box">
{item.nickname.charAt(0)}
</div>
<div className="name-box">{item.nickname}</div>
<div
className="delete-box"
onClick={() => {
handeldelete('principal', key)
}}
>
<DeleteFilled />
</div>
</div>
)
})}
<Button
size={'default'}
onClick={() => {
handelAddUser('principal')
}}
>
<EditFilled />
</Button>
</div>
</div>
<div className="PositionResponsble-list-box margintop20">
<div className="title">招聘协助人</div>
<p className="tips-box">
协助参与该职位的招聘拥有对该职位的权限操作
</p>
<div className="PositionResponsble-user-list-box">
{personlist['patronn'].map((item, key) => {
return (
<div className="user-list-box" key={key}>
<div className="surname-box">
{item.nickname.charAt(0)}
</div>
<div className="name-box">{item.nickname}</div>
<div
className="delete-box"
onClick={() => {
handeldelete('patronn', key)
}}
>
<DeleteFilled />
</div>
</div>
)
})}
<Button
size={'default'}
onClick={() => {
handelAddUser('patronn')
}}
>
<PlusOutlined />
</Button>
</div>
</div>
<div className="PositionResponsble-list-box margintop20">
<div className="title">用人经理</div>
<p className="tips-box">
推荐简历给用人部门时优先找到该用户
</p>
<div className="PositionResponsble-user-list-box">
{personlist['hr_name'].map((item, key) => {
return (
<div className="user-list-box" key={key}>
<div className="surname-box">
{item.nickname.charAt(0)}
</div>
<div className="name-box">{item.nickname}</div>
<div
className="delete-box"
onClick={() => {
handeldelete('hr_name', key)
}}
>
<DeleteFilled />
</div>
</div>
)
})}
<Button
size={'default'}
onClick={() => {
handelAddUser('hr_name')
}}
>
<PlusOutlined />
</Button>
</div>
</div>
<div className="PositionResponsble-list-box margintop20">
<div className="title">面试官</div>
<p className="tips-box">
添加面试时优先找到该用户作为面试官
</p>
<div className="PositionResponsble-user-list-box">
{personlist['interview'].map((item, key) => {
return (
<div className="user-list-box" key={key}>
<div className="surname-box">
{item.nickname.charAt(0)}
</div>
<div className="name-box">{item.nickname}</div>
<div
className="delete-box"
onClick={() => {
handeldelete('interview', key)
}}
>
<DeleteFilled />
</div>
</div>
)
})}
<Button
size={'default'}
onClick={() => {
handelAddUser('interview')
}}
>
<PlusOutlined />
</Button>
</div>
</div>
{/* <div className="PositionResponsble-list-box margintop20 textalign">
<Button type="primary" >保存</Button>
</div> */}
</div>
<div className="PositionResponsble-type-box">
<div className="margin24">
<p className="title fontweight">高级用人经理安排面试</p>
<div className="type-box">
<label>
允许安排面试 <InfoCircleOutlined />
</label>{' '}
<Switch defaultChecked />
</div>
<div className="type-box margintop20">
<label>
允许给自己面试过的候选人安排面试{' '}
<InfoCircleOutlined />
</label>{' '}
<Switch defaultChecked />
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,11 @@
.PositionSetup-box {
margin: 0 120px;
min-height: 300px;
background-color: #fff;
}
.PositionSetup-box .title {
font-weight: bold;
font-size: 20px;
color: #575d6a;
margin-bottom: 24px;
}

View File

@ -0,0 +1,129 @@
import React, {useState} from 'react'
import './index.css'
import {
Button,
Form,
Input,
Radio,
Select,
Row,
Col,
InputNumber,
Divider,
DatePicker,
} from 'antd'
import moment from 'moment';
import 'moment/locale/zh-cn'
import locale from 'antd/es/date-picker/locale/zh_CN'
const { Option } = Select
export default function PositionSetup({updataPosition, postdata}) {
const [form] = Form.useForm()
// form.setFieldsValue(postdata)
console.log(postdata)
return (
<div className="PositionSetup-box">
<div className="padding24">
<div className="title">职位设置</div>
<Form >
<Form.Item
label="招聘状态"
name="state"
initialValue={postdata['state']}
rules={[
{
required: true,
},
]}
>
<Radio.Group onChange={(e)=>{
console.log(e.target.value)
updataPosition({state: e.target.value})
}}>
<Radio.Button value={true}>正在招聘</Radio.Button>
<Radio.Button value={false}>结束招聘</Radio.Button>
</Radio.Group>
</Form.Item>
<Divider />
{/* <Form.Item
label="招聘流程"
name=""
rules={[
{
required: true,
},
]}
>
<Select
defaultValue="0"
>
<Option value="0">普通流程</Option>
<Option value="1">技术岗招聘流程</Option>
</Select>
</Form.Item>
<Divider />
<Form.Item
label="职位优先级"
name=""
>
<Select
defaultValue="0"
>
<Option value="0">普通流程</Option>
<Option value="1">技术岗招聘流程</Option>
</Select>
</Form.Item>
<Divider /> */}
<Form.Item
label="开始招聘时间"
name="start_time"
initialValue={postdata['start_time'] ? moment(postdata['start_time'], 'YYYY-MM-DD') : ''}
rules={[
{
required: true,
},
]}
>
<DatePicker
locale={locale}
placeholder="请选择招聘开始时间"
onChange={(dates, dateStrings) => {
updataPosition({start_time: dateStrings+" 00:00:00"})
}}
/>
</Form.Item>
<Divider />
<Form.Item
label="目标完成时间"
name="end_time"
initialValue={postdata['end_time'] ? moment(postdata['end_time'], 'YYYY-MM-DD') : ''}
rules={[
{
required: true,
},
]}
>
<DatePicker
locale={locale}
placeholder="请选择目标完成时间"
onChange={(dates, dateStrings) => {
updataPosition({end_time: dateStrings+" 23:59:59"})
}}
/>
</Form.Item>
{/* <Form.Item className="textalign">
<Button type="primary">保存</Button>
</Form.Item> */}
</Form>
</div>
</div>
)
}

View File

@ -0,0 +1,43 @@
.Positioninfo-box {
margin: 0 120px;
min-height: 300px;
display: flex;
justify-content: space-between;
}
.Positioninfo-box .Positioninfo-info-box {
background-color: #fff;
width: calc(100% - 308px);
border-right: 1px solid #ececee;
}
.Positioninfo-custom-box {
width: 278px;
background-color: #fff;
}
.Positioninfo-info-box .title {
font-weight: bold;
font-size: 20px;
color: #575d6a;
margin-bottom: 24px;
}
.Positioninfo-custom-box .title {
font-size: 16px;
color: #575d6a;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
}
.Positioninfo-custom-box .title span {
color: #40a9ff;
font-size: 12px;
}
.Positioninfo-custom-box .title2 {
margin: 14px 0;
font-size: 14px;
color: #89909e;
}
.Positioninfo-custom-box .label {
color: #40a9ff;
}

View File

@ -0,0 +1,452 @@
import React, { useState, useEffect } from 'react'
import { ownerlist, addjob, postamendjob } from '../../../util/requestURL'
import './index.css'
import { ProjectFilled } from '@ant-design/icons'
import { useNavigate } from 'react-router-dom'
import {
Button,
Form,
Input,
Radio,
Select,
Row,
Col,
InputNumber,
Divider,
message,
DatePicker,
} from 'antd'
import 'moment/locale/zh-cn'
import locale from 'antd/es/date-picker/locale/zh_CN'
const { Option } = Select
const { TextArea } = Input
export default function Positioninfo({ updataPosition, postdata }) {
const [form] = Form.useForm()
const navigate = useNavigate()
const [value, setValue] = useState(1)
const [sectorlist, setSectorlist] = useState([])
const [functiontype, setFunctiontype] = useState([])
const onChange = (e) => {
console.log('radio checked', e.target.value)
setValue(e.target.value)
updataPosition({ job_nature: e.target.value })
}
const onFinish = (values) => {
console.log(postdata)
if (!postdata.principal || !postdata.hr_name) {
message.warning('信息填写不完整,请选择负责人和用人经理!')
return
} else {
if (
postdata.principal.length === 0 ||
postdata.hr_name.length === 0
) {
message.warning('信息填写不完整,请选择负责人和用人经理!')
return
}
}
if (!postdata.end_time || !postdata.start_time) {
message.warning('信息填写不完整,请选择开始时间和结束时间!')
return
}
if (postdata.job_id) {
postamendjob(postdata).then(
(res) => {
message.success('修改成功')
navigate('/admin/position')
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
} else {
addjob(postdata).then(
(res) => {
// setSectorlist(res.data.date)
message.success('添加成功')
navigate('/admin/position')
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
}
}
const handelreturn=()=>{
window.history.back()
}
//
// const onValuesChange = (changedValues) => {
// updataPosition(changedValues)
// }
useEffect(() => {
//
ownerlist({ name: 'sector' }).then(
(res) => {
setSectorlist(res.data.date)
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
//
ownerlist({ name: 'function_type' }).then(
(res) => {
setFunctiontype(res.data.date)
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
}, [])
setTimeout(() => {
form.setFieldsValue(postdata)
}, 1)
return (
<Form
form={form}
layout="vertical"
name="register"
onFinish={(e) => {
form.validateFields()
onFinish(e)
}}
// onValuesChange={onValuesChange}
initialValues={postdata}
>
<div className="Positioninfo-box">
<div className="Positioninfo-info-box">
<div className="margin24">
<div className="title">职位信息</div>
<Form.Item
label="职位名称"
name={'job_name'}
rules={[
{
required: true,
message: '请填写职位名称!',
},
]}
>
<Input
placeholder="请输入职位名称"
onChange={(e) => {
updataPosition({ job_name: e.target.value })
}}
/>
</Form.Item>
<Row gutter={24}>
<Col span={12}>
<Form.Item
label="所属部门"
name={'job_sector'}
rules={[
{
required: true,
message: '请填写所属部门!',
},
]}
>
<Select
onChange={(e) => {
updataPosition({ job_sector: e })
}}
>
{sectorlist.map((item, key) => {
return (
<Option value={item} key={key}>
{item}
</Option>
)
})}
</Select>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="职位性质"
name={'job_nature'}
rules={[
{
required: true,
message: '请选择职位性质!',
},
]}
>
<Radio.Group
onChange={onChange}
value={value}
>
<Radio value={1} checked>
全职
</Radio>
<Radio value={2}>兼职</Radio>
<Radio value={3}>实习</Radio>
<Radio value={4}>其他</Radio>
</Radio.Group>
</Form.Item>
</Col>
{/* <Col span={12}>
<Form.Item label="职位类别" name={'function_type'} required>
<Input placeholder="input placeholder" />
</Form.Item>
</Col> */}
<Col span={12}>
<Form.Item
label="工作经验"
name={'work_exp'}
rules={[
{
required: true,
message: '请选择工作经验!',
},
]}
>
<Select
onChange={(e) => {
updataPosition({ work_exp: e })
}}
>
<Option value={0}>不限</Option>
<Option value={1}>应届生</Option>
<Option value={2}>1-3</Option>
<Option value={3}>3-5</Option>
<Option value={4}>5年以上</Option>
</Select>
</Form.Item>
</Col>
<Col span={2}>
<Form.Item
label="薪资范围"
name={'min_money'}
rules={[
{
required: true,
message: '请选择薪资范围!',
},
]}
>
<InputNumber
placeholder="最低薪资 K"
onChange={(e) => {
updataPosition({
min_money: e,
})
}}
/>
</Form.Item>
</Col>
<Col span={1} className="textaligncenter">
<Form.Item label=" ">~</Form.Item>
</Col>
<Col span={9}>
<Form.Item
label=" "
name={'max_money'}
rules={[
{
required: true,
message: '请选择薪资范围!',
},
]}
>
<InputNumber
placeholder="最高薪资 K"
onChange={(e) => {
updataPosition({
max_money: e,
})
}}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="招聘人数"
name={'job_num'}
rules={[
{
required: true,
message: '请选择招聘人数!',
},
]}
>
<Input
placeholder="请填写招聘人数"
onChange={(e) => {
updataPosition({
job_num: e.target.value,
})
}}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="职能类型"
name={'function_type'}
rules={[
{
required: true,
message: '请选择职能类型!',
},
]}
>
<Select
onChange={(e) => {
updataPosition({ function_type: e })
}}
>
{functiontype.map((item, key) => {
return (
<Option value={item} key={key}>
{item}
</Option>
)
})}
</Select>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="职位级别"
name={'job_rank'}
rules={[
{
required: true,
message: '请选择职位级别!',
},
]}
>
<Select
onChange={(e) => {
updataPosition({ job_rank: e })
}}
>
<Option value="0">不限</Option>
<Option value={1}>p1 - p4</Option>
<Option value={2}>p5 - p9</Option>
<Option value={3}>m1 - m2</Option>
<Option value={4}>m2以上</Option>
</Select>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="学历要求"
name={'education'}
rules={[
{
required: true,
message: '请选择学历要求!',
},
]}
>
<Select
onChange={(e) => {
updataPosition({ education: e })
}}
>
<Option value={0}>不限</Option>
<Option value={1}>专科</Option>
<Option value={2}>本科</Option>
<Option value={3}>硕士</Option>
<Option value={4}>博士</Option>
</Select>
</Form.Item>
</Col>
<Col span={24}>
<Form.Item
label="职位描述"
name={'requirement'}
rules={[
{
required: true,
message: '请选择职位描述!',
},
]}
>
<TextArea
rows={4}
onChange={(e) => {
updataPosition({
requirement: e.target.value,
})
}}
/>
</Form.Item>
</Col>
</Row>
<Row style={{'justifyContent': 'end'}}>
<Col span={1}>
<Form.Item className="textalign">
<Button type="primary" danger onClick={handelreturn}>
取消
</Button>
</Form.Item>
</Col>
<Col span={2}>
<Form.Item className="textalign">
<Button type="primary" htmlType="submit">
保存
</Button>
</Form.Item>
</Col>
</Row>
</div>
</div>
{/* 自定义 */}
<div className="Positioninfo-custom-box">
<div className="margin24">
<div className="title">
自定义岗位推荐{' '}
<span>
{' '}
<ProjectFilled /> 不准确反馈
</span>
</div>
<div className="title2">
从您JD中的提取岗位画像如下
</div>
<label className="label">行业要求软件开发</label>
<Divider />
<div className="title2">
您也可自定义岗位画像的特征设置完成后在候选人人才库推荐处看到岗位推荐结果
</div>
{/*
<Form.Item label="候选人所在地" required>
<Select defaultValue="lucy">
<Option value="jack">Jack</Option>
<Option value="lucy">Lucy</Option>
<Option value="disabled" disabled>
Disabled
</Option>
<Option value="Yiminghe">
yiminghe
</Option>
</Select>
</Form.Item> */}
</div>
</div>
</div>
</Form>
)
}

View File

@ -0,0 +1,2 @@
.AddPosition-box .ant-tabs { width: 100%; margin: 0 auto; }
.AddPosition-box .ant-tabs-nav { background-color: #fff; padding: 0 24px; }

View File

@ -0,0 +1,81 @@
import React, { Component } from 'react'
import { Button, Tabs, message } from 'antd'
import Positioninfo from './Positioninfo'
import PositionSetup from './PositionSetup'
import PositionResponsble from './PositionResponsble'
import { EllipsisOutlined, BarsOutlined } from '@ant-design/icons'
import { postfindjob } from '../../util/requestURL'
import PubSub from 'pubsub-js'
import './index.css'
const { TabPane } = Tabs
export default class AddPosition extends Component {
state = {
state: true
}
componentDidMount(){
const urlParams = new URL(window.location.href);
const pathname = urlParams.hash;
const pathnameArr = pathname.split('/');
const id = pathnameArr[pathnameArr.length - 1] //id
console.log(pathnameArr)
if(id !== "addposition"){
postfindjob({job_id: id}).then((res)=>{
this.setState({...res.data})
PubSub.publish('headtitle',{headtitle: res.data.job_name})
}).catch((err)=>{
message.error('网络加载错误,请稍后再试')
})
}else {
PubSub.publish('headtitle',{headtitle: '添加职位'})
}
}
render() {
const onChange = (key) => {
}
//
const updataPosition = (item) =>{
this.setState(item,()=>{
console.log(this.state)
})
}
const operations = (
<>
<Button>结束招聘</Button>
<Button>
<BarsOutlined />
</Button>
<Button>
<EllipsisOutlined className="transform90" />
</Button>
</>
)
return (
<div className="AddPosition-box">
<Tabs
defaultActiveKey="1"
onChange={onChange}
tabBarExtraContent={operations}
>
<TabPane tab="职位信息" key="1">
<Positioninfo updataPosition={updataPosition} postdata={this.state}/>
</TabPane>
<TabPane tab="职位设置" key="2">
<PositionSetup updataPosition={updataPosition} postdata={this.state}/>
</TabPane>
<TabPane tab="负责人" key="3">
<PositionResponsble updataPosition={updataPosition} postdata={this.state}/>
</TabPane>
</Tabs>
</div>
)
}
}

View File

@ -0,0 +1,108 @@
import React, { useEffect, useState } from 'react'
import { Form, Modal, Select, message } from 'antd'
import { getjob, updatainterview } from '../../util/requestURL'
import './index.css'
import { get } from 'store'
const { Option } = Select
export default function Assignmentposition(props) {
const [form] = Form.useForm()
const { visible, onCreate, onCancel, uid } = props
const [getjobdata, setgetjobdata] = useState([])
useEffect(() => {
if (uid) {
getjob({
job_name: '',
job_sector: '',
principal: [],
}).then(
(res) => {
setgetjobdata(res.data.job)
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
}
}, [visible])
return (
<div>
<Modal
visible={visible}
title="进入下一阶段"
okText="确认"
cancelText="取消"
onCancel={onCancel}
destroyOnClose={true}
maskClosable={false}
onOk={() => {
form.validateFields()
.then((values) => {
form.resetFields()
const job_names = values['job_name'][0].split('-')[0]
const job_id = values['job_name'][0].split('-')[1]
updatainterview({
data_in: {
interview_stage: 1,
job_names: job_names,
job_id: job_id,
},
interview_query: { uid: uid },
}).then(
(res) => {
if (res.msg === 'ok') {
message.success('添加成功!')
onCreate()
}
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
})
.catch((info) => {
console.log('Validate Failed:', info)
})
}}
>
<Form form={form} layout="vertical" name="form_in_modal">
<Form.Item
name="job_name"
label="分配职位"
rules={[
{
required: true,
message: '分配职位!',
},
]}
>
<Select
mode="multiple"
showSearch
placeholder="分配职位"
onChange={(option) =>{
if(option.length > 1){
option.splice(0,1)
}
}}
>
{getjobdata.map((item, key) => {
return (
<Option
value={item.job_name + '-' + item.key}
key={item.key}
>
{item.job_name}
</Option>
)
})}
</Select>
</Form.Item>
</Form>
</Modal>
</div>
)
}

View File

@ -1,15 +1,19 @@
.CandidateTable-box {}
.CandidateTable-box { height: calc(100% - 32px); overflow-y: auto; position: relative; }
.CandidateTable-list-box { width: 100%; padding: 14px; border-bottom: 1px solid #f4f4f5; display: flex; justify-content: space-between; }
.CandidateTable-list-box:hover { background-color: #f9f9fa; }
.CandidateTable-list-box:hover .but-box { display: flex; }
.CandidateTable-info-box { width: 510px; display: flex;}
.CandidateTable-info { margin-left: 45px; color: #575d6a; }
.CandidateTable-action { width: 420px; position: relative; }
.CandidateTable-info .name { font-size: 16px; font-weight: bold; color: #000; margin-right: 5px; }
.CandidateTable-info div { height: 28px; line-height: 28px; }
.but-box { width: 100%; display: flex; position: absolute; bottom: 0; }
.CandidateTable-info div {line-height: 28px; }
.CandidateTable-action .but-box { width: 100%; display: none; position: absolute; bottom: 0; }
.CandidateTable-action .but-box button { margin-right: 14px; }
.qxcheck-box { color: #c1c5cc; border: 1px solid #c1c5cc; width: 46px; text-align: center; margin-left: 14px; }
.qxcheck-box .ant-checkbox + span { padding-right: 0; }
.CandidateTable-table-action { border-bottom: 1px solid #f4f4f5; padding: 10px 0;}

View File

@ -1,122 +1,679 @@
import React from 'react'
import React, { Component } from 'react'
import './index.css'
import { Checkbox, Tag, Button } from 'antd'
import { Checkbox, Tag, Button, message, Empty, Popconfirm, Modal } from 'antd'
import {
ShoppingFilled,
BankOutlined,
SendOutlined,
RightOutlined,
ExclamationCircleOutlined,
} from '@ant-design/icons'
import Remarks from '../Remarks'
import Eliminate from '../Eliminate'
import Recommend from '../Recommend'
import Enterthenextstage from '../Enterthenextstage'
import Assignmentposition from '../Assignmentposition'
import InterviewerInfoPop from '../../components/InterviewerInfoPop'
import Editresume from '../../components/Editresume'
import {
interviewwaiteinfind,
interviewupdate,
sendinterviewee,
} from '../../util/requestURL'
const { confirm } = Modal
export default function CandidateTable() {
const onChange = (e) => {
console.log(`checked = ${e.target.checked}`)
export default class CandidateTable extends Component {
state = {
data: [
// {
// interview_name: '',
// interview_type: 0,
// interview_sign: 1,
// feedback: 0,
// interview_round: 1,
// star_time: '2022-07-11T10:30:00',
// end_time: '2022-07-11T11:00:00',
// event_time: '2022-07-01T10:00:00',
// uid: '1234567890',
// name: '',
// phone: '13789923799',
// job_name: 'python',
// hr_name: '',
// work_exp: '2',
// interview_stage: 1,
// owner_name: 2,
// education: 1,
// work_undergo: '',
// school: '',
// specialty: '',
// mmended_state: 0,
// mail: '250213000@qq.com',
// account: '',
// id_card: '421202199986750339',
// gender: '',
// interview_state: 2,
// graduate_time: '2022-07-01T08:00:00',
// counts: 1,
// choice: false, //
// },
// {
// interview_name: '',
// interview_type: 0,
// interview_sign: 1,
// feedback: 0,
// interview_round: 1,
// star_time: '2022-07-11T10:30:00',
// end_time: '2022-07-11T11:00:00',
// event_time: '2022-07-01T10:00:00',
// uid: '1234567890',
// name: '',
// phone: '13789923799',
// job_name: 'python',
// hr_name: '',
// work_exp: '2',
// interview_stage: 1,
// owner_name: 2,
// education: 1,
// work_undergo: '',
// school: '',
// specialty: '',
// mmended_state: 0,
// mail: '250213000@qq.com',
// account: '',
// id_card: '421202199983750339',
// gender: '',
// interview_state: 2,
// graduate_time: '2022-07-01T08:00:00',
// counts: 1,
// choice: false, //
// },
],
isremarks: false,
iseliminate: false,
isrecommend: false,
isenterthenextstage: false,
isassignmentposition: false,
interviewerInfo: false,
editresume: false,
uid: '',
step1open: false,
}
componentDidMount() {
this.PostInterviewwaiteinfind()
}
const data = [
{
interview_name: '张四',
interview_type: 0,
interview_sign: 1,
feedback: 0,
interview_round: 1,
star_time: '2022-07-11T10:30:00',
end_time: '2022-07-11T11:00:00',
event_time: '2022-07-01T10:00:00',
uid: '1234567890',
name: '李五',
phone: '13789923799',
job_name: 'python开发',
hr_name: '王六',
work_exp: '2年',
interview_stage: 1,
owner_name: 2,
education: 1,
work_undergo: '',
school: '武汉大学',
specialty: '计算机专业',
mmended_state: 0,
mail: '250213000@qq.com',
account: '湖北武汉',
id_card: '421202199986750339',
gender: '男',
interview_state: 2,
graduate_time: '2022-07-01T08:00:00',
counts: 1,
componentDidUpdate() {
if (this.props.step !== this.state.prevstep) {
this.PostInterviewwaiteinfind()
} else if (this.props.job_names !== this.state.provjob_names) {
this.PostInterviewwaiteinfind()
}
}
//
PostInterviewwaiteinfind() {
interviewwaiteinfind({
interview_query: {
interview_stage: this.props.step,
job_names: this.props.job_names,
},
]
}).then(
(res) => {
for (let i in res.data) {
res.data[i]['choice'] = false
}
this.setState({
data: res.data,
prevstep: this.props.step,
provjob_names: this.props.job_names,
})
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
}
onPopconfirm = (uid) => {
sendinterviewee({ uid: uid }).then(
(res) => {
if (res.msg === 'ok') {
this.PostInterviewwaiteinfind()
message.success('推荐成功,请尽快要用人经理确认!')
} else {
message.error('网络加载错误,请稍后再试')
}
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
}
render() {
const { data, step1open } = this.state
const { step, job_names } = this.props
//
let choiceCount = 0
if (data.length > 0) {
choiceCount = data.reduce(
(pre, item) => pre + (item.choice ? 1 : 0),
0
)
}
//
const todata = data.length
//
const onChange = (e) => {
const type = e.target.checked
const id = e.target.value
const { data } = this.state
const newData = data.map((dataObj) => {
if (dataObj.id_card === id) return { ...dataObj, choice: type }
else return dataObj
})
this.setState({
data: newData,
})
}
//
const onAllChange = (e) => {
const type = e.target.checked
const { data } = this.state
const newData = data.map((dataObj) => {
return { ...dataObj, choice: type }
})
this.setState({ data: newData })
}
//
const onClose = () => {
this.setState({
isremarks: false,
})
}
const handleRemarks = (uid) => {
this.setState({
isremarks: true,
uid: uid,
})
}
const handleEliminate = (uid) => {
this.setState({
iseliminate: true,
uid: uid,
})
}
const handleRecommend = (uid) => {
this.setState({
isrecommend: true,
uid: uid,
})
}
const onEliminateCreate = (e) => {
setTimeout(() => {
this.PostInterviewwaiteinfind()
this.props.updatainterviewstagenum()
this.setState({
iseliminate: false,
isrecommend: false,
isenterthenextstage: false,
isassignmentposition: false,
})
}, 2000)
}
//
const handelinterviewupdate = (uid) => {
const type = parseInt(this.props.step) + parseInt(1)
interviewupdate({
data_in: { interview_stage: type },
interview_query: { uid: uid },
}).then(
(res) => {
if (res.msg === 'ok') {
message.success('操作成功!')
setTimeout(() => {
this.PostInterviewwaiteinfind()
this.props.updatainterviewstagenum()
}, 2000)
}
},
(err) => {}
)
}
const handelCandidateClick = (uid) => {
console.log(uid)
this.setState({
interviewerInfo: true,
uid: uid,
})
}
//
const onEditresume = (data) => {
this.setState({
interviewerInfo: false,
editresume: true,
})
}
return (
<div className="CandidateTable-box">
<div className="CandidateTable-table-action">
<div className="qxcheck-box">
<Checkbox onChange={onChange}>
<Checkbox
onChange={onAllChange}
checked={choiceCount === todata ? true : false}
>
<RightOutlined style={{ color: '#c1c5cc' }} />
</Checkbox>
</div>
{/* <div>
</div> */}
</div>
<div>
{data.map((item, key) => {
<div style={{ width: 'calc(100% - 4px)' }}>
{this.state.data.length > 0 ? (
this.state.data.map((item, key) => {
return (
<div className="CandidateTable-list-box">
<div
className="CandidateTable-list-box"
key={item.uid}
onClick={() =>
handelCandidateClick(item.uid)
}
>
<div className="CandidateTable-info-box">
<div>
<Checkbox onChange={onChange}></Checkbox>
<Checkbox
checked={item.choice}
onChange={onChange}
value={item.id_card}
></Checkbox>
</div>
<div className="CandidateTable-info">
<div>
<span className="time">
2022-05-16申请 |
{item.event_time}申请 |
</span>
<span> {item.job_name}</span>
</div>
<div>
<span className="name">{item.name}</span>
<span> {item.gender} |</span> <span> 25 |</span>
<span> {item.work_exp}工作经验</span>
<span className="name">
{item.name}
</span>
<span> {item.gender} |</span>
<span> {item.age} | </span>
{item.work_exp == 0 ? (
<span>无经验</span>
) : (
<span>
{item.work_exp}工作经验
</span>
)}
</div>
<div>
{item['work_list'].map(
(items, key) => {
return (
<div key={key}>
<ShoppingFilled />
<span>{item.account} |</span>
<span> 2021-06至2022-05</span>
<span>
{
items.company_name
}{' '}
|{' '}
{
items.position_name
}{' '}
| {items.time}
</span>
</div>
)
}
)}
<div>
<BankOutlined />
{item.school !== '' ? (
<span> {item.school}</span>
) : (
''
)}
{item.specialty !== '' ? (
<span>
{' '}
| {item.specialty}
</span>
) : (
''
)}
{item.education !== '' ? (
<span>
{' '}
| {item.education}
</span>
) : (
''
)}
{item.graduate_time !== '' ? (
<span>
{' '}
| {item.graduate_time}
</span>
) : (
''
)}
</div>
<div>
<BankOutlined /> <span> {item.school} |</span>
<span> {item.specialty} | </span> <span> 硕士 |</span>
<span> {item.graduate_time}</span>
</div>
<div>
<Tag color="processing">硕士</Tag>
<Tag color="warning">空白经历</Tag>
<Tag color="processing">
{item.education}
</Tag>
{/* <Tag color="warning">
空白经历
</Tag> */}
</div>
</div>
</div>
<div className="CandidateTable-owner">
候选人所有者:<span>张三</span>
候选人所有者:<span>{item.hr_name}</span>
</div>
<div className="CandidateTable-action">
推荐状态
<Tag icon={<SendOutlined />} color="default">
{item.mmended_state == '未推荐' ? (
<Tag
icon={<SendOutlined />}
color="default"
>
未推荐
</Tag>
) : (
<Tag
icon={<SendOutlined />}
color="magenta"
>
以推荐
</Tag>
)}
<div className="but-box">
<Button type="primary">
{step < 5 && step != 1 && step != 3? (
<Button
type="primary"
onClick={(e) => {
e.stopPropagation()
if (step === '2') {
this.setState({
isenterthenextstage: true,
uid: item.uid,
})
} else if (
step === '0'
) {
this.setState({
isassignmentposition: true,
uid: item.uid,
})
} else {
handelinterviewupdate(
item.uid
)
}
}}
>
进入 下一阶段
</Button>
<Button>淘汰</Button>
<Button>备注</Button>
<Button>推荐给用人部门</Button>
) : step === '1' ? (
<Popconfirm
title="确定进入下一阶段,并推送给对应的用人经理"
onConfirm={(e) => {
e.stopPropagation()
this.onPopconfirm(
item.uid
)
}}
open={step1open}
okText="Yes"
cancelText="No"
onCancel={(e) => {
e.stopPropagation()
}}
>
<Button
type="primary"
onClick={(e) => {
e.stopPropagation()
this.setState({
step1open: true,
})
}}
>
进入下一阶段
</Button>
</Popconfirm>
) : step === '3'?(
item.teacher_state === "未反馈"?(
<Button
type="primary"
disabled
>
{item.teacher_state}
</Button>
):(
<Button
type="primary"
onClick={(e) => {
e.stopPropagation()
handelinterviewupdate(
item.uid
)
}}
>
进入 下一阶段
</Button>
)
):(
''
)}
{step < 5 ? (
<>
<Button
onClick={(e) => {
e.stopPropagation()
handleEliminate(
item.uid
)
}}
>
淘汰
</Button>
<Button
onClick={(e) => {
e.stopPropagation()
handleRemarks(
item.uid
)
}}
>
备注
</Button>
</>
) : (
<>
<Button
onClick={(e) => {
e.stopPropagation()
handleRemarks(
item.uid
)
}}
>
{/* 淘汰 */}
未入职
</Button>
<Button
onClick={(e) => {
e.stopPropagation()
confirm({
title: '确认修改候选人状态为已入职',
icon: (
<ExclamationCircleOutlined />
),
okText: '确认',
cancelText:
'取消',
onOk() {
interviewupdate({
data_in: { interview_stage: 8 },
interview_query: { uid: item.uid },
}).then(
(res) => {
if (res.msg === 'ok') {
message.success('操作成功!')
setTimeout(() => {
this.PostInterviewwaiteinfind()
this.props.updatainterviewstagenum()
}, 2000)
}
},
(err) => {}
)
},
})
}}
>
{/* 备注 */}
待入职
</Button>
</>
)}
{/* {
step <= 3? (<Button >重新推荐</Button>) :''
} */}
{/* {step === '1' ? (
<Button
onClick={(e) => {
e.stopPropagation()
handleRecommend(
item.uid
)
}}
>
推荐给用人部门
</Button>
) : (
''
)} */}
</div>
</div>
</div>
)
})}
})
) : (
<div className="margintop30 Empty-box">
<Empty description={'暂无数据'} />
</div>
)}
</div>
{/* 备注 */}
<Remarks
onClose={onClose}
visible={this.state.isremarks}
uid={this.state.uid}
remark_stage={step}
/>
{/* 淘汰 */}
<Eliminate
visible={this.state.iseliminate}
onCreate={onEliminateCreate}
uid={this.state.uid}
remark_stage={step}
onCancel={() => {
this.setState({
iseliminate: false,
})
}}
/>
{/* 推荐给用人部门 */}
<Recommend
visible={this.state.isrecommend}
onCreate={onEliminateCreate}
uid={this.state.uid}
onCancel={() => {
this.setState({
isrecommend: false,
})
}}
/>
{/* 进入下一阶段 */}
<Enterthenextstage
visible={this.state.isenterthenextstage}
onCreate={onEliminateCreate}
uid={this.state.uid}
onCancel={() => {
this.setState({
isenterthenextstage: false,
})
}}
/>
{/* 下一阶段分配岗位 */}
<Assignmentposition
visible={this.state.isassignmentposition}
onCreate={onEliminateCreate}
uid={this.state.uid}
onCancel={() => {
this.setState({
isassignmentposition: false,
})
}}
/>
{/* 查看面试人信息弹窗 */}
<InterviewerInfoPop
visible={this.state.interviewerInfo}
onCreate={() => {
this.setState({
interviewerInfo: false,
})
}}
data={this.state.uid}
onCancel={() => {
this.setState({
interviewerInfo: false,
})
}}
onEditresume={() => {
onEditresume()
}}
/>
{/* 编辑候选人信息弹窗 */}
<Editresume
visible={this.state.editresume}
onCreate={() => {
this.setState({
editresume: false,
})
}}
data={this.state.uid}
onCancel={() => {
this.setState({
editresume: false,
})
}}
/>
</div>
)
}
}

View File

@ -29,10 +29,15 @@
.upload-head-box label { color: #75777d; font-size: 12px; margin-top: 5px; }
.Editresume-box .ant-tabs-content-holder {
max-height: 557px !important;
overflow-x: hidden;
overflow-y: auto;
}
.upload-button-box {
margin: 24px;
}
.upload-button-box .ant-upload {width: 100%;}
.upload-button-box button { width: 100%; }
.upload-button-box .ant-upload-list {display: none;}
.resume-energy-box { display: flex; justify-content: space-between; }
.resume-xq-box { height: 625px; overflow-x: hidden; overflow-y: auto; }
.width700 { width: 700px; }

File diff suppressed because it is too large Load Diff

View File

View File

@ -0,0 +1,105 @@
import React, { useEffect , useState} from 'react'
import { Form, Input, Modal, Radio,Select, message } from 'antd'
import {interviewupdate, operatelog} from "../../util/requestURL"
import memoryUtils from '../../util/memoryUtils'
const { Option } = Select
export default function Eliminate(props) {
const { visible, onCreate, onCancel, uid, remark_stage } = props
const [form] = Form.useForm()
const pass_why_list = [
{title: '招聘需求变更', value: 0},
{title: '福利待遇不匹配', value: 1},
{title: '与公司文化不符', value: 2},
{title: '淘汰', value: 3},
{title: '胜任力不足', value: 4},
{title: '没有回应', value: 5},
{title: '其他', value: 6}
]
let userInfo = memoryUtils.userInfo
return (
<div>
<Modal
visible={visible}
title="淘汰候选人"
okText="确认"
cancelText="取消"
onCancel={onCancel}
onOk={() => {
form.validateFields()
.then((values) => {
form.resetFields()
interviewupdate({
data_in: {"interview_stage": 11, ...values},//
interview_query: { uid: uid },
data:{interview_stage: remark_stage},
find_column:[]
}).then(
(res) => {
// console.log(res)
message.success('操作成功!')
onCreate(values)
operatelog({
user_id: uid,
why: "淘汰候选人",
content: pass_why_list[values.pass_why]['title'] + values.pass_text,
})
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
})
.catch((info) => {
console.log('Validate Failed:', info)
})
}}
>
<Form
form={form}
layout="vertical"
name="form_in_modal"
initialValues={{
modifier: 'message',
}}
>
<Form.Item
name="pass_why"
label="淘汰原因"
rules={[
{
required: true,
message:
'请选择淘汰原因!',
},
]}
>
<Select>
<Option value={0}>招聘需求变更</Option>
<Option value={1}>福利待遇不匹配</Option>
<Option value={2}>与公司文化不符</Option>
<Option value={3}>淘汰</Option>
<Option value={4}>胜任力不足</Option>
<Option value={5}>没有回应</Option>
<Option value={6}>其他</Option>
</Select>
</Form.Item>
<Form.Item name="pass_text" label="详细原因">
<Input type="textarea" />
</Form.Item>
{/* <Form.Item
name="modifier"
className="collection-create-form_last-form-item"
>
<Radio.Group>
<Radio value="message" >短信拒绝</Radio>
<Radio value="mailbox">邮箱拒绝</Radio>
</Radio.Group>
</Form.Item> */}
</Form>
</Modal>
</div>
)
}

View File

@ -0,0 +1,190 @@
import React, { useEffect, useState } from 'react'
import { Form, Modal, DatePicker, Select, message, Row, Col } from 'antd'
import locale from 'antd/es/date-picker/locale/zh_CN'
import moment from 'moment'
import { getname, addinterviewe } from '../../util/requestURL'
import './index.css'
const { Option } = Select
const { RangePicker } = DatePicker
export default function Enterthenextstage(props) {
const [form] = Form.useForm()
const { visible, onCreate, onCancel, uid } = props
const [getnamedata, setgetnamedata] = useState([])
const [stime, ssettime] = useState()
const [etime, esettime] = useState()
useEffect(() => {
if (uid) {
getname().then(
(res) => {
console.log(res.data)
setgetnamedata(res.data)
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
}
}, [visible])
return (
<div>
<Modal
visible={visible}
title="进入下一阶段"
okText="确认"
cancelText="取消"
onCancel={onCancel}
destroyOnClose={true}
onOk={() => {
form.validateFields()
.then((values) => {
form.resetFields()
var data = {}
data['uid'] = uid
data['user_id'] = values.user_id
data['hr_user'] = values.hr_user
data['star_time'] = stime
data['end_time'] = etime
addinterviewe(data).then(
(res) => {
if (res.msg === 'ok') {
message.success('添加成功!')
onCreate()
esettime('')
}
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
})
.catch((info) => {
console.log('Validate Failed:', info)
})
}}
>
<Form
form={form}
layout="vertical"
name="form_in_modal"
preserve={false}
>
<Form.Item
name="hr_user"
label="分配HR"
rules={[
{
required: true,
message: '请选择HR!',
},
]}
>
<Select mode="multiple">
{getnamedata.map((item, key) => {
return item.rank === 1 ? (
<Option value={item.user_id} key={item._id}>
{item.nickname}
</Option>
) : (
''
)
})}
</Select>
</Form.Item>
<Form.Item
name="user_id"
label="分配面试官"
rules={[
{
required: true,
message: '请选择面试官!',
},
]}
>
<Select mode="multiple">
{getnamedata.map((item, key) => {
return item.rank === 2 ? (
<Option value={item.user_id} key={item._id}>
{item.nickname}
</Option>
) : (
''
)
})}
</Select>
</Form.Item>
<Row gutter={24}>
<Col span={24}>
<Form.Item
name="stime"
label="面试时间"
>
<DatePicker
showTime
locale={locale}
onChange={(date, dateString) => {
// console.log(dateString)
var date = new Date(dateString)
var time3 =
(Date.parse(date) / 1000 + 1800) *
1000
var time = new Date(time3)
var y = time.getFullYear()
var m =
time.getMonth() + 1 < 10
? '0' + (time.getMonth() + 1)
: time.getMonth() + 1
var d =
time.getDate() < 10
? '0' + time.getDate()
: time.getDate()
var h =
time.getHours() < 10
? '0' + time.getHours()
: time.getHours()
var mm =
time.getMinutes() < 10
? '0' + time.getMinutes()
: time.getMinutes()
var s =
time.getSeconds() < 10
? '0' + time.getSeconds()
: time.getSeconds()
var end_time =
y +
'-' +
m +
'-' +
d +
' ' +
h +
':' +
mm +
':' +
s
ssettime(dateString)
esettime(end_time)
}}
/>
<DatePicker
showTime
locale={locale}
value={etime?moment(
etime,
'YYYY/MM/DD HH:mm:ss'
):''}
format={'YYYY/MM/DD HH:mm:ss'}
/>
</Form.Item>
</Col>
</Row>
</Form>
</Modal>
</div>
)
}

View File

@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react'
import { Modal, DatePicker, Form, Radio, Button } from 'antd'
import { Modal, DatePicker, Form, Radio, Button, message } from 'antd'
import { downloadinterview } from '../../util/requestURL'
import './index.css'
import moment from 'moment'
import 'moment/locale/zh-cn'
@ -7,6 +8,7 @@ import locale from 'antd/es/date-picker/locale/zh_CN'
const { RangePicker } = DatePicker
const dateFormat = 'YYYY/MM/DD'
export default function ExportForm({ visible, onCreate, onCancel }) {
const today = [
moment(
@ -59,7 +61,26 @@ export default function ExportForm({ visible, onCreate, onCancel }) {
cancelText="取消"
onCancel={onCancel}
onOk={() => {
console.log(time)
// console.log(time)
let name = ''
if(time[0] === time[1]){
name = time[0]+'面试安排'
}else{
name = time[0]+'至'+time[1]+'面试安排'
}
const dateTimes = {
start_time: time[0],
end_time: time[1],
name: name
}
downloadinterview(dateTimes).then(
(res) => {
// console.log(res)
},
(error) => {
message.error('网络加载错误,请稍后再试');
}
)
}}
>
<h5>最多可导出连续7天的面试</h5>
@ -71,7 +92,6 @@ export default function ExportForm({ visible, onCreate, onCancel }) {
value={dataArr}
onChange={(dates, dateStrings) => {
setdataArr(dates)
console.log(dateStrings)
setTime(dateStrings)
}}
/>

View File

@ -1,11 +1,27 @@
import React,{Component} from 'react'
import PubSub from 'pubsub-js'
import "./index.css"
// import { withRouter } from 'react-router-dom'
class HeadLeftTtile extends Component {
componentDidMount(){
this.headtitle = PubSub.subscribe('headtitle',(_,stateObj)=>{
this.setState(stateObj)
})
}
componentWillUnmount(){
PubSub.unsubscribe(this.headtitle)
}
render() {
const { headtitle } = this.props
let headtitle
if(this.state){
headtitle = this.state.headtitle
}
return <div className="HeadLeftTitle-box">{headtitle}</div>
}
}

View File

@ -1,27 +0,0 @@
import React, { useState } from 'react'
import { Button } from 'antd'
import ResumeUpload from '../../pages/ResumeUpload'
import {PlusOutlined} from '@ant-design/icons'
export default function InterviewUploadPop() {
return (
<div>
<Button
type="primary"
// onClick={() => {
// setVisible(true)
// }}
>
<PlusOutlined />上传
</Button>
{/* <ResumeUpload
visible={visible}
onCreate={onCreate}
onCancel={() => {
setVisible(false)
}}
/> */}
</div>
)
}

View File

@ -1,17 +1,37 @@
import React from 'react'
import React,{useEffect} from 'react'
import './index.css'
import { UploadOutlined } from '@ant-design/icons'
import { Button, message, Upload,Input } from 'antd'
const props = {
export default function EnclosureTabs({data, Tabsnum}) {
useEffect(() => {
if (Tabsnum === '6') {
// getoperatelog({ user_id: data }).then(
// (res) => {
// console.log(res)
// setdatalist(res.data)
// },
// (err) => {
// message.error('')
// }
// )
}
}, [Tabsnum])
const props = {
name: 'file',
action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
// action: 'http://myip.legu.cc:7800/api/v1/itr/resume_affix',
action: 'http://10.0.0.4:7800/api/v1/itr/resume_affix',
data:{
uid: data.uid
},
headers: {
authorization: 'authorization-text',
},
onChange(info) {
if (info.file.status !== 'uploading') {
if (info.file.status != 'uploading') {
console.log(info.file, info.fileList)
}
@ -21,9 +41,8 @@ const props = {
message.error(`${info.file.name} file upload failed.`)
}
},
}
}
export default function EnclosureTabs() {
return (
<div className="EnclosureTabs-box">
<h2>附件</h2>

View File

@ -0,0 +1,5 @@
.Feedback-title { font-size: 17px; color: #000; font-weight: bold; margin-top: 24px; }
.Feedback-tag { margin-bottom: 0; color: #75777d; }
/* .result-box .ant-radio-button-wrapper { padding: 16px 30px; } */

View File

@ -0,0 +1,193 @@
import React, { useEffect, useState } from 'react'
import { Drawer, Button, Input, message, Radio, Space } from 'antd'
import {
interviewteacher,
getteacher,
upteacher,
} from '../../../util/requestURL'
import storageUtils from '../../../util/storageUtils'
import './index.css'
const { TextArea } = Input
export default function Feedback({ visible, onClose, uid }) {
var userInfo = storageUtils.getUser()
const [voice, setvoice] = useState(1)
const [thought, setthought] = useState(1)
const [diathesis, setdiathesis] = useState(1)
const [evaluate, setevaluate] = useState('')
const [teacherback, setteacherback] = useState(1)
const [isupdata, setisupdata] = useState(0)
useEffect(() => {
if (uid) {
getteacher({ uid: uid }).then(
(res) => {
if (res.msg === 'ok') {
if (res.data.uid) {
const user_id = userInfo.user_id
if (res.data['survey']['']) {
setisupdata(1)
setvoice(res.data['survey'][user_id].voice || 1)
setthought(res.data['survey'][user_id].thought || 1)
setdiathesis(
res.data['survey'][user_id].diathesis || 1
)
setevaluate(res.data['survey'][user_id].evaluate)
setteacherback(res.data['survey'][user_id].teacher_back)
}
}
}
},
(err) => {
message.error('网络加载失败,请稍后再试')
}
)
}
}, [visible])
const handelFeedback = () => {
const date = {
name: userInfo.name,
voice,
thought,
diathesis,
evaluate,
teacher_back: teacherback,
}
if (isupdata === 0) {
interviewteacher({
uid: uid,
survey: {
[userInfo.user_id]: date,
},
}).then(
(res) => {
if (res.msg === 'ok') {
message.success('添加成功')
onClose()
}
},
(err) => {
message.error('网络加载失败,请稍后再试')
}
)
} else {
upteacher({
uid: uid,
[userInfo.user_id]: date,
}).then(
(res) => {
if (res.msg === 'ok') {
message.success('修改成功')
onClose()
}
},
(err) => {
message.error('网络加载失败,请稍后再试')
}
)
}
}
return (
<div>
<Drawer
title="填写反馈"
placement="right"
onClose={onClose}
visible={visible}
zIndex={10000}
width={520}
extra={
<Space>
<Button onClick={onClose}>取消</Button>
<Button type="primary" onClick={handelFeedback}>
确定
</Button>
</Space>
}
>
<div className="Feedback-box">
<div className="Feedback-title">语言表达单选题</div>
<p className="Feedback-tag">
语言表达是否具有逻辑性简洁性及感染力
</p>
<p className="Feedback-tag">
评分规则好8-10中4-70-3
</p>
<Radio.Group
onChange={(e) => setvoice(e.target.value)}
value={voice}
>
<Space direction="vertical">
<Radio value={1}></Radio>
<Radio value={2}></Radio>
<Radio value={3}></Radio>
</Space>
</Radio.Group>
<div className="Feedback-title">逻辑思维单选题</div>
<p className="Feedback-tag">
思维是否清晰分析问题是否准确透彻
</p>
<p className="Feedback-tag">
评分规则好8-10中4-70-3
</p>
<Radio.Group
onChange={(e) => setthought(e.target.value)}
value={thought}
>
<Space direction="vertical">
<Radio value={1}></Radio>
<Radio value={2}></Radio>
<Radio value={3}></Radio>
</Space>
</Radio.Group>
<div className="Feedback-title">个人素养单选题</div>
<p className="Feedback-tag">
个人学历仪表气质是否达到要求
</p>
<p className="Feedback-tag">
评分规则好8-10中4-70-3
</p>
<Radio.Group
onChange={(e) => setdiathesis(e.target.value)}
value={diathesis}
>
<Space direction="vertical">
<Radio value={1}></Radio>
<Radio value={2}></Radio>
<Radio value={3}></Radio>
</Space>
</Radio.Group>
<div className="Feedback-title">综合评价</div>
<p className="Feedback-tag">
请详细说明候选人的优势不足及任何值得注意的方面
</p>
<TextArea
rows={4}
className="margintop20"
value={evaluate}
onChange={(e) => setevaluate(e.target.value)}
/>
<div className="Feedback-title">面试结果</div>
<div className="margintop20 result-box">
<Radio.Group
value={teacherback}
buttonStyle="solid"
onChange={(e) => setteacherback(e.target.value)}
>
<Radio.Button value={4}>非常不满意</Radio.Button>
<Radio.Button value={3}>不满意</Radio.Button>
<Radio.Button value={1}>满意</Radio.Button>
<Radio.Button value={2}>非常满意</Radio.Button>
</Radio.Group>
</div>
</div>
</Drawer>
</div>
)
}

View File

@ -14,6 +14,8 @@
.InterviewTabs-content-list-box label { width: 160px; }
.InterviewTabs-content-list-actionbar { margin-top: 10px; padding: 0 10px; display: flex; justify-content: space-between; align-items: center; background-color: #f0f0f0; height: 35px; line-height: 35px; }
.InterviewTabs-content-list-left-box { display: flex; }
.InterviewTabs-content-list-left-name { width: 160px; }

View File

@ -1,68 +1,61 @@
import React from 'react'
import { Button, Collapse, Divider, Tag } from 'antd'
import React, { useState, useEffect } from 'react'
import { Button, Collapse, Divider, message, Tag, Empty } from 'antd'
import Enterthenextstage from '../../Enterthenextstage'
import { record, teacherstate } from '../../../util/requestURL'
import storageUtils from '../../../util/storageUtils'
import './index.css'
import {
CalendarFilled,
SettingOutlined,
WarningFilled,
DislikeOutlined,
LikeOutlined,
} from '@ant-design/icons'
const { Panel } = Collapse
export default function InterviewTabs() {
export default function InterviewTabs({
data,
Tabsnum,
setisfeedback,
seefeedback,
}) {
const [isenterthenextstage, setisenterthenextstage] = useState(false)
const [datalist, setdatalist] = useState([])
let userinfo = storageUtils.getUser()
useEffect(() => {
if (Tabsnum === '2') {
record({ uid: data.uid }).then(
(res) => {
// console.log(res)
setdatalist(res.data)
},
(err) => {
message.error('网络错误,请稍后再试')
}
)
}
}, [Tabsnum])
const handelFeedback = (record) => {
teacherstate({
user_id: [record.interview_id],
name: record.name,
}).then(
(res) => {
if (res.code == 200) {
message.success('发送成功')
}
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
}
const onChange = (key) => {
console.log(key)
}
const data = [
{
title: '2022年5月16日 星期一·现场面试',
place: '武汉市洪山区野芷湖西路创意天地5号楼2层',
pic: '张三',
rounds: 0,
time: '12:00',
duration: '30分钟',
},
{
title: '2022年5月16日 星期一·现场面试',
place: '武汉市洪山区野芷湖西路创意天地5号楼2层',
pic: '张三',
rounds: 0,
time: '12:00',
duration: '30分钟',
},
{
title: '2022年5月16日 星期一·现场面试',
place: '武汉市洪山区野芷湖西路创意天地5号楼2层',
pic: '张三',
rounds: 0,
time: '12:00',
duration: '30分钟',
},
{
title: '2022年5月16日 星期一·现场面试',
place: '武汉市洪山区野芷湖西路创意天地5号楼2层',
pic: '张三',
rounds: 0,
time: '12:00',
duration: '30分钟',
},
{
title: '2022年5月16日 星期一·现场面试',
place: '武汉市洪山区野芷湖西路创意天地5号楼2层',
pic: '张三',
rounds: 0,
time: '12:00',
duration: '30分钟',
},
{
title: '2022年5月16日 星期一·现场面试',
place: '武汉市洪山区野芷湖西路创意天地5号楼2层',
pic: '张三',
rounds: 0,
time: '12:00',
duration: '30分钟',
},
]
const genExtra = () => (
<SettingOutlined
onClick={(event) => {
@ -72,56 +65,193 @@ export default function InterviewTabs() {
/>
)
const handeladdinterview = () => {
setisenterthenextstage(true)
}
return (
<div className="InterviewTabs-box">
<div className="InterviewTabs-top-buttom-box">
<Button size="Large">添加面试</Button>
<Button size="Large" onClick={handeladdinterview}>
添加面试
</Button>
<label>导出面试反馈</label>
</div>
<div className="InterviewTabs-content-box">
<Collapse defaultActiveKey="0" onChange={onChange}>
{data.map((item, key) => {
{datalist.length > 0 ? (
datalist.map((item, key) => {
return (
<Panel
header={item.title}
header={item.times}
key={key}
extra={genExtra()}
>
<div className="InterviewTabs-content-list-box">
<label className="color2">面试地点</label>
<span>{item.place}</span>
<label className="color2">
面试地点
</label>
<span>
武汉市洪山区野芷湖西路创意天地5号楼2层
</span>
</div>
<div className="InterviewTabs-content-list-box">
<label className="color2">面试负责人</label>
<span>{item.pic}</span>
<label className="color2">
面试负责人
</label>
<span>{item.hr_name}</span>
</div>
<Divider />
<div className="InterviewTabs-content-list-box">
<label className="color2">面试信息</label>
<label className="color2">
面试信息
</label>
<span>
{' '}
<Tag color="#22b8cf">初试</Tag>{' '}
{item.time} 时长{item.duration}
{/* {item.time} 时长:{item.duration} */}
</span>
</div>
<div className="InterviewTabs-content-list-actionbar">
<div>{item.pic}</div>
<div>
<WarningFilled style={{color:'#f57527'}}/>
为评估
{item.survey.map((items, keys) => {
return (
<div
className="InterviewTabs-content-list-actionbar"
key={keys}
>
<div className="InterviewTabs-content-list-left-box">
<div className="InterviewTabs-content-list-left-name">
{items.name}
</div>
<div>
<Button type="link">催促反馈</Button>
<Button type="link">填写反馈</Button>
<Button type="link">面试未进行</Button>
{!items.type ? (
<div>
<WarningFilled
style={{
color: '#f57527',
}}
/>{' '}
未评估
</div>
) : item.teacher_back ===
1 ? (
<div>
<LikeOutlined
style={{
color: '#2cda6c',
}}
/>{' '}
满意
</div>
) : item.teacher_back ===
2 ? (
<div>
<LikeOutlined
style={{
color: '#2cda6c',
}}
/>{' '}
非常满意
</div>
) : item.teacher_back ===
3 ? (
<div>
<DislikeOutlined
style={{
color: '#f7260f',
}}
/>{' '}
不满意
</div>
) : (
<div>
<DislikeOutlined
style={{
color: '#f7260f',
}}
/>{' '}
非常满意
</div>
)}
</div>
</div>
<div>
{item.back_status ===
0 ? (
<div>
<Button
type="link"
onClick={() => {
handelFeedback(
item
)
}}
>
催促反馈
</Button>
{userinfo.id ===
items.user_id || userinfo.name === 'root' ? (
<Button
type="link"
onClick={() => {
setisfeedback()
}}
>
填写反馈
</Button>
) : (
''
)}
</div>
) : (
<div>
<Button
type="link"
onClick={() => {
seefeedback()
}}
>
查看反馈
</Button>
</div>
)}
{item.interview_sign !=
0 ? (
<Button type="link">
面试未进行
</Button>
) : (
''
)}
</div>
</div>
)
})}
</div>
</Panel>
)
})}
})
) : (
<div className="margintop30">
<Empty description={'暂无数据'} />
</div>
)}
</Collapse>
</div>
{/* 添加面试 */}
<Enterthenextstage
visible={isenterthenextstage}
onCreate={() => {
setisenterthenextstage(false)
}}
uid={data.uid}
onCancel={() => {
setisenterthenextstage(false)
}}
/>
</div>
)
}

View File

@ -3,16 +3,17 @@
.OfferTabs-title-box span { color: #0c8cf6; font-size: 14px; cursor: pointer; font-weight: 400; }
.OfferTabs-border-box { border: 1px solid #dddfe3; padding: 16px; margin-top: 16px; text-align: left; }
.OfferTabs-border-box .OfferTabs-border-div { margin-bottom: 20px; }
.display { display: flex; justify-content: space-between; align-items: center; }
.Approval-box { text-align: left; }
.Approval-box .title {font-weight: bold;}
.Approval-box span { color: #0c8cf6; }
.but-box { display: flex; justify-content: space-between; align-items: center; margin-top: 16px;}
.OfferTabs-box .but-box { display: flex; justify-content: space-between; align-items: center; margin-top: 16px;}
.Offer-info-top-box { display: flex; justify-content: space-between; align-items: center; text-align: left; }
.Offer-info-box { text-align: left; margin: 16px 24px; }
.label-box { background-color: #dddfe3; padding: 8px 5px; color: #6e7686; }
.label-box { background-color: #dddfe3; padding: 6px 8px; color: #6e7686; }
.generate-box { display: flex; justify-content: space-between; align-items: center; margin: 16px 0; }
.generate-box span { margin-left: 10px; color: #0c8cf6; cursor: pointer; }
.OfferTabs-maillist { width: 100%; }

View File

@ -1,13 +1,66 @@
import React from 'react'
import React, { useEffect, useState } from 'react'
import './index.css'
import { Button, Dropdown, Menu, Tag } from 'antd'
import {
Button,
Dropdown,
Menu,
Tag,
message,
DatePicker,
InputNumber,
Input,
Select,
} from 'antd'
import {
emailrecord,
updataentry,
sendstrmail,
getname,
updatainterview,
operatelog
} from '../../../util/requestURL'
import memoryUtils from '../../../util/memoryUtils'
import moment from 'moment'
import 'moment/locale/zh-cn'
import locale from 'antd/es/date-picker/locale/zh_CN'
import {
RightOutlined,
EllipsisOutlined,
DownOutlined,
} from '@ant-design/icons'
const { Option } = Select
const dateFormat = 'YYYY/MM/DD'
export default function OfferTabs({ data }) {
const [OfferPostList, setOfferPostList] = useState([]) //offer
const [Offerstate, setOfferstate] = useState(false)
const [offerdata, setoffdata] = useState({
Entry_time: data.Entry_time || '', //
wages: data.wages || data.hope_money, //
phone: data.phone, //
mail: data.mail, //
hr_name: data.hr_name, //
hr_id: data.hr_id
})
const [getnames, setgetnames] = useState([])
let userInfo = memoryUtils.userInfo
useEffect(() => {
emailrecord({ user_id: data.uid }).then(
(res) => {
// console.log(res.data)
setOfferPostList(res.data)
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
getname().then((res) => {
setgetnames(res.data)
})
}, [])
export default function OfferTabs() {
const onMenuClick = (e) => {
console.log('click', e)
}
@ -32,21 +85,94 @@ export default function OfferTabs() {
/>
)
const postupdataentry = (type) => {
updataentry({
uid: data.uid,
interview_stage: type,
job_id: data.job_id,
}).then(
(res) => {
console.log(res.data)
// setOfferPostList(res.data)
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
}
// Offer
const handelSendstrMail = () => {
sendstrmail({
user_id: data.uid,
name: data.name,
email: data.mail,
email_str:`${data.name},您好\n\n感谢关注乐谷在线科技有限公司很高兴的通知您通过了我们的面试真诚的邀请您加入我们的团队\n职位${data.job_names}\n入职时间${data.Entry_time}
\n薪酬${data.wages}\n联系人${data.hr_name}\n联系电话${data.phone}\n联系邮箱${data.email}\n\n如有问题请用以上联系方式及时与我们沟通谢谢\n系统邮件请勿回复`
}).then(
(res) => {
operatelog({
user_id: data.uid,
why: "发送Offer",
content: `${data.name},您好\n\n感谢关注乐谷在线科技有限公司很高兴的通知您通过了我们的面试真诚的邀请您加入我们的团队\n职位${data.job_names}\n入职时间${data.Entry_time}
\n薪酬${data.wages}\n联系人${data.hr_name}\n联系电话${data.phone}\n联系邮箱${data.email}\n\n如有问题请用以上联系方式及时与我们沟通谢谢\n系统邮件请勿回复`,
})
},
(err) => {}
)
}
const handelOfferstate = (type) => {
if (!type) {
const data_in = {
...data,
Entry_time: offerdata.Entry_time,
wages: offerdata.wages,
phone: offerdata.phone,
mail: offerdata.mail,
hr_name: offerdata.hr_name,
hr_id: offerdata.hr_id
}
updatainterview({data_in: data_in}).then((res)=>{
operatelog({
user_id: data.uid,
why: "修改候选人Offer信息",
content: '修改候选人Offer信息',
})
},(err)=>{
})
}
setOfferstate(type)
}
return (
<div className="OfferTabs-box">
<div style={{ textAlign: 'center' }}>
<Dropdown.Button overlay={menu}>导出offer信息</Dropdown.Button>
{/* <Dropdown.Button overlay={menu}>导出offer信息</Dropdown.Button> */}
<div className="OfferTabs-title-box">入职记录</div>
<div className="OfferTabs-border-box display">
<span>尚未确定候选人是否入职</span>
<div className="">
<Button>确认入职</Button>
<Button>放弃入职</Button>
<Button
onClick={() => {
postupdataentry(7)
}}
>
确认入职
</Button>
<Button
onClick={() => {
postupdataentry(6)
}}
>
放弃入职
</Button>
</div>
</div>
<div className="OfferTabs-title-box">Offer审批</div>
{/* <div className="OfferTabs-title-box">Offer</div>
<div className="but-box">
<Button>重新发起审批</Button>
<Button>修改审批流程</Button>
@ -63,17 +189,25 @@ export default function OfferTabs() {
<Tag color="#37b24d">已通过</Tag>
<RightOutlined />
</div>
</div>
</div> */}
<div className="OfferTabs-title-box">Offer发送记录</div>
<div className="but-box">
<Button>重新发送</Button>
<Button onClick={handelSendstrMail}> {OfferPostList.length > 0 ? '重新发送': '发送Offer'} </Button>
</div>
<div className="OfferTabs-border-box display">
<div className="OfferTabs-border-box">
{OfferPostList.map((item, key) => {
return (
<div
className="display OfferTabs-maillist"
key={key}
>
<div className="Approval-box">
<div className="title">杨女士的Offer邮件</div>
<div>2022-05-16 发起</div>
<div className="title">
{item.name}的Offer邮件
</div>
<div>{item.times} 发起</div>
</div>
<div className="">
@ -81,78 +215,195 @@ export default function OfferTabs() {
<EllipsisOutlined />
</div>
</div>
)
})}
</div>
<div className="OfferTabs-title-box display">
{/* <div className="OfferTabs-title-box display">
Offer详情
<span>编辑</span>
</div>
<div className="OfferTabs-border-box">
<div className="Offer-info-top-box">
<span>杨女士的Offer</span>
<span>
{data.name.charAt(0)}
{data.gender === '男' ? '先生' : '女士'}的Offer
</span>
<DownOutlined />
</div>
<div className="Offer-info-box">
<h4 className="fontweight">offer详情</h4>
{/* <div>
<span>薪资待遇</span>
</div>
</div> */}
</div>
</div>
<div className="OfferTabs-title-box display">
Offer邮件及短信
<span>编辑</span>
{Offerstate ? (
<span
onClick={() => {
handelOfferstate(false)
}}
>
保存
</span>
) : (
<span
onClick={() => {
handelOfferstate(true)
}}
>
编辑
</span>
)}
</div>
<h4 className="fontweight">邮件信息</h4>
<div className="OfferTabs-border-box">
<p>
<label className='label-box'>杨女士</label>,你好
</p>
<p>
感谢关注<label className='label-box'>武汉乐谷在线科技有限公司</label>!
跟高兴的通知您通过了我们的面试真诚的邀请您加入我们的团队
<div className="OfferTabs-border-div">
<label className="label-box">
{data.name.charAt(0)}
{data.gender === '男' ? '先生' : '女士'}
</label>
,你好
</div>
<div className="OfferTabs-border-div">
感谢关注
<label className="label-box">
武汉乐谷在线科技有限公司
</label>
!
很高兴的通知您通过了我们的面试真诚的邀请您加入我们的团队
<br />
</p>
<p>
职位 <label className='label-box'>高级游戏测试工程师</label>
</p>
<p>
入职时间<label className='label-box'>2022-05-26</label>
</p>
<p>
薪酬<label className='label-box'>月薪</label>
</p>
<p>
<label className='label-box'>111111</label>
</p>
<p>
入职地点<label className='label-box'>xxx</label>
</p>
<p>
联系人<label className='label-box'>张三</label>
</p>
<p>
联系电话<label className='label-box'>123568875145</label>
</p>
<p>
联系邮箱<label className='label-box'>123659@163.com</label>
</p>
</div>
<div className="OfferTabs-border-div">
职位{' '}
<label className="label-box">{data.job_names}</label>
</div>
<div className="OfferTabs-border-div">
入职时间
{/* <label className="label-box"> */}
<DatePicker
onChange={(date, dateString) => {
setoffdata({
...offerdata,
Entry_time: dateString,
})
}}
disabled={!Offerstate}
locale={locale}
defaultValue={moment(
moment(
(Math.round(new Date() / 1000) -
7 * 86400) *
1000
).format('YYYY-MM-DD'),
dateFormat
)}
/>
{/* </label> */}
</div>
<div className="OfferTabs-border-div">
薪酬<label className="label-box">月薪</label>{' '}
<InputNumber
min={1}
defaultValue={offerdata.wages}
disabled={!Offerstate}
onChange={(e) => {
setoffdata({
...offerdata,
wages: e,
})
}}
/>
</div>
{/* <div className='OfferTabs-border-div'>
Entry_time wages phone mail hr_name
</div> */}
<div className="OfferTabs-border-div">
入职地点
<label className="label-box">
武汉市洪山区野芷湖西路16号创意天地-05办公楼2楼,6
</label>
</div>
<div className="OfferTabs-border-div">
联系人
<label>
<Select
defaultValue={offerdata.hr_name}
disabled={!Offerstate}
style={{
width: 120,
}}
onChange={(e) => {
const hr_name = e.split(',')[0]
const hr_id = e.split(',')[1]
setoffdata({
...offerdata,
hr_name: hr_name,
hr_id:hr_id
})
}}
>
{getnames.map((item, key) => {
return (
<Option key={key} value={item.name + ',' + item._id}>
{item.nickname}
</Option>
)
})}
</Select>
</label>
</div>
<div className="OfferTabs-border-div">
联系电话
<label>
<InputNumber
style={{
width: 120,
}}
maxLength={11}
defaultValue={offerdata.phone}
disabled={!Offerstate}
onChange={(e) => {
setoffdata({
...offerdata,
phone: e,
})
}}
/>
</label>
</div>
<div className="OfferTabs-border-div">
联系邮箱
<label>
<Input
placeholder="请输入邮箱"
defaultValue={offerdata.mail}
disabled={!Offerstate}
onChange={(e) => {
setoffdata({
...offerdata,
mail: e.target.value,
})
}}
/>
</label>
</div>
<p>如有问题请用以上联系方式及时与我们沟通谢谢</p>
<p>系统邮件请勿直接回复</p>
</div>
<div className='generate-box'>
{/* <div className="generate-box">
<label>生成Offer附件</label>
<a href='#'>杨女士录用通知函.pdf</a>
<a href="#">杨女士录用通知函.pdf</a>
<div>
<span>下载</span>
<span>预览</span>
</div>
</div>
</div> */}
</div>
</div>
)

View File

@ -1,35 +1,47 @@
import React from 'react'
import React, { useState, useEffect } from 'react'
import './index.css'
import { Timeline, Divider } from 'antd'
import { getoperatelog } from '../../../util/requestURL'
import { Timeline, Divider, message, Empty } from 'antd'
import Item from 'antd/lib/list/Item'
export default function OperatiolistTabs({ data, Tabsnum }) {
const [datalist, setdatalist] = useState([])
useEffect(() => {
if (Tabsnum === '7') {
getoperatelog({ user_id: data }).then(
(res) => {
console.log(res)
setdatalist(res.data)
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
}
}, [Tabsnum])
export default function OperatiolistTabs() {
return (
<div className="OperatiolistTabs-box ">
<Timeline>
<Timeline.Item>
<p className="fontweight">2022-05-15 16:06:35</p>
<p className="">候选人更新信息</p>
<p className="color1">由管理员 王二狗操作</p>
<Divider />
</Timeline.Item>
<Timeline.Item>
<p className="fontweight">2022-05-15 16:06:35</p>
<p className="">候选人更新信息</p>
<p className="color1">由管理员 王二狗操作</p>
<Divider />
</Timeline.Item>
<Timeline.Item>
<p className="fontweight">2022-05-15 16:06:35</p>
<p className="">候选人更新信息</p>
<p className="color1">由管理员 王二狗操作</p>
<Divider />
</Timeline.Item>
<Timeline.Item>
<p className="fontweight">2022-05-15 16:06:35</p>
<p className="">候选人更新信息</p>
<p className="color1">由管理员 王二狗操作</p>
{datalist.length > 0 ? (
datalist.map((item, key) => {
return (
<Timeline.Item key={key}>
<p className="fontweight">{item.times}</p>
<p className="">{item.content}</p>
<p className="color1">
{item.why} {item.who}操作
</p>
<Divider />
</Timeline.Item>
)
})
) : (
<div className="Empty-box2">
<Empty description="暂无数据..." />
</div>
)}
</Timeline>
</div>
)

View File

@ -1,70 +1,91 @@
import React from 'react'
import React, { useEffect, useState } from 'react'
import { findremark } from '../../../util/requestURL'
import Remarks from '../../Remarks'
import './index.css'
import { Checkbox, Button } from 'antd'
import { Checkbox, Button , Empty } from 'antd'
export default function RemarksTabs() {
const onChange=(e)=>{
console.log(e);
export default function RemarksTabs({ uid, interview_stage,Tabsnum }) {
const [remarklist, setremarklist] = useState([])
const [isremarks, setisremarks] = useState(false)
useEffect(() => {
if (uid != '' && Tabsnum === '5') {
handelFindremark()
}
}, [Tabsnum])
//
const handelFindremark = () => {
findremark({ remark_uid: uid, where: {} })
.then((res) => {
// console.log(res)
setremarklist(res.data)
})
.catch((err) => {})
}
const onChange = (e) => {
console.log(e)
}
//
const onClose = () => {
setisremarks(false)
}
return (
<div>
<div className="RemarksTabs-top-box">
<Checkbox onChange={onChange}>设为私密</Checkbox>
<Button type="primary">添加</Button>
<Button
type="primary"
onClick={() => {
setisremarks(true)
}}
>
添加
</Button>
</div>
<div className='remarks-title'>查看备注</div>
<div className='remarks-list-box'>
<div className='remarks-list'>
<div className='head-box'></div>
<div className='remarks-list-right-box'>
<div className='remarks-list-userinfo-box'>
<p className='remarks-list-name'>刘大哥 </p>
<div className='operation-box'>
<Button type="link" danger>删除</Button>
<p className='remarks-list-time'>2022-07-07</p>
<div className="remarks-title">查看备注</div>
<div className="remarks-list-box">
{remarklist.length > 0?( remarklist.map((item, key) => {
return (
<div className="remarks-list" key={key}>
<div className="head-box">
{item.remark_from.charAt(0)}
</div>
<div className="remarks-list-right-box">
<div className="remarks-list-userinfo-box">
<p className="remarks-list-name">
{item.remark_from}
</p>
<div className="operation-box">
<Button type="link" danger>
删除
</Button>
<p className="remarks-list-time">
{item.remark_time}
</p>
</div>
</div>
<div className='color1'>
呵呵哈哈哈或或
<div className="color1">{item.comment}</div>
</div>
</div>
)
})):(
<div className='Empty-box2'>
<Empty description="暂无数据..."/>
</div>
)}
</div>
<div className='remarks-list'>
<div className='head-box'></div>
<div className='remarks-list-right-box'>
<div className='remarks-list-userinfo-box'>
<p className='remarks-list-name'>刘大哥 </p>
<div className='operation-box'>
<Button type="link" danger>删除</Button>
<p className='remarks-list-time'>2022-07-07</p>
</div>
</div>
<div className='color1'>
呵呵哈哈哈或或
</div>
</div>
</div>
<div className='remarks-list'>
<div className='head-box'></div>
<div className='remarks-list-right-box'>
<div className='remarks-list-userinfo-box'>
<p className='remarks-list-name'>刘大哥 </p>
<div className='operation-box'>
<Button type="link" danger>删除</Button>
<p className='remarks-list-time'>2022-07-07</p>
</div>
</div>
<div className='color1'>
呵呵哈哈哈或或
</div>
</div>
</div>
</div>
{/* 备注 */}
<Remarks
onClose={onClose}
visible={isremarks}
uid={uid}
remark_stage={interview_stage}
/>
</div>
)
}

View File

@ -16,9 +16,10 @@
display: flex;
padding: 50px 0 10px 0;
}
.resume-head { width: 90px; height: 90px; overflow: hidden; border-radius: 50%; border: 1px solid #000; margin-left: 24px; box-shadow: 0 0px 10px rgba(000, 000, 000, 0.2); }
.resume-head { width: 90px; height: 90px; overflow: hidden; border-radius: 50%; border: 0px solid #000; margin-left: 24px; box-shadow: 0 0px 10px rgba(000, 000, 000, 0.2); }
.resume-head img { width: 100%; }
.resume-info { margin-left: 14px; }
.resume-info-name { font-size: 22px; font-weight: bold; margin-bottom: 30px; }
.resume-info-name { font-size: 22px; font-weight: bold; margin-bottom: 0px; }
.xian { width: 60px; height: 4px; background-color: #dddfe0; }
.personal-information-box {margin: 20px 100px; }
/* .personal-info-box { padding-bottom: 30px; } */

View File

@ -1,11 +1,14 @@
import React, { useState, useEffect } from 'react'
import { Button, Form, Input, Modal, Tabs, Badge } from 'antd'
import { Button, Form, Input, Modal, Tabs, Badge, message, Avatar } from 'antd'
import Steptone from '../Steptone'
import InterviewTabs from './InterviewTabs'
import OperatiolistTabs from './OperatiolistTabs'
import EnclosureTabs from './EnclosureTabs'
import RemarksTabs from './RemarksTabs'
import OfferTabs from './OfferTabs'
import Eliminate from '../Eliminate'
import Remarks from '../Remarks'
import Feedback from './Feedback'
import './index.css'
import {
UserOutlined,
@ -18,6 +21,7 @@ import {
EditOutlined,
CloseOutlined,
} from '@ant-design/icons'
import { findcriterion, interviewupdate } from '../../util/requestURL'
export default function InterviewerInfoPop({
visible,
@ -28,9 +32,60 @@ export default function InterviewerInfoPop({
}) {
const [form] = Form.useForm()
const { TabPane } = Tabs
const [candidateinfo, setcandidateinfo] = useState({})
const [iseliminate, setiseliminate] = useState(false)
const [isremarks, setisremarks] = useState(false)
const [Tabsnum, setTabsnum] = useState(0)
const [isfeedback, setisfeedback] = useState(false)
const onChange = (key) => {
console.log(key)
// console.log(key)
setTabsnum(key)
}
useEffect(() => {
if (data != 0) {
postfindcriterion()
}
}, [visible])
const postfindcriterion = () => {
findcriterion({ uid: data }).then(
(res) => {
setcandidateinfo(res.data)
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
}
//
const handelNextstage = () => {
// console.log(candidateinfo)
const interview_stage =
parseInt(candidateinfo.interview_stage) + parseInt(1)
interviewupdate({
data_in: { interview_stage: interview_stage },
interview_query: { uid: candidateinfo.uid },
}).then(
(res) => {
if (res.msg === 'ok') {
message.success('操作成功!')
setTimeout(() => {
postfindcriterion()
}, 2000)
}
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
}
//
const onClose = () => {
setisremarks(false)
}
return (
@ -41,22 +96,28 @@ export default function InterviewerInfoPop({
footer={null}
closable={false}
width={1200}
destroyOnClose={true}
>
<div className="InterviewerInfoPop-box">
<div className="InterviewerInfoPop-top-box">
<div className="">
<div className="divdisplay">
<span className="InterviewerInfoPop-name coloc5">
张三
{candidateinfo.name}
</span>
{candidateinfo.counts > 1 ? (
<div className="InterviewerInfoPop-label color6 border6">
</div>
) : (
''
)}
<div className="InterviewerInfoPop-label color3 border3">
<UserOutlined />
</div>
<div className="InterviewerInfoPop-apply-num">
已申请1
已申请{candidateinfo.counts}
</div>
</div>
<div
@ -64,75 +125,136 @@ export default function InterviewerInfoPop({
style={{ marginTop: 10 }}
>
<div className="color1 font-size14 marginright10">
<UserOutlined /> · 24
<UserOutlined /> {candidateinfo.gender} ·{' '}
{candidateinfo.age}
</div>
<div className="color1 font-size14 marginright10">
<PhoneOutlined /> 15369874569(上海)
<PhoneOutlined /> {candidateinfo.phone}
</div>
<div className="color1 font-size14 marginright10">
<MailOutlined /> 212365748@qq.com
<MailOutlined /> {candidateinfo.mail}
</div>
<div className="color1 font-size14 marginright10">
<ShoppingOutlined /> 1
<ShoppingOutlined />{' '}
{candidateinfo.work_exp === 0
? '应届生'
: candidateinfo.work_exp === 1
? '1-3年'
: candidateinfo.work_exp === 2
? '3-5年'
: candidateinfo.work_exp === 3
? '5年以上'
: ''}
</div>
<div className="color1 font-size14 marginright10">
<ReadOutlined /> 硕士
<ReadOutlined />{' '}
{candidateinfo.education === 1
? '专科'
: candidateinfo.education === 2
? '本科'
: candidateinfo.education === 3
? '硕士'
: candidateinfo.education === 2
? '博士'
: '未填学历'}
</div>
<div className="color1 font-size14 marginright10">
<SendOutlined /> 湖北省-武汉市
<SendOutlined /> {candidateinfo.account}
</div>
</div>
</div>
<div className="operationbutton">
<div className="">
<DeleteOutlined />
</div>
{onEditresume ? (
<div className="">
<EditOutlined
onClick={() => onEditresume({ data })}
/>
</div>
) : (
''
)}
<div className="">
<CloseOutlined onClick={onCreate} />
</div>
</div>
</div>
<div className="divdisplay">
<div className="divdisplay2">
<div style={{ width: '850px' }}>
<Tabs defaultActiveKey="1" onChange={onChange}>
<TabPane tab="基本信息" key="1">
<div className="information-box">
<div className="resume-top-info">
<div className="resume-head"></div>
<div className="resume-head">
{candidateinfo.head ? (
<img
src={candidateinfo.head}
></img>
) : (
<Avatar
size={90}
icon={<UserOutlined />}
/>
)}
</div>
<div className="resume-info">
<div className="resume-info-name coloc5">
张三
{candidateinfo.name}
</div>
<div>
<span className="fontsize14 color1 marginright10">
{' '}
<PhoneOutlined className="marginright5" />{' '}
<PhoneOutlined className="marginright5" />
<span>
15369874569(上海)
</span>{' '}
{
candidateinfo.phone
}
</span>
</span>
<span className="fontsize14 color1">
{' '}
<MailOutlined className="marginright5" />{' '}
<MailOutlined className="marginright5" />
<span>
212365748@qq.com
</span>{' '}
{candidateinfo.mail}
</span>
</span>
</div>
<div className="fontsize14 color1 margintop5">
·24·1年工作经验·硕士·湖北-武汉
{candidateinfo.gender}·
{candidateinfo.age}·
{candidateinfo.work_exp ===
0
? '应届生'
: candidateinfo.work_exp ===
1
? '1-3年'
: candidateinfo.work_exp ===
2
? '3-5年'
: candidateinfo.work_exp ===
3
? '5年以上'
: ''}
·
{candidateinfo.education ===
1
? '专科'
: candidateinfo.education ===
2
? '本科'
: candidateinfo.education ===
3
? '硕士'
: candidateinfo.education ===
2
? '博士'
: '未填学历'}
·{candidateinfo.account}
</div>
</div>
</div>
<div className="fontsize24 fontweight margintop50 coloc5">
<div className="fontsize22 fontweight margintop50 coloc5">
个人信息
<div className="xian margintop20"></div>
</div>
@ -142,22 +264,41 @@ export default function InterviewerInfoPop({
最近公司
</label>
<p className="coloc5">
武汉掌游科技有限公司
{candidateinfo.firm}
</p>
</div>
<div className="divdisplay personal-information-box lineheight24">
<label className="color1 marginright24">
毕业院校
</label>
<p className="coloc5">
{candidateinfo.at_school}{' '}
{candidateinfo.school}{' '}
{candidateinfo.specialty}
</p>
</div>
</div>
<div className="fontsize24 fontweight margintop50 coloc5">
<div className="fontsize22 fontweight margintop50 coloc5">
工作经历
<div className="xian margintop20"></div>
</div>
<div className="personal-info-box">
<div>
{candidateinfo.work_list
? candidateinfo.work_list.map(
(item, key) => {
return (
<div key={key}>
<div className="work-experience-box margintop20 divdisplay">
<label className="color7 marginright24">
2122-11 - 2022-02
{
item.time
}
</label>
<div className="coloc5 fontweight">
武汉xxx有限公司
{
item.company_name
}
</div>
</div>
<div className="divdisplay personal-information-box lineheight24">
@ -165,7 +306,9 @@ export default function InterviewerInfoPop({
职位名称
</label>
<p className="coloc5">
计算会计
{
item.position_name
}
</p>
</div>
<div className="divdisplay personal-information-box lineheight24">
@ -173,18 +316,38 @@ export default function InterviewerInfoPop({
工作职责
</label>
<p className="coloc5">
安守本分挥洒的回访客户萨迪克返回空上岛咖啡哈师大放开手大富科技阿士大夫卡啥打开就发生接口黄卡就收到回复交换空间撒大黄蜂尽快哈萨克东方红卡上的反馈
{
item.duty
}
</p>
</div>
</div>
)
}
)
: ''}
</div>
<div>
<div className="fontsize22 fontweight margintop50 coloc5">
项目经历
<div className="xian margintop20"></div>
</div>
<div className="personal-info-box">
{candidateinfo.project_undergo
? candidateinfo.project_undergo.map(
(item, key) => {
return (
<div key={key}>
<div className="work-experience-box margintop20 divdisplay">
<label className="color7 marginright24">
2122-11 - 2022-02
{
item.time
}
</label>
<div className="coloc5 fontweight">
武汉xxx有限公司
{
item.name
}
</div>
</div>
<div className="divdisplay personal-information-box lineheight24">
@ -192,7 +355,19 @@ export default function InterviewerInfoPop({
职位名称
</label>
<p className="coloc5">
计算会计
{
item.work
}
</p>
</div>
<div className="divdisplay personal-information-box lineheight24">
<label className="color1 marginright24">
项目介绍
</label>
<p className="coloc5">
{
item.comment
}
</p>
</div>
<div className="divdisplay personal-information-box lineheight24">
@ -200,30 +375,142 @@ export default function InterviewerInfoPop({
工作职责
</label>
<p className="coloc5">
安守本分挥洒的回访客户萨迪克返回空上岛咖啡哈师大放开手大富科技阿士大夫卡啥打开就发生接口黄卡就收到回复交换空间撒大黄蜂尽快哈萨克东方红卡上的反馈
{
item.duty
}
</p>
</div>
</div>
)
}
)
: ''}
</div>
<div className="fontsize22 fontweight margintop50 coloc5">
语言能力
<div className="xian margintop20"></div>
</div>
<div className="personal-info-box">
{candidateinfo.language_list
? candidateinfo.language_list.map(
(item, key) => {
return (
<div key={key}>
<div className="divdisplay personal-information-box lineheight24">
<label className="color1 marginright24">
语言类型
</label>
<p className="coloc5">
{item.language_name !=
''
? item.language_name
: '未填写'}
</p>
</div>
<div className="divdisplay personal-information-box lineheight24">
<label className="color1 marginright24">
掌握程度
</label>
<p className="coloc5">
{item.has_sleep !=
''
? item.has_sleep
: '未填写'}
</p>
</div>
<div className="divdisplay personal-information-box lineheight24">
<label className="color1 marginright24">
听说
</label>
<p className="coloc5">
{item.reading !=
''
? item.reading
: '未填写'}
</p>
</div>
<div className="divdisplay personal-information-box lineheight24">
<label className="color1 marginright24">
读写
</label>
<p className="coloc5">
{item.writing !=
''
? item.writing
: '未填写'}
</p>
</div>
</div>
)
}
)
: ''}
</div>
<div className="fontsize22 fontweight margintop50 coloc5">
获奖经历
<div className="xian margintop20"></div>
</div>
<div className="personal-info-box">
{candidateinfo.remembrance_list
? candidateinfo.remembrance_list.map(
(item, key) => {
return (
<div key={key}>
<div className="divdisplay personal-information-box lineheight24">
<label className="color1 marginright24">
获奖时间
</label>
<p className="coloc5">
{item.prize_time !=
''
? item.prize_time
: '未填写'}
</p>
</div>
<div className="divdisplay personal-information-box lineheight24">
<label className="color1 marginright24">
获奖名称
</label>
<p className="coloc5">
{item.prize_name !=
''
? item.prize_name
: '未填写'}
</p>
</div>
</div>
)
}
)
: ''}
</div>
</div>
</TabPane>
<TabPane tab="面试" key="2">
<div className="information-box">
<InterviewTabs />
<InterviewTabs
data={candidateinfo}
Tabsnum={Tabsnum}
setisfeedback={() => {
setisfeedback(true)
}}
seefeedback={() => {
setisfeedback(true)
postfindcriterion()
}}
/>
</div>
</TabPane>
<TabPane tab="考试/测评" key="3">
考试/测评
</TabPane>
<TabPane tab="Offer/录用" key="4">
<OfferTabs/>
<OfferTabs data={candidateinfo} />
</TabPane>
<TabPane
tab={
<Badge
size="small"
count="2"
count="0"
color={'blue'}
>
<span>备注</span>
@ -231,35 +518,122 @@ export default function InterviewerInfoPop({
}
key="5"
>
<RemarksTabs/>
<RemarksTabs
uid={candidateinfo.uid}
interview_stage={
candidateinfo.interview_stage
}
Tabsnum={Tabsnum}
/>
</TabPane>
<TabPane tab="附加信息" key="6">
<EnclosureTabs/>
<EnclosureTabs data={candidateinfo.uid} Tabsnum={Tabsnum}/>
</TabPane>
<TabPane tab="操作记录" key="7">
<OperatiolistTabs />
<OperatiolistTabs
data={candidateinfo.uid}
Tabsnum={Tabsnum}
/>
</TabPane>
</Tabs>
</div>
<div className="side-action-bar-box">
<div className="progress-bar-box">
{/* 步骤条组件 */}
<Steptone data="1" />
<Steptone
data={candidateinfo.interview_stage}
/>
</div>
<div className="button-box">
<Button type="primary" block>
{onEditresume ? (
<Button
type="primary"
block
onClick={handelNextstage}
disabled={candidateinfo.interview_stage === 3 && candidateinfo.teacher_state !== 2? true:false}
>
进入下一阶段
</Button>
<Button block>淘汰</Button>
<Button block>关注</Button>
<Button block>备注</Button>
) : (
''
)}
{candidateinfo.teacher_state != 2 ? (
<Button
block
onClick={() => {
setisfeedback(true)
postfindcriterion()
}}
>
填写反馈
</Button>
) : (
<Button
block
onClick={() => {
setisfeedback(true)
postfindcriterion()
}}
>
修改反馈
</Button>
)}
<Button
block
onClick={() => {
setiseliminate(true)
}}
>
淘汰
</Button>
<Button
block
onClick={() => {
setisremarks(true)
}}
>
备注
</Button>
</div>
</div>
</div>
</div>
</Modal>
{/* 淘汰 */}
<Eliminate
visible={iseliminate}
onCreate={() => {
postfindcriterion()
setiseliminate(false)
}}
uid={candidateinfo.uid}
remark_stage={candidateinfo.interview_stage}
onCancel={() => {
setiseliminate(false)
}}
/>
{/* 备注 */}
<Remarks
onClose={onClose}
visible={isremarks}
uid={candidateinfo.uid}
remark_stage={candidateinfo.interview_stage}
/>
{/* 反馈 */}
<Feedback
visible={isfeedback}
uid={candidateinfo.uid}
onClose={() => {
setisfeedback(false)
postfindcriterion()
}}
/>
</div>
)
}

View File

@ -1,8 +1,8 @@
import React, { Component, useState } from 'react'
import ResumeUploadPop from '../ResumeUploadPop'
import InterviewUploadPop from '../InterviewUploadPop'
import './index.css'
import { Input, Space, Button, Badge, Menu, Dropdown } from 'antd'
import { Input, Space, Button, Badge, Menu, Dropdown, message } from 'antd'
import storageUtils from '../../util/storageUtils'
import {
ShopOutlined,
QuestionCircleOutlined,
@ -19,51 +19,34 @@ const { Search } = Input
// />
// )
const menu = (
<Menu
items={[
{
key: '1',
label: <p>1st menu item</p>,
},
{
key: '2',
label: <p>2nd menu item</p>,
},
{
key: '3',
label: <p>退出登录</p>,
},
]}
/>
)
const onSearch = (value) => console.log(value)
export default class NavHeader extends Component {
state = { component: '' }
componentDidMount(){
handelLogout=()=>{
storageUtils.removerUser()
window.location.reload();
}
handeluserinfo=()=>{
componentWillUpdate(){
const { headnav } = this.props
// console.log('NavHeader')
// switch (headnav) {
// case 'position':
// this.setState({
// component: <ResumeUploadPop />,
// })
// break
// case 'interview':
// this.setState({
// component: <InterviewUploadPop />,
// })
// break
// }
message.success("该功能暂未开放")
}
render() {
const { headnav } = this.props
const { userInfo } = this.props
const menu = (
<Menu
items={[
{
key: '2',
label: <span onClick={this.handeluserinfo}>个人信息</span>,
},
{
key: '3',
label: <span onClick={this.handelLogout}>退出登录</span>,
},
]}
/>
)
return (
<div className="navheader-box">
<div className="navheader-biv">
@ -77,7 +60,9 @@ export default class NavHeader extends Component {
/>
</Space>
</div>
<div className="navheader-biv">{headnav== 'position'? <ResumeUploadPop />: <InterviewUploadPop />}</div>
<div className="navheader-biv">
<ResumeUploadPop />
</div>
<div className="navheader-biv">
<ShopOutlined
style={{ fontSize: '20px', color: '#ff6b6b' }}
@ -94,7 +79,7 @@ export default class NavHeader extends Component {
</div>
<div className="navheader-biv">
<Dropdown overlay={menu} placement="bottomLeft" arrow>
<div className="headimg"></div>
<div className="headimg">{userInfo.name? userInfo.name.charAt(0): '' }</div>
</Dropdown>
</div>
</div>

View File

View File

@ -0,0 +1,53 @@
import React, { Component } from 'react'
import { Document, Page } from 'react-pdf'
import axios from 'axios'
export default class Originalresume extends Component {
state = {
numPages: 0,
pageNumber: 1,
}
onDocumentLoad = ({ numPages }) => {
this.setState({ numPages })
}
render() {
const { pageNumber, numPages } = this.state
const { data } = this.props
var url = ""
if(JSON.stringify(data) != "{}"){
const urlarr = data.file_url.split('hrms/')
// url = 'http://localhost:3000/pdf/hrms/' + decodeURIComponent(urlarr[1])
url = 'http://10.0.0.4/pdf/hrms/' + decodeURIComponent(urlarr[1])
}
return (
<div>
{
url !== ""?(<Document
file={url}
// file={data.file_url}
onLoadSuccess={this.onDocumentLoad}
onLoadError={() => {
console.log('CALLED')
}}
onLoadProgress={() => {
console.log('PRGREOSS')
}}
>
{
new Array(numPages).fill('').map((item,key)=>{
return <Page key={key+1} pageNumber={key+1} scale={1.3}/>
})
}
</Document>):''
}
</div>
)
}
}

View File

@ -0,0 +1 @@
.Tips { font-size: 12px; color: #75777d; }

View File

@ -0,0 +1,118 @@
import React, { useEffect, useState } from 'react'
import { Form, Input, Modal, Radio, Select, message } from 'antd'
import { getname, sendinterviewee } from '../../util/requestURL'
import './index.css'
const { Option } = Select
const { TextArea } = Input
export default function Eliminate(props) {
const [form] = Form.useForm()
const [getnamedata, setgetnamedata] = useState([])
const { visible, onCreate, onCancel, uid } = props
useEffect(() => {
if(uid){
getname().then(
(res) => {
console.log(res.data)
setgetnamedata(res.data)
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
}
}, [visible])
return (
<div>
<Modal
visible={visible}
title="推荐给用人部门"
okText="确认"
cancelText="取消"
onCancel={onCancel}
onOk={() => {
form.validateFields()
.then((values) => {
form.resetFields()
var data = values;
data['uid'] = uid
sendinterviewee(data).then(
(res) => {
if(res.msg === "ok"){
message.success("推荐成功!")
onCreate();
}
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
})
.catch((info) => {
console.log('Validate Failed:', info)
})
}}
>
<Form form={form} layout="vertical" name="form_in_modal">
<p>
重新推荐会使前一次推荐取消且前一次的推荐链接无法操作
</p>
<Form.Item
name="push"
label="推荐到"
rules={[
{
required: true,
message: '请选择淘汰原因!',
},
]}
>
<Select mode="multiple">
{getnamedata.map((item, key) => {
return item.rank === 2 ? (
<Option value={item.user_id} key={item._id}>
{item.name}
</Option>
) : (
''
)
})}
</Select>
</Form.Item>
<span className="Tips">
一次推荐简历给多位面试官在面试官全部反馈后需HR判断候选人是否进入面试
</span>
<Form.Item name="content" label="告诉用人部门">
<TextArea
rows={4}
placeholder="例如:大公司从业经历,有成功产品案例"
/>
</Form.Item>
<Form.Item
name="bcc"
label="抄送"
rules={[
{
required: true,
message: '请选择抄送人账号!',
},
]}
>
<Select mode="multiple">
{getnamedata.map((item, key) => {
return (
<Option value={item.user_id} key={item._id}>
{item.name}
</Option>
)
})}
</Select>
</Form.Item>
</Form>
</Modal>
</div>
)
}

View File

@ -0,0 +1,6 @@
.Remarks-box .remarks-list-box{
margin: 0;
}
.Remarks-box .RemarksTabs-top-box {
margin: 24px 0;
}

View File

@ -0,0 +1,167 @@
import React, { useEffect, useState } from 'react'
import { Form, Drawer, Button, Checkbox, Input, message, Empty } from 'antd'
import {
findremark,
addremark,
operatelog,
interviewupdate,
} from '../../util/requestURL'
import storageUtils from '../../util/storageUtils'
import './index.css'
const { TextArea } = Input
const { form } = Form
export default function Remarks(props) {
const { onClose, visible, uid, remark_stage } = props
const [remarklist, setremarklist] = useState([])
let userinfo = storageUtils.getUser()
useEffect(() => {
if (uid != '' && uid) {
handelFindremark()
}
}, [visible])
//
const handelFindremark = () => {
findremark({ remark_uid: uid, where: {} })
.then((res) => {
setremarklist(res.data)
})
.catch((err) => {})
}
//
const handelAddremark = (values) => {
if (values.stage_num == undefined) {
values.stage_num = 0
} else if (values.stage_num) {
values.stage_num = 1
} else {
values.stage_num = 0
}
addremark({
remark_from: userinfo.name,
remark_uid: uid,
comment: values.remark,
stage_num: values.stage_num,
remark_stage: remark_stage,
})
.then((res) => {
if (res.code == 200) {
if (remark_stage === 5) {
interviewupdate({
data_in: { interview_stage: 11 }, //
interview_query: { uid: uid },
data: { interview_stage: remark_stage },
find_column: [],
}).then(
(res) => {
// console.log(res)
message.success('操作成功!')
handelFindremark()
operatelog({
user_id: uid,
why: '淘汰候选人',
content:'对方放弃入职机会',
})
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
} else {
message.success('添加成功!')
handelFindremark()
operatelog({
user_id: uid,
why: '添加备注',
content: values.remark,
})
}
}
})
.catch((err) => {
message.error('网络加载错误,请稍后再试')
})
}
return (
<div>
<Drawer
title="备注"
placement="right"
onClose={onClose}
visible={visible}
width={520}
>
<div className="Remarks-box">
<Form onFinish={handelAddremark}>
<Form.Item
name="remark"
rules={[
{
required: true,
message: '请填写备注!',
},
]}
>
<TextArea
rows={4}
placeholder="请输入备注,支持@提醒他人"
/>
</Form.Item>
<div className="RemarksTabs-top-box">
<Form.Item name="stage_num" valuePropName="checked">
<Checkbox>设为私密</Checkbox>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
添加
</Button>
</Form.Item>
</div>
</Form>
<div className="remarks-list-box">
{remarklist.length > 0 ? (
remarklist.map((item, key) => {
return (
<div className="remarks-list" key={key}>
<div className="head-box">
{item.remark_from.charAt(0)}
</div>
<div className="remarks-list-right-box">
<div className="remarks-list-userinfo-box">
<p className="remarks-list-name">
{item.remark_from}
</p>
<div className="operation-box">
<Button type="link" danger>
删除
</Button>
<p className="remarks-list-time">
{item.remark_time}
</p>
</div>
</div>
<div className="color1">
{item.comment}
</div>
</div>
</div>
)
})
) : (
<div className="margin24">
<Empty description="暂无数据" />
</div>
)}
</div>
</div>
</Drawer>
</div>
)
}

View File

@ -1,30 +1,98 @@
import React, { useState } from 'react'
import { Button } from 'antd'
import ResumeUpload from '../../pages/ResumeUpload'
import Uploadresume from '../Uploadresume'
import { Button, Dropdown, Menu } from 'antd'
import { useNavigate } from 'react-router-dom'
import { PlusOutlined } from '@ant-design/icons'
export default function ResumeUploadPop() {
const navigate = useNavigate()
const [visible, setVisible] = useState(false)
const [usvisible, setusvisible] = useState(false)
//
const [data, setdata] = useState([])
const onCreate = (values) => {
console.log('Received values of form: ', values)
setVisible(false)
}
const UsonCreate = (values) => {
setusvisible(false)
}
const onMenuClick = (e) => {
console.log('click', e)
if (e.key == 1) {
setVisible(true)
} else if (e.key == 2) {
//
navigate('/admin/position/addposition')
} else if (e.key == 4) {
setusvisible(true)
}
}
const menu = (
<Menu
onClick={onMenuClick}
items={[
// {
// key: '1',
// label: '',
// },
{
key: '4',
label: '上传简历',
},
{
key: '2',
label: '添加职位',
},
// {
// key: '3',
// label: '',
// },
]}
/>
)
return (
<div>
<Button
type="primary"
onClick={() => {
setVisible(true)
}}
>
上传简历
<Dropdown overlay={menu} placement="bottomLeft">
<Button type="primary">
<PlusOutlined />
上传
</Button>
</Dropdown>
<ResumeUpload
visible={visible}
onCreate={onCreate}
data={data}
onCancel={() => {
setVisible(false)
}}
onReupload={() => {
//
setVisible(false)
setusvisible(true)
}}
/>
<Uploadresume
visible={usvisible}
onCreate={UsonCreate}
onResumeUpload={(data) => {
setusvisible(false)
setVisible(true)
setdata(data)
}}
onCancel={() => {
setusvisible(false)
}}
/>
</div>
)

View File

@ -1,7 +1,10 @@
.progress-bar-box { background-color: #edeff1; height: 70px; padding: 24px; position: relative; }
.progress-bar-bg { width: 100%; height: 6px; background-color: #fff; }
.progress-bar-bg { width: 100%; height: 6px; background-color: #fff; overflow: hidden; }
.progress-bar-yuan-mr { width: 100%; display: flex; justify-content: space-between; position: relative; top: -13px; }
.progress-bar-yuan-mr div { width: 20px; height: 20px; border-radius: 50%; background-color: #fff; text-align: center; line-height: 20px; color: #75777d; }
.progress-bar-yuan-mr div { width: 20px; height: 20px; border-radius: 50%; background-color: #fff; text-align: center; line-height: 20px; color: #75777d; cursor: pointer; }
.progress-bar-complete { width: 23%; height: 6px; background-color: #40a9ff; }
.progress-bar-yuan-complete { background-color: #40a9ff !important; color: #fff !important; }
.progress-bar-yuan-complete { background-color: #40a9ff !important; color: #fff !important; cursor: pointer; }
.progress-bar-complete-title { width: 60px; text-align: center; position: relative; left: -20px; top: -10px; font-size: 12px; color: #75777d; }
.hover-show-title { display: flex;align-items: center; position: absolute; top: 40px; font-size: 12px; }
.hover-show-title div { position: relative;}

View File

@ -2,16 +2,48 @@ import React, { Component } from 'react'
import './index.css'
export class Steptone extends Component {
state = {
isshow1: false,
isshow2: false,
isshow3: false,
isshow4: false,
isshow5: false,
}
handlMouseLeave = (key) => {
this.setState({
[key]: false,
})
}
handlMouseEnter = (key) => {
this.setState({
[key]: true,
})
}
render() {
const { data } = this.props //
let { data } = this.props //
const { isshow1, isshow2, isshow3, isshow4, isshow5 } = this.state
if (data === 11) {
data = 6
}
const dataArr = [1, 2, 3, 4, 5] //
const completetitlearr = [-20, 33, 84, 133, 185] //
const completetitlearr = [-20, 38, 96, 155, 215, 215] //
const titarr = ['初筛', '复筛', '面试', '沟通Offer', '待入职', '已淘汰']
return (
<div>
<div className="progress-bar-bg">
<div
className="progress-bar-complete"
style={{ width: (data - 1) * 23 + '%' }}
style={{
width:
((data - 1) * 23 > 100
? 100
: (data - 1) * 23) < 0
? 0
: (data - 1) * 23 + '%',
}}
></div>
</div>
<div className="progress-bar-yuan-mr">
@ -24,6 +56,12 @@ export class Steptone extends Component {
: ''
}
key={i}
onMouseLeave={() => {
this.handlMouseLeave('isshow' + i)
}}
onMouseEnter={() => {
this.handlMouseEnter('isshow' + i)
}}
>
{i}
</div>
@ -31,11 +69,58 @@ export class Steptone extends Component {
})}
</div>
{/* -20 40 155 210 */}
<div className="hover-show-title">
<div
className="hover-show"
style={{
display: isshow1 ? 'block' : 'none',
left: '-2px',
}}
>
初筛
</div>
<div
className="hover-show"
style={{
display: isshow2 ? 'block' : 'none',
left: '56px',
}}
>
复筛
</div>
<div
className="hover-show"
style={{
display: isshow3 ? 'block' : 'none',
left: '114px',
}}
>
面试
</div>
<div
className="hover-show"
style={{
display: isshow4 ? 'block' : 'none',
left: '160px',
}}
>
沟通Offer
</div>
<div
className="hover-show"
style={{
display: isshow5 ? 'block' : 'none',
left: '227px',
}}
>
待入职
</div>
</div>
<div
className="progress-bar-complete-title"
style={{ left: completetitlearr[data - 1] }}
>
沟通Offer
{titarr[data - 1]}
</div>
</div>
)

View File

View File

@ -0,0 +1,222 @@
import React, { Component, useState } from 'react'
import {
message,
Upload,
Modal,
Spin,
Alert,
Select,
Row,
Col,
Button,
} from 'antd'
import {
InboxOutlined,
ExclamationCircleOutlined,
UploadOutlined,
} from '@ant-design/icons'
import { interviewfileinsert, filetohw } from '../../util/requestURL'
import memoryUtils from '../../util/memoryUtils'
import './index.css'
const { Dragger } = Upload
const { confirm } = Modal
const { Option } = Select
export default class Candidate extends Component {
state = {
loading: false,
resume_affix_id: '',
owner_name: 0,
}
render() {
const { visible, onCancel, onCreate, onResumeUpload } = this.props
let userInfo = memoryUtils.userInfo
const token = userInfo.token_type + ' ' + userInfo.token || ''
const that = this
const props = {
name: 'file',
multiple: true,
action: 'http://10.0.0.240:7800/api/v1/itr/file_to_hw',
// action: 'http://myip.legu.cc:7800/api/v1/itr/file_to_hw',
// action: 'http://10.0.0.4:7800/api/v1/itr/file_to_hw',
headers: {
Authorization: token,
},
onChange(info) {
const { status } = info.file
if (status == 'done') {
that.setState(
{
loading: true,
},
() => {
that.setState({
loading: false,
})
if (
info.file.response.data !== null &&
info.file.response.data.length !== 0
) {
if (info.file.response.data.exist === 1) {
showPromiseConfirm(info.file.response.data)
} else {
const candidatedata =
info.file.response.data
candidatedata.data['uid'] =
info.file.response.data.uid
candidatedata.data['owner_name'] =
this.state.owner_name
candidatedata.data['resume_affix_id'] =
this.state.resume_affix_id
that.props.onResumeUpload(candidatedata)
}
} else {
message.error(info.file.response.msg)
}
}
)
}
if (status === 'done') {
message.success(`${info.file.name} 已成功上传.`)
} else if (status === 'error') {
message.error(`${info.file.name} 上传失败.`)
}
},
onDrop(e) {
console.log('Dropped files', e.dataTransfer.files)
},
}
const showPromiseConfirm = (data) => {
var that = this;
confirm({
title: '提示',
icon: <ExclamationCircleOutlined />,
content: '当前简历以添加,是否覆盖',
okText: '确认',
cancelText: '取消',
onOk() {
// console.log(data)
const candidatedata = data
candidatedata.data['uid'] = data.uid
candidatedata.data['owner_name'] = that.state.owner_name
candidatedata.data['resume_affix_id'] =
that.state.resume_affix_id
that.props.onResumeUpload(candidatedata)
},
onCancel() {},
})
}
const propss = {
name: 'file',
multiple: true,
action: 'http://10.0.0.240:7800/api/v1/itr/resume_affix',
// action: 'http://myip.legu.cc:7800/api/v1/itr/resume_affix',
// action: 'http://10.0.0.4:7800/api/v1/itr/resume_affix',
headers: {
Authorization: token,
},
onChange(info) {
if (info.file.status !== 'uploading') {
const data = JSON.parse(info.file.response.data)
this.setState({
resume_affix_id: data.file_url,
})
}
if (info.file.status === 'done') {
message.success(
`${info.file.name} file uploaded successfully`
)
} else if (info.file.status === 'error') {
message.error(`${info.file.name} file upload failed.`)
}
},
}
return (
<div>
<Modal
visible={visible}
okText="确定"
cancelText="取消"
onCancel={onCancel}
closable={false}
maskClosable={false}
destroyOnClose={true}
onOk={() => {}}
>
<Row gutter={24} align="baseline">
<Col span={24} className="marginbottom10">
<label className="from-label">选择渠道</label>
<Select
defaultValue={0}
onChange={(e) => {
this.setState({
owner_name: e,
})
}}
style={{
width: 240,
marginLeft: 16,
}}
placeholder="请选择简历渠道"
>
<Option value={0}>内推</Option>
<Option value={1}>boss</Option>
<Option value={2}>前程无忧</Option>
<Option value={3}>智联招聘</Option>
<Option value={4}>58同城</Option>
</Select>
</Col>
<Col span={24} className="marginbottom10">
<label className="from-label">上传附件</label>
<Upload {...propss}>
<Button
icon={<UploadOutlined />}
style={{
marginLeft: 16,
}}
>
上传附件
</Button>
</Upload>
</Col>
<Col span={24} className="marginbottom10">
<label className="from-label">上传简历</label>
<Spin
spinning={this.state.loading}
tip="解析简历中请等待..."
>
<Dragger
{...props}
// customRequest={(e)=>{customRequest(e)}}
>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">
单击或拖动文件到此区域上传
</p>
<p className="ant-upload-hint">
支持单次或批量上传严禁
通过上传公司数据或其他频带文件
</p>
</Dragger>
</Spin>
</Col>
</Row>
</Modal>
</div>
)
}
}

BIN
src/images/bg_login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
src/images/left-logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

BIN
src/images/logo_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -3,13 +3,14 @@ import ReactDOM from 'react-dom/client'
import './index.css'
import App from './App'
import reportWebVitals from './reportWebVitals'
import { BrowserRouter } from 'react-router-dom'
import { HashRouter } from 'react-router-dom'
// HashRouter
// BrowserRouter
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<BrowserRouter>
<HashRouter>
<App />
</BrowserRouter>
</HashRouter>
)
reportWebVitals()

View File

@ -1,27 +1,62 @@
import React from 'react';
import { Form, Input, Button, Checkbox } from 'antd';
import {useNavigate} from 'react-router-dom';
import { UserOutlined, LockOutlined } from '@ant-design/icons';
import './login.css'
import { Button, message, Form, Input } from 'antd'
import React from 'react'
import { Login } from '../util/requestURL'
import storageUtils from '../util/storageUtils'
import memoryUtils from '../util/memoryUtils'
import { useNavigate } from 'react-router-dom'
import Redirect from '../routes/Redirect'
import '../pages/Login.css'
export default function Loging() {
const navigate = useNavigate()
//
const userInfo = memoryUtils.userInfo
if (userInfo.user_id) {
return <Redirect to="/" />
}
export default function Login() {
const navigator = useNavigate();
const onFinish = (values) => {
localStorage.setItem('token',"asdasdasd");
navigator('/')
// console.log('Received values of form: ', values);
};
Login(values).then(
(res) => {
storageUtils.saceUser(res.data)
navigate('/')
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
}
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo)
}
return (
<div className='components-form-demo-normal-login'>
<div className="login-box">
<div className='login-boxs'>
<div className="login-left">
</div>
<div className="components-form-demo-normal-login">
<div className='login-logo'></div>
<Form
name="normal_login"
className="login-form"
name="basic"
labelCol={{
span: 8,
}}
wrapperCol={{
span: 16,
}}
initialValues={{
remember: true,
}}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
size={'Large'}
>
<Form.Item
label="账号"
name="username"
rules={[
{
@ -30,14 +65,11 @@ export default function Login() {
},
]}
>
<Input
prefix={
<UserOutlined className="site-form-item-icon" />
}
placeholder="用户名"
/>
<Input style={{height: '40px'}}/>
</Form.Item>
<Form.Item
label="密码"
name="password"
rules={[
{
@ -46,35 +78,22 @@ export default function Login() {
},
]}
>
<Input
prefix={
<LockOutlined className="site-form-item-icon" />
}
type="password"
placeholder="密码"
/>
</Form.Item>
<Form.Item>
<Form.Item name="remember" valuePropName="checked" noStyle>
<Checkbox>记住我</Checkbox>
<Input.Password style={{height: '40px'}}/>
</Form.Item>
{/* <a className="login-form-forgot" href="#">
忘记密码
</a> */}
</Form.Item>
<Form.Item>
<Button
type="primary"
htmlType="submit"
className="login-form-button"
<Form.Item
wrapperCol={{
offset: 8,
span: 16,
}}
>
<Button type="primary" htmlType="submit" block style={{height: '40px'}}>
登录
</Button>
</Form.Item>
</Form>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,9 @@
.StandardResume-box .ant-row { margin-left: 0 !important; margin-right: 0 !important; }
.resume-pop-box .ant-tabs-content-holder { overflow-y: scroll; max-height: calc( 100vh - 375px); }
.from-label {padding: 0 0 8px; line-height: 1.5715; white-space: initial; text-align: left; display: inline-block; margin-top: 12px;}
.textalignright { text-align: right; }
.select-box div {
width: 100%;
}
.StandardResume-box { width: 700px; }

View File

@ -1,10 +1,561 @@
import React from 'react'
import React, { useState, useEffect } from 'react'
import {
Form,
Input,
Row,
Col,
Divider,
Button,
Space,
DatePicker,
Select,
} from 'antd'
import { PlusOutlined, MinusCircleOutlined } from '@ant-design/icons'
import 'moment/locale/zh-cn'
import locale from 'antd/es/date-picker/locale/zh_CN'
import moment from 'moment'
import './index.css'
const dateFormat = 'YYYY-MM-DD'
export default function StandardResume({ data, updataPosition }) {
const [form] = Form.useForm()
const { TextArea } = Input
const { Option } = Select
const { RangePicker } = DatePicker
const typelist = {
work_list: {
company_name: '',
duty: '',
position_name: '',
},
project_undergo: {
time: '',
name: '',
work: '',
comment: '',
duty: '',
},
language: {
language_name: '',
has_sleep: 1,
reading: 1,
writing: 1,
},
remembrance: {
prize_name: '',
prize_time: '',
},
}
const [worklist, setworklist] = useState(data['work_list'])
const [projectundergo, setprojectundergo] = useState(
data['project_undergo']
)
const [language, setlanguage] = useState(data['language_list'])
const [remembrance, setremembrance] = useState(data['remembrance_list'])
useEffect(()=>{
updataPosition({...data.data,work_list:worklist,project_undergo:projectundergo,language_list:language,remembrance_list:remembrance})
},[])
const onValuesChange = (type, e, valuestype, index) => {
switch (type) {
case 'work_list':
const newworklist = worklist.map((item, key) => {
if (index === key)
return { ...item, [valuestype]: e }
else return item
})
setworklist(newworklist)
updataPosition({work_list:newworklist})
break
case 'project_undergo':
const newprojectundergo = projectundergo.map((item, key) => {
if (index === key)
return valuestype != 'time'
? { ...item, [valuestype]: e }
: { ...item, [valuestype]: e }
else return item
})
setprojectundergo(newprojectundergo)
updataPosition({project_undergo:newprojectundergo})
break
case 'language':
const newlanguage = language.map((item, key) => {
if (index === key)
return { ...item, [valuestype]: e }
else return item
})
setlanguage(newlanguage)
updataPosition({language_list:newlanguage})
break
case 'remembrance':
const newremembrance = remembrance.map((item, key) => {
if (index === key)
return valuestype != 'prize_time'
? { ...item, [valuestype]: e }
: { ...item, [valuestype]: e }
else return item
})
setremembrance(newremembrance)
updataPosition({remembrance_list:newremembrance})
break
default:
break
}
}
const add = (type) => {
switch (type) {
case 'work_list':
const newworklist = [...worklist, typelist[type]]
setworklist(newworklist)
break
case 'project_undergo':
const newprojectundergo = [...projectundergo, typelist[type]]
setprojectundergo(newprojectundergo)
break
case 'language':
const newlanguage = [...language, typelist[type]]
setlanguage(newlanguage)
break
case 'remembrance':
const newremembrance = [...remembrance, typelist[type]]
setremembrance(newremembrance)
break
default:
break
}
}
const remove = (type, index) => {
switch (type) {
case 'work_list':
const newworklist = worklist.filter((item, key) => {
return key != index
})
setworklist(newworklist)
break
case 'project_undergo':
const newprojectundergo = projectundergo.filter((item, key) => {
return key != index
})
setprojectundergo(newprojectundergo)
break
case 'language':
const newlanguage = language.filter((item, key) => {
return key != index
})
setlanguage(newlanguage)
break
case 'remembrance':
const newremembrance = remembrance.filter((item, key) => {
return key != index
})
setremembrance(newremembrance)
break
default:
break
}
}
export default function StandardResume() {
return (
<div>
StandardResume
<div className="StandardResume-box">
{/* <Form layout="vertical" onValuesChange={onValuesChange}> */}
<Divider orientation="left">工作经验</Divider>
{worklist.map((item, key) => {
return (
<Row gutter={24} align="baseline" key={key}>
<Col span={11} className="marginbottom10">
<label className="from-label">公司名称</label>
<Input
placeholder="请输入公司名称"
value={item.company_name}
onChange={(e) => {
onValuesChange(
'work_list',
e.target.value,
'company_name',
key
)
}}
/>
</Col>
<Col span={11} className="marginbottom10">
<label className="from-label">职位名称</label>
<Input
placeholder="请输入职位名称"
value={item.position_name}
onChange={(e) => {
onValuesChange(
'work_list',
e.target.value,
'position_name',
key
)
}}
/>
</Col>
<Col span={2} className="textalignright">
<MinusCircleOutlined
onClick={() => remove('work_list', key)}
/>
</Col>
<Col span={24} className="marginbottom10">
<label className="from-label">工作职责</label>
<TextArea
rows={4}
placeholder="请输入工作职责"
value={item.duty}
onChange={(e) => {
onValuesChange(
'work_list',
e.target.value,
'duty',
key
)
}}
/>
</Col>
</Row>
)
})}
<Col span={24}>
<Form.Item>
<Button
type="dashed"
onClick={() => add('work_list')}
block
icon={<PlusOutlined />}
>
添加
</Button>
</Form.Item>
</Col>
<Divider orientation="left">项目经验</Divider>
{projectundergo.map((item, key) => {
return (
<Row key={key} gutter={24} align="baseline">
<Col span={12} className="marginbottom10">
<label className="from-label">起止时间 </label>
<div>
<RangePicker
locale={locale}
picker="month"
value={
item.time.length > 0
?( [
moment(
item.time.split(
'-'
)[0],
dateFormat
),
moment(
item.time.split(
'-'
)[1],
dateFormat
),
])
: ''
}
onChange={(date, dateString) => {
onValuesChange(
'project_undergo',
dateString[0] +
'-' +
dateString[1],
'time',
key
)
}}
/>
</div>
</Col>
<Col span={12} className="textalignright">
<MinusCircleOutlined
onClick={() =>
remove('project_undergo', key)
}
/>
</Col>
<Col span={12} className="marginbottom10">
<label className="from-label">项目名称</label>
<Input
placeholder="参与项目"
value={item.name}
onChange={(e) => {
onValuesChange(
'project_undergo',
e.target.value,
'name',
key
)
}}
/>
</Col>
<Col span={12} className="marginbottom10">
<label className="from-label">职务</label>
<Input
placeholder="请输入职务名称"
value={item.work}
onChange={(e) => {
onValuesChange(
'project_undergo',
e.target.value,
'work',
key
)
}}
/>
</Col>
<Col span={24} className="marginbottom10">
<label className="from-label">项目介绍</label>
<TextArea
rows={4}
placeholder="内容"
value={item.comment}
onChange={(e) => {
onValuesChange(
'project_undergo',
e.target.value,
'comment',
key
)
}}
/>
</Col>
<Col span={24} className="marginbottom10">
<label className="from-label">项目中职责</label>
<TextArea
rows={4}
placeholder="项目中职责"
value={item.duty}
onChange={(e) => {
onValuesChange(
'project_undergo',
e.target.value,
'duty',
key
)
}}
/>
</Col>
</Row>
)
})}
<Col span={24}>
<Form.Item>
<Button
type="dashed"
onClick={() => add('project_undergo')}
block
icon={<PlusOutlined />}
>
添加
</Button>
</Form.Item>
</Col>
<Divider orientation="left">语言能力</Divider>
{language.map((item, key) => {
return (
<Row key={key} gutter={24} align="baseline">
<Col span={11} className="marginbottom10">
<label className="from-label">语言类型</label>
<Input
placeholder="请输入掌握语言类型"
value={item.language_name}
onChange={(e) => {
onValuesChange(
'language',
e.target.value,
'language_name',
key
)
}}
/>
</Col>
<Col span={11} className="marginbottom10">
<label className="from-label">掌握程度</label>
<div className="select-box">
<Select
placeholder="请选择"
value={item.has_sleep}
onChange={(e) => {
onValuesChange(
'language',
e,
'has_sleep',
key
)
}}
>
<Option value={"初级"}>初级</Option>
<Option value={"中级"}>中级</Option>
<Option value={"高级"}>高级</Option>
</Select>
</div>
</Col>
<Col span={2} className="textalignright">
<MinusCircleOutlined
onClick={() => remove('language', key)}
/>
</Col>
<Col span={11} className="marginbottom10">
<label className="from-label">听说</label>
<div className="select-box">
<Select
placeholder="请选择"
value={item.reading}
onChange={(e) => {
onValuesChange(
'language',
e,
'reading',
key
)
}}
>
<Option value={"初级"}>初级</Option>
<Option value={"中级"}>中级</Option>
<Option value={"高级"}>高级</Option>
</Select>
</div>
</Col>
<Col span={11} className="marginbottom10">
<label className="from-label">读写</label>
<div className="select-box">
<Select
placeholder="请选择"
value={item.writing}
onChange={(e) => {
onValuesChange(
'language',
e,
'writing',
key
)
}}
>
<Option value={"初级"}>初级</Option>
<Option value={"中级"}>中级</Option>
<Option value={"高级"}>高级</Option>
</Select>
</div>
</Col>
</Row>
)
})}
<Col span={24}>
<Form.Item>
<Button
type="dashed"
onClick={() => add('language')}
block
icon={<PlusOutlined />}
>
添加
</Button>
</Form.Item>
</Col>
<Divider orientation="left">自我描述</Divider>
<Row gutter={24} align="baseline">
<Col span={24}>
<label className="from-label">自我描述</label>
<TextArea
rows={4}
placeholder="请输入自我描述"
defaultValue={data['data']['review']}
onChange={(e) => {
updataPosition({review: e.target.value})
}}
/>
</Col>
</Row>
<Divider orientation="left">获奖经历</Divider>
{remembrance.map((item, key) => {
return (
<Row key={key} gutter={24} align="baseline">
<Col span={11} className="marginbottom10">
<label className="from-label">获奖时间</label>
<div className="select-box">
<DatePicker
locale={locale}
value={
item.prize_time.length > 0
? (moment(
item.prize_time,
dateFormat
))
: ''
}
onChange={(date, dateString) => {
onValuesChange(
'remembrance',
dateString[0],
'prize_time',
key
)
}}
/>
</div>
</Col>
<Col span={11} className="marginbottom10">
<label className="from-label">奖项名称</label>
<Input
placeholder="请输入奖项名称"
value={item.prize_name}
onChange={(e) => {
onValuesChange(
'remembrance',
e.target.value,
'prize_name',
key
)
}}
/>
</Col>
<Col span={2} className="textalignright">
<Form.Item label=" ">
<MinusCircleOutlined
onClick={() =>
remove('remembrance', key)
}
/>
</Form.Item>
</Col>
</Row>
)
})}
<Col span={24}>
<Form.Item>
<Button
type="dashed"
onClick={() => add('remembrance')}
block
icon={<PlusOutlined />}
>
添加
</Button>
</Form.Item>
</Col>
</div>
</div>
)
}

View File

@ -6,7 +6,7 @@
width: 40%;
}
.resume-content-box {
width: 55%;
/* width: 55%; */
}
.resume-energy-box {
width: 100%;
@ -21,3 +21,5 @@
background-color: #eee;
margin-top: 46px;
}
.resume-energy-box .ant-tabs { width: 100%; display: flex; justify-content: space-between; }
.ResumeUpload-box .resume-pop-box .ant-tabs-content-holder{overflow-y:scroll; max-height: calc( 100vh - 375px);}

View File

@ -1,57 +1,94 @@
import React from 'react';
import { Form, Input, Modal, Radio, Row ,Col, Select, DatePicker, Tabs } from 'antd';
import 'moment/locale/zh-cn';
import locale from 'antd/es/date-picker/locale/zh_CN';
import React,{ useState, Component } from 'react'
import {
Form,
Input,
Modal,
Radio,
Row,
Col,
Select,
DatePicker,
Tabs,
Button,
message,
} from 'antd'
import 'moment/locale/zh-cn'
import moment from 'moment'
import locale from 'antd/es/date-picker/locale/zh_CN'
import StandardResume from './StandardResume'
import Originalresume from '../../components/Originalresume'
import {updatainterview} from '../../util/requestURL'
import './index.css'
const {Option} = Select
const { Option } = Select
const { TabPane } = Tabs
const dateFormat = 'YYYY-MM-DD'
export default class ResumeUpload extends Component {
state= this.props.data.data
export default function ResumeUpload({visible,onCancel,onCreate}) {
const [form] = Form.useForm();
const onChange = (key) => {
console.log(key);
};
return (
<div>
<Modal
width={'96%'}
visible={visible}
okText="确定"
cancelText="取消"
onCancel={onCancel}
closable= {false}
onOk={() => {
form
.validateFields()
.then((values) => {
form.resetFields();
onCreate(values);
updataPosition = (item)=>{
this.setState(item,()=>{
// console.log(this.state)
})
}
onValuesChange = (item) =>{
this.setState(item,()=>{
// console.log(this.state)
})
}
render(){
const { visible, onCancel, onCreate,onReupload, data } = this.props
return(
<div className="ResumeUpload-box">
<Modal
width={'1600px'}
visible={visible}
closable={false}
maskClosable={false}
destroyOnClose={true}
footer={[
<Button key={'cancel'} onClick={onCancel}>取消</Button>,
<Button key={'Reupload'} onClick={onReupload}>重新上传</Button>,
<Button
key={'ok'}
type="primary"
onClick={() => {
var data= this.state;
if(data.name === "" || data.job_name === "" || data.phone === "" || data.mail ===""){
message.warning('姓名,职位,电话,邮箱必填')
return;
}
updatainterview({data_in:this.state}).then((res)=>{
if(res.msg === "ok"){
message.success("添加成功!")
}
},(err)=>{
message.error('网络加载错误,请稍后再试')
})
.catch((info) => {
console.log('Validate Failed:', info);
});
}}
>
<div className='resume-pop-box'>
<div className='resume-img-box'>
保存
</Button>,
]}
>
<div className="resume-pop-box">
{/* <div className='resume-img-box'>
</div>
<div className='resume-content-box'>
</div> */}
<div className="resume-content-box">
<Form
form={form}
onValuesChange={this.onValuesChange}
layout="vertical"
name="form_in_modal"
initialValues={{
modifier: 'public',
}}
initialValues={data.data}
>
<Row gutter={24}>
<Col span={6}>
<Form.Item
name="title"
name="name"
rules={[
{
required: true,
@ -59,30 +96,48 @@ export default function ResumeUpload({visible,onCancel,onCreate}) {
},
]}
>
<Input placeholder='填写姓名'/>
<Input placeholder="填写姓名" />
</Form.Item>
</Col>
<Col span={6}>
<Form.Item name="position">
<Input type="textarea" placeholder='填写岗位'/>
<Form.Item name="job_name" rules={[
{
required: true,
message: '请填写岗位',
},
]}>
<Input
type="textarea"
placeholder="填写岗位"
/>
</Form.Item>
</Col>
<Col span={6}>
<Form.Item name="sex">
<Form.Item name="gender">
<Select
placeholder="选择性别"
optionFilterProp="children"
>
<Option value="1"></Option>
<Option value="2"></Option>
<Option value={0}></Option>
<Option value={1}></Option>
</Select>
</Form.Item>
</Col>
<Col span={6}>
<Form.Item name="age">
<DatePicker locale={locale} />
<Form.Item name="birthday"
>
<DatePicker placeholder='请选择出生年月' value={
data.data > 0
? (moment(
data.data.birthday,
dateFormat
))
: ''
} locale={locale} onChange={(dates, dateStrings) => {
this.updataPosition({birthday: dateStrings})
}}/>
</Form.Item>
</Col>
<Col span={6}>
@ -105,7 +160,7 @@ export default function ResumeUpload({visible,onCancel,onCreate}) {
</Col>
<Col span={6}>
<Form.Item
name="email"
name="mail"
rules={[
{
type: 'email',
@ -121,15 +176,15 @@ export default function ResumeUpload({visible,onCancel,onCreate}) {
</Form.Item>
</Col>
<Col span={4}>
<Form.Item name="workingyears">
<Form.Item name="work_exp">
<Select
placeholder="选择工作年限"
optionFilterProp="children"
>
<Option value="1">应届生</Option>
<Option value="2">1-3</Option>
<Option value="3">3-5</Option>
<Option value="4">5年以上</Option>
<Option value={0}>应届生</Option>
<Option value={1}>1-3</Option>
<Option value={2}>3-5</Option>
<Option value={3}>5年以上</Option>
</Select>
</Form.Item>
</Col>
@ -139,57 +194,42 @@ export default function ResumeUpload({visible,onCancel,onCreate}) {
placeholder="选择学历"
optionFilterProp="children"
>
<Option value="1">高中及以下</Option>
<Option value="2">专科</Option>
<Option value="3">本科</Option>
<Option value="4">硕士</Option>
<Option value="5">博士</Option>
<Option value={1}>大专</Option>
<Option value={2}>本科</Option>
<Option value={3}>硕士</Option>
<Option value={4}>博士</Option>
</Select>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item
name="address"
name="now_address"
rules={[
{
required: true,
message: '填写现住地址',
},
]}
>
<Input placeholder='填写现住地址'/>
<Input placeholder="填写现住地址"/>
</Form.Item>
</Col>
</Row>
<div className="resume-energy-box">
<div className='resume-xq-box'>
<Originalresume data={data}/>
</div>
<div className='resume-xq-box'>
<StandardResume data={data} updataPosition={this.updataPosition}/>
</div>
</div>
</Form>
<div className='resume-energy-box'>
<Tabs defaultActiveKey="2" onChange={onChange}>
<TabPane tab="原始简历" key="1">
原始简历
</TabPane>
<TabPane tab="标准简历" key="2">
<StandardResume />
</TabPane>
</Tabs>
<div className='resume-right-box'>
</div>
</div>
</div>
</div>
</Modal>
</div>
)
}
}

View File

@ -7,7 +7,85 @@
.Candidate-left-box {
width: 250px;
height: 100%;
position: relative;
}
.Candidate-left-top-box {
border-top: 1px solid #d9d9d9;
border-bottom: 1px solid #d9d9d9;
background-color: #e9eaec;
display: flex;
align-items: center;
}
.Candidate-left-icon-box {
font-size: 20px;
border-right: 1px solid #dddfe3;
padding: 10px 15px;
}
.Candidate-left-tit-box {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.Candidate-left-tit-icon-box {
width: 30px;
height: 30px;
border-radius: 30px;
background-color: #fff;
font-size: 20px;
text-align: center;
line-height: 30px;
color: #0c8cf6;
}
.Candidate-left-tit {
margin-left: 10px;
font-size: 16px;
color: #0c8cf6;
display: flex;
align-items: center;
}
.Candidate-left-tit-jt-box {
color: #0c8cf6;
margin-right: 15px;
font-size: 16px;
}
.Candidate-position-box {
margin: 15px;
}
.Candidate-position-search-box {
display: flex;
align-items: center;
}
.Candidate-position-search-box button {
margin-left: 10px;
padding: 4px 10px;
color: #c1c5cc;
}
.Candidate-addgroup-box {
color: #89909e;
}
.Candidate-info-screen-box {
width: 100%;
padding: 18px 15px;
position: absolute;
bottom: 0;
border-top: 1px solid #dddfe3;
display: flex;
justify-content: space-between;
align-items: center;
color: #292c32;
}
.Candidate-info-screen-icon-box {
height: 20px;
font-size: 12px;
display: grid;
}
.Candidate-info-screen-icon-xia-box {
position: relative;
top: -4px;
}
.Candidate-right-box {
width: calc(100% - 250px);
background-color: #fff;
@ -48,7 +126,7 @@
}
.Candidate-table-box {
margin: 24px;
height: calc(100vh - 210px);
}

View File

@ -1,97 +1,357 @@
import React, { Component } from 'react'
import { Button, Form, Input, Modal, Radio, Tabs } from 'antd'
import { Button, Form, Input, Tabs, Tree, TreeSelect, message } from 'antd'
import './index.css'
import CandidateTable from '../../../components/CandidateTable'
import { interviewstagenum, getsection } from '../../../util/requestURL'
import memoryUtils from '../../../util/memoryUtils'
import PubSub from 'pubsub-js'
import {
ShareAltOutlined,
TeamOutlined,
RightOutlined,
SearchOutlined,
FilterFilled,
FolderFilled,
FireFilled,
PlusCircleOutlined,
MenuOutlined,
CaretUpOutlined,
CaretDownOutlined,
} from '@ant-design/icons'
const { TabPane } = Tabs
const { DirectoryTree } = Tree
const { SHOW_PARENT } = TreeSelect
export default class Candidate extends Component {
state = {
tjnum: 0,
csnum: 2,
fsnum: 3,
msnum: 4,
gtnum: 5,
drznum: 2,
csnum: 0,
fsnum: 0,
msnum: 0,
gtnum: 0,
drznum: 0,
step: '0',
job_names:'',
treeData: [],
}
componentDidMount() {
PubSub.publish('headtitle', { headtitle: '候选人管理' })
getsection().then(
(res) => {
const data = res.data
const treeData = []
for (let i in data) {
const arr = {
title: data[i]['name'],
key: i,
icon: <FolderFilled style={{ color: '#0c8cf6' }} />,
children: [],
}
treeData.push(arr)
for (let j in data[i]['position']) {
const drr = {
title: data[i]['position'][j]['job_name'],
key: data[i]['position'][j]['job_id'],
icon: <FireFilled style={{ color: '#ff6b6b' }} />,
}
treeData[i]['children'].push(drr)
}
}
this.setState({ treeData })
},
(ree) => {}
)
this.updatainterviewstagenum()
}
//
updatainterviewstagenum = () => {
interviewstagenum({ data_in: {} }).then(
(res) => {
// console.log(res)
const data = res.data
this.setState({
tjnum: data[0],
csnum: data[1],
fsnum: data[2],
msnum: data[3],
gtnum: data[4],
drznum: data[5],
})
},
(err) => {}
)
}
render() {
const { treeData } = this.state
const onSelect = (selectedKeys, info) => {
// console.log(selectedKeys)
if(selectedKeys[0].length > 3){
console.log(selectedKeys)
console.log(info.node.title)
this.setState({
job_names:info.node.title
})
}
}
const handleClickTabs = (key) => {
console.log(key)
this.setState({
step: key,
})
}
const treeSeletData = [
{
title: '初试',
value: '0-0',
key: '0-0',
},
{
title: '复试',
value: '0-1',
key: '0-1',
},
{
title: '终试',
value: '0-2',
key: '0-2',
},
]
const interviewstatusData = [
{
title: '待安排',
value: '0-0',
key: '0-0',
children: [
{
title: '待安排一',
value: '0-0-0',
key: '0-0-0',
},
],
},
{
title: '已安排',
value: '0-1',
key: '0-1',
children: [
{
title: '已安排一',
value: '0-1-0',
key: '0-1-0',
},
],
},
]
const undetestatusData = [
{
title: '待安排',
value: '0-0',
key: '0-0',
children: [
{
title: '待安排一',
value: '0-0-0',
key: '0-0-0',
},
],
},
{
title: '已安排',
value: '0-1',
key: '0-1',
children: [
{
title: '已安排一',
value: '0-1-0',
key: '0-1-0',
},
],
},
]
const onTreeChange = (newValue) => {
// console.log('onChange ', value);
// setValue(newValue);
}
return (
<div className="Candidate-box">
<div className="Candidate-left-box"></div>
<div className="Candidate-left-box">
<div className="Candidate-left-top-box">
<div className="Candidate-left-icon-box">
<ShareAltOutlined />
</div>
<div className="Candidate-left-tit-box cursor">
<div className="Candidate-left-tit">
<div className="Candidate-left-tit-icon-box">
<TeamOutlined />
</div>
<span className="marginleft6 fontweight">
优先招聘职位
</span>
</div>
<div className="Candidate-left-tit-jt-box">
<RightOutlined />
</div>
</div>
</div>
<div className="Candidate-position-box">
<p className="fontweight">招聘职位</p>
<div className="Candidate-position-search-box">
<Input
placeholder="搜索职位"
suffix={<SearchOutlined />}
onChange={(e)=>{
this.setState({
job_names: e.target.value
})
}}
/>
<Button>
<FilterFilled />
</Button>
</div>
<div className="margintop20 backgroundcolor">
<DirectoryTree
multiple
defaultExpandAll
showIcon={true}
blockNode={true}
onSelect={onSelect}
treeData={treeData}
/>
<div className="Candidate-addgroup-box cursor margintop20">
<PlusCircleOutlined />{' '}
<span className="marginleft6">
添加职位分组
</span>
</div>
</div>
</div>
<div className="Candidate-info-screen-box cursor">
<div>
<MenuOutlined />{' '}
<span className="marginleft6">候选人信息筛选</span>
</div>
<div className="Candidate-info-screen-icon-box">
<CaretUpOutlined />
<CaretDownOutlined className="Candidate-info-screen-icon-xia-box" />
</div>
</div>
</div>
<div className="Candidate-right-box">
<div className="Candidate-tabs-box">
<Tabs defaultActiveKey="1">
<Tabs defaultActiveKey="0" onChange={handleClickTabs}>
<TabPane
tab={
<div className='tabs-box shadowright'>
<div className="tabs-box shadowright">
<div>{this.state.tjnum}</div>
<div>人才推荐</div>
</div>
}
key="1"
>
</TabPane>
key="0"
></TabPane>
<TabPane
tab={
<div className='tabs-box tabs-border'>
<div className="tabs-box tabs-border">
<div>{this.state.csnum}</div>
<div>处筛</div>
<div></div>
</div>
}
key="2"
>
</TabPane>
key="1"
></TabPane>
<TabPane
tab={
<div className='tabs-box tabs-border'>
<div className="tabs-box tabs-border">
<div>{this.state.fsnum}</div>
<div>用人部门复筛</div>
</div>
}
key="3"
>
</TabPane>
key="2"
></TabPane>
<TabPane
tab={
<div className='tabs-box tabs-border'>
<div className="tabs-box tabs-border">
<div>{this.state.msnum}</div>
<div>面试</div>
</div>
}
key="4"
>
</TabPane>
key="3"
></TabPane>
<TabPane
tab={
<div className='tabs-box tabs-border'>
<div className="tabs-box tabs-border">
<div>{this.state.gtnum}</div>
<div>沟通offer</div>
</div>
}
key="5"
>
</TabPane>
key="4"
></TabPane>
<TabPane
tab={
<div className='tabs-box border0'>
<div className="tabs-box border0">
<div>{this.state.drznum}</div>
<div>待入职</div>
</div>
}
key="6"
>
</TabPane>
key="5"
></TabPane>
</Tabs>
</div>
<div className='Candidate-table-box'>
<CandidateTable/>
<div className="Candidate-table-box">
{/* <div className="">
<TreeSelect
treeData={treeSeletData}
onChange={onTreeChange}
treeCheckable={true}
showCheckedStrategy={SHOW_PARENT}
placeholder="面试轮次"
style={{ width: '10%' }}
/>
<TreeSelect
treeData={interviewstatusData}
onChange={onTreeChange}
treeCheckable={true}
showCheckedStrategy={SHOW_PARENT}
placeholder="面试状态"
style={{ width: '10%' }}
/>
<TreeSelect
treeData={undetestatusData}
onChange={onTreeChange}
treeCheckable={true}
showCheckedStrategy={SHOW_PARENT}
placeholder="面试状态"
style={{ width: '10%' }}
/>
</div> */}
<CandidateTable
step={this.state.step}
job_names = {this.state.job_names}
updatainterviewstagenum={
this.updatainterviewstagenum
}
/>
</div>
</div>
</div>
)

View File

@ -1,4 +1,4 @@
.Interview-bj { width: 100%; background-color: #fff;}
.Interview-bj { width: 100%; background-color: #fff; padding-bottom: 20px;}
.Interview-box {
margin: 0px 180px;
padding-top: 45px;
@ -9,3 +9,9 @@
.Interview-querycriteria-box { margin-top: 25px; width: 80%; }
.Interview-table-title { font-size: 17px; font-weight: bold; color: #575d6a; margin: 20px 0; }
.ewmimg {
opacity: 0;
z-index: 10;
position: absolute;
top: 0
}

View File

@ -1,7 +1,9 @@
import React, { useState } from 'react'
import React, { useState, useEffect } from 'react'
import ExportForm from '../../../components/ExportForm'
import InterviewerInfoPop from '../../../components/InterviewerInfoPop'
import Editresume from '../../../components/Editresume'
import PubSub from 'pubsub-js'
import storageUtils from '../../../util/storageUtils'
import './index.css'
import {
Button,
@ -15,6 +17,9 @@ import {
Col,
Table,
Tag,
message,
Pagination,
Image,
} from 'antd'
import {
UnorderedListOutlined,
@ -25,18 +30,81 @@ import {
LikeOutlined,
WarningOutlined,
} from '@ant-design/icons'
import {
getname,
getjob,
postcondition,
teacherstate,
} from '../../../util/requestURL'
const { Option } = Select
function Interview() {
const [tabPosition, setTabPosition] = useState('list')
const [visible, setVisible] = useState(false) //
const [interviewerInfo, setInterviewerInfo] = useState(false) //
const [editresume, setEditresume] = useState(false) //
const [candidateid,setcandidateid] = useState(0)
const [candidateid, setcandidateid] = useState(0) //id
const [hrName, setHrName] = useState([]) //
const [interviewName, setinterviewName] = useState([]) //
const [job, setjob] = useState([]) //
const [today, settoday] = useState() //
const [todaynum, settodaynum] = useState(0)
const [tomorrow, settomorrow] = useState() //
const [tomorrownum, settomorrownum] = useState(0)
const [yesterday, setyesterday] = useState() //
const [yesterdaynum, setyesterdaynum] = useState(0)
const [form] = Form.useForm()
const todaynum = 0
const tomorrow = 0
const yesterday = 0
var userInfo = storageUtils.getUser()
let screenData = {
date: {
job_name: '',
hr_name: '',
interview_name: '',
interview_type: '',
interview_sign: '',
feedback: '',
interview_round: '',
hr_id: '',
},
pages: 1,
time_type: 'now',
}
useEffect(() => {
PubSub.publish('headtitle', { headtitle: '面试安排' })
// console.log('')
//
getname().then(
(res) => {
const interview_name = []
const hr_name = []
for (let i in res.data) {
if (res.data[i]['rank'] == 1) {
hr_name.push(res.data[i])
} else if (res.data[i]['rank'] == 2) {
interview_name.push(res.data[i])
}
}
setinterviewName(interview_name)
setHrName(hr_name)
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
//
getjob().then(
(res) => {
setjob(res.data.job)
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
CallApiList()
}, [])
const changeTabPosition = (e) => {
setTabPosition(e.target.value)
@ -49,45 +117,143 @@ function Interview() {
console.log('Received values of form: ', values)
setVisible(false)
}
const handelFeedback = (record,index)=>{
console.log("催促反馈")
const handelFeedback = (record, index) => {
teacherstate({
user_id: [record.interview_id],
name: record.name,
}).then(
(res) => {
console.log(res)
if (res.code == 200) {
message.success('发送成功')
}
const handelInterviewerInfo = (record,index)=>{
console.log("查看信息")
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
}
const handelInterviewerInfo = (record, index) => {
// console.log('',record)
// console.log(record.key)
setcandidateid(record.key)
setInterviewerInfo(true)
}
const onInterviewerInfo = (values) =>{
const onInterviewerInfo = (values) => {
setInterviewerInfo(false)
}
const onEditresumeCreate = () =>{
const onEditresumeCreate = () => {
setEditresume(false)
}
//
const onEditresume = (data)=>{
const onEditresume = (data) => {
setInterviewerInfo(false)
console.log(data);
setTimeout(()=>{
console.log(data)
setTimeout(() => {
setEditresume(true)
},500)
}, 500)
}
const handelSelect = (value, res) => {
// console.log(value)
screenData['date'][res] = value == 0 ? '' : value
CallApiList()
}
const CallApiList = () => {
PostTableData(screenData, 'now')
PostTableData(screenData, 'tomorrow')
PostTableData(screenData, 'yesterday')
}
const PostTableData = (screenData, type, pages) => {
screenData['time_type'] = type
if (pages) {
screenData['pages'] = pages
}
postcondition(screenData).then(
(res) => {
switch (type) {
case 'now':
settoday(res.data.data)
settodaynum(res.data.lens)
break
case 'tomorrow':
settomorrow(res.data.data)
settomorrownum(res.data.lens)
break
default:
setyesterday(res.data.data)
setyesterdaynum(res.data.lens)
}
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
}
const handelPage = (page, type) => {
PostTableData(screenData, 'tomorrow', page)
}
const [allpositionmenutitle, setallpositionmenutitle] = useState('全部职位')
const onallpositionclick = (item) => {
setallpositionmenutitle(item.key)
if (item.key === '我负责的职位') {
screenData.date.hr_id = userInfo.user_id
} else {
screenData.date.hr_id = ''
}
CallApiList()
}
const allpositionmenu = (
<Menu
selectable
defaultSelectedKeys={['1']}
onClick={onallpositionclick}
items={[
{
key: '全部职位',
label: '全部职位',
},
{
key: '我负责的职位',
label: '我负责的职位',
},
// {
// key: '',
// label: '',
// },
]}
/>
)
const menu = (
<Menu
onClick={onMenuClick}
items={[
{
key: '1',
label: '面试签到二维码',
label: (
<div>
<Image
width={'100px'}
height={20}
className="ewmimg"
src="https://legu-cdn-source.obs.cn-east-2.myhuaweicloud.com/hrms/checkInQR.png"
/>
<p style={{ position: 'absolute' , top: 10 , 'z-index': 1}}>面试签到二维码</p>
</div>
),
},
{
key: '2',
label: '显示已取消的面试',
}
},
]}
/>
)
@ -95,14 +261,14 @@ function Interview() {
const columns = [
{
title: '面试信息',
dataIndex: 'info',
dataIndex: 'msg',
render: (text, record, index) => {
return (
<div className="basicdata-box">
<Tag color="#00d1db">
{text.rounds === 0
{text.interview_round === 0
? '初试'
: text.rounds === 1
: text.interview_round === 1
? '复试'
: '终试'}
</Tag>
@ -116,21 +282,16 @@ function Interview() {
},
{
title: '候选人',
dataIndex: 'candidate',
dataIndex: 'name',
render: (text, record, index) => (
<div className="font-size14 color0">
{text.type ? (
<CheckCircleTwoTone twoToneColor="#51cf66" />
) : (
''
)}
<span style={{ marginLeft: 2 }}>{text.name}</span>
<span style={{ marginLeft: 2 }}>{text}</span>
</div>
),
},
{
title: '联系方式',
dataIndex: 'tel',
dataIndex: 'phone',
render: (text, record, index) => (
<div className="font-size14 color0">
<span>{text}</span>
@ -139,21 +300,14 @@ function Interview() {
},
{
title: '应聘职位',
dataIndex: 'position',
render: (text, record, index) => (
<div className="font-size14 color1">{text}</div>
),
},
{
title: '面试地点',
dataIndex: 'address',
dataIndex: 'job_names',
render: (text, record, index) => (
<div className="font-size14 color1">{text}</div>
),
},
{
title: '面试负责人',
dataIndex: 'principal',
dataIndex: 'hr_name',
render: (text, record, index) => (
<div className="font-size14 color1">{text}</div>
),
@ -164,74 +318,38 @@ function Interview() {
render: (text, record, index) => (
<div className="font-size14 color1">
{text.type === 0 ? (
<LikeOutlined style={{ color: '#51cf66' }} />
) : (
<WarningOutlined style={{ color: '#f59f00' }} />
) : (
<LikeOutlined style={{ color: '#51cf66' }} />
)}
{text.name}
&nbsp;{text.name}
</div>
),
},
{
title: '',
dataIndex: 'setup',
dataIndex: 'type',
render: (text, record, index) => (
<div className="font-size14 color4 ">
<span className='cursor' style={{'margin':'0px 5px'}} onClick={()=>handelInterviewerInfo(record,index)}>查看详情</span>
<span className='cursor' style={{'margin':'0px 5px'}} onClick={()=>handelFeedback(record,index)}>{text.type === 0 ? '催促反馈' : ''}</span>
<span
className="cursor"
style={{ margin: '0px 5px' }}
onClick={() => handelInterviewerInfo(record, index)}
>
查看详情
</span>
<span
className="cursor"
style={{ margin: '0px 5px' }}
onClick={() => handelFeedback(record, index)}
>
{text.type === 0 ? '催促反馈' : ''}
</span>
</div>
),
},
]
const data = [
{
key: 0,
info: {
rounds: 0,
type: 0,
time: '5云17日 0930 - 10:00',
},
candidate: {
type: true,
name: '张三',
},
tel: 18563265489,
position: '前端工程师',
address: '武汉 创意天地',
principal: '李四',
type: {
type: 0,
name: '王五',
},
setup: {
type: 0,
},
},
{
key: 1,
info: {
rounds: 1,
type: 1,
time: '5云17日 0930 - 10:00',
},
candidate: {
type: false,
name: '张三',
},
tel: 18563265489,
position: '前端工程师',
address: '武汉 创意天地',
principal: '李四',
type: {
type: 1,
name: '王五',
},
setup: {
type: 1,
},
},
]
const [hasData, setHasData] = useState(true)
const tableColumns = columns.map((item) => ({ ...item }))
return (
@ -252,10 +370,12 @@ function Interview() {
</Radio.Button>
</Radio.Group>
<Dropdown overlay={allpositionmenu}>
<Button className="allposition">
<TeamOutlined /> 全部职位
<TeamOutlined /> {allpositionmenutitle}
<RightOutlined />
</Button>
</Dropdown>
</Space>
</div>
@ -283,86 +403,144 @@ function Interview() {
<Row gutter={12}>
<Col span={4}>
<Form.Item label="职位">
<Select defaultValue="0">
<Option value="0">全部职位</Option>
<Option value="1">职位一</Option>
<Option value="2">职位二</Option>
<Option value="3">职位三</Option>
<Select
defaultValue=""
onChange={(value) => {
handelSelect(value, 'job_id')
}}
>
<Option value="">全部职位</Option>
{job.map((item, key) => {
return (
<Option
key={item['key']}
value={item['key']}
>
{item['job_name']}
</Option>
)
})}
</Select>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label="面试负责人">
<Select defaultValue="0">
<Option value="0">全部职位</Option>
<Option value="1">职位一</Option>
<Option value="2">职位二</Option>
<Option value="3">职位三</Option>
<Select
defaultValue=""
defaultActiveFirstOption={true}
onChange={(value) => {
handelSelect(value, 'hr_name')
}}
>
<Option value="">全部</Option>
{hrName.map((item, key) => {
return (
<Option
value={item.name}
key={(key + 1) * 1}
>
{item.name}
</Option>
)
})}
</Select>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label="面试官">
<Select defaultValue="0">
<Option value="0">全部职位</Option>
<Option value="1">职位一</Option>
<Option value="2">职位二</Option>
<Option value="3">职位三</Option>
<Select
defaultValue=""
onChange={(value) => {
handelSelect(
value,
'interview_name'
)
}}
>
<Option value="">全部</Option>
{interviewName.map((item, key) => {
return (
<Option
value={item.name}
key={(key + 1) * 1}
>
{item.name}
</Option>
)
})}
</Select>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label="面试类型">
<Select defaultValue="0">
<Option value="0">全部职位</Option>
<Option value="1">职位一</Option>
<Option value="2">职位二</Option>
<Option value="3">职位三</Option>
</Select>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label="面试地点">
<Select defaultValue="0">
<Option value="0">全部职位</Option>
<Option value="1">职位一</Option>
<Option value="2">职位二</Option>
<Option value="3">职位三</Option>
<Select
defaultValue=""
onChange={(value) => {
handelSelect(
value,
'interview_type'
)
}}
>
<Option value="">全部类型</Option>
<Option value="0">现场面试</Option>
<Option value="1">视频面试</Option>
<Option value="2">集体面试</Option>
<Option value="3">电话面试</Option>
</Select>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label="面试签到">
<Select defaultValue="0">
<Option value="0">全部职位</Option>
<Option value="1">职位一</Option>
<Option value="2">职位二</Option>
<Option value="3">职位三</Option>
<Select
defaultValue=""
onChange={(value) => {
handelSelect(
value,
'interview_sign'
)
}}
>
<Option value="">全部</Option>
<Option value="0">已签到</Option>
<Option value="1">未签到</Option>
</Select>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label="面反馈">
<Select defaultValue="0">
<Option value="0">全部职位</Option>
<Option value="1">职位一</Option>
<Option value="2">职位二</Option>
<Option value="3">职位三</Option>
<Form.Item label="面试反馈">
<Select
defaultValue=""
onChange={(value) => {
handelSelect(value, 'feedback')
}}
>
<Option value="">全部反馈</Option>
<Option value={0}>已全部反馈</Option>
<Option value={1}>未全部反馈</Option>
</Select>
</Form.Item>
</Col>
<Col span={4}>
{/* <Col span={4}>
<Form.Item label="面试轮次">
<Select defaultValue="0">
<Select
defaultValue="0"
onChange={(value) => {
handelSelect(
value,
'interview_round'
)
}}
>
<Option value="0">全部轮次</Option>
<Option value="1">初试</Option>
<Option value="2">复试</Option>
<Option value="3">终试</Option>
</Select>
</Form.Item>
</Col>
</Col> */}
</Row>
</Form>
</div>
@ -374,36 +552,55 @@ function Interview() {
<Table
bordered
columns={tableColumns}
dataSource={hasData ? data : []}
dataSource={hasData ? today : []}
pagination={{
position: ['none', 'none'],
}}
/>
<div className="margintop20 textalign">
<Pagination total={todaynum} hideOnSinglePage={true} />
</div>
{/* 明天及之后的面试 */}
<div className="Interview-table-title">
明天及之后的面试{tomorrow}
明天及之后的面试{tomorrownum}
</div>
<Table
bordered
columns={tableColumns}
dataSource={hasData ? data : []}
dataSource={hasData ? tomorrow : []}
pagination={{
position: ['none', 'none'],
}}
/>
<div className="margintop20 textalign">
<Pagination
total={tomorrownum}
hideOnSinglePage={true}
onChange={(page) => handelPage(page, 'tomorrow')}
/>
</div>
{/* 昨天及之前的面试 */}
<div className="Interview-table-title">
昨天及之前的面试{yesterday}
昨天及之前的面试{yesterdaynum}
</div>
<Table
bordered
columns={tableColumns}
dataSource={hasData ? data : []}
dataSource={hasData ? yesterday : []}
pagination={{
position: ['none', 'none'],
}}
/>
<div className="margintop20 textalign">
<Pagination total={yesterdaynum} hideOnSinglePage={true} />
</div>
{/* 查看面试人信息弹窗 */}
<InterviewerInfoPop
visible={interviewerInfo}
onCreate={onInterviewerInfo}
data={candidateid}
onCancel={() => {
setInterviewerInfo(false);
setInterviewerInfo(false)
}}
onEditresume={onEditresume}
/>
@ -412,7 +609,8 @@ function Interview() {
<Editresume
visible={editresume}
onCreate={onEditresumeCreate}
onCancel={()=>{
data={candidateid}
onCancel={() => {
setEditresume(false)
}}
/>

View File

@ -0,0 +1,23 @@
.Resumescreen-box {
margin: 60px 230px;
}
.Resumescreen-list-data-box {
background-color: #fff;
min-height: 600px;
}
.Resumescreen-list-tab-box {
margin: 0 30px;
}
.Resumescreen-list-tab-box .ant-tabs-tab-btn {
color: #312b2e !important;
}
.Resumescreen-list-tab-box .num {
font-size: 17px;
}
.Resumescreen-list-tab-box .ant-tabs { width: 100%; }
.Resumescreen-list-table-box { margin: 0 30px; }
.Resumescreen-list-data-box .CandidateTable-owner { width: 10%; }
.Resumescreen-list-data-box .CandidateTable-list-box { align-items: center; }
.Resumescreen-list-data-box .CandidateTable-info { margin-left: 0; }

View File

@ -0,0 +1,334 @@
import React, { Component } from 'react'
import {
Input,
Space,
Form,
Select,
Col,
Row,
Tabs,
Empty,
Tag,
Button,
message,
DatePicker,
Table,
} from 'antd'
import { AudioOutlined, ShoppingFilled, BankOutlined } from '@ant-design/icons'
import PubSub from 'pubsub-js'
import {
getjob,
postcondition,
interview,
interviewfind,
interviews,
} from '../../../util/requestURL'
import storageUtils from '../../../util/storageUtils'
import InterviewerInfoPop from '../../../components/InterviewerInfoPop'
import moment from 'moment'
import 'moment/locale/zh-cn'
import locale from 'antd/es/date-picker/locale/zh_CN'
import './index.css'
const { Search } = Input
const { Option } = Select
export default class Resumescreen extends Component {
state = {
getjoblist: {},
name: '',
job_name: '',
result: '',
userInfo: storageUtils.getUser(),
interviewerInfo: false,
uid: 0,
psges: 1,
star_time: '',
later: 0,
later_false: 0,
later_invalid: 0,
teacher_true: 0,
dataSource: [],
columns: [
{
title: '候选人',
dataIndex: 'name',
key: 'name',
},
{
title: '应聘职位',
dataIndex: 'job_names',
key: 'job_names',
},
{
title: '面试日期',
dataIndex: 'star_time',
key: 'star_time',
},
{
title: '类型',
dataIndex: 'interview_type',
key: 'interview_type',
},
{
title: '面试负责人',
dataIndex: 'hr_name',
key: 'hr_name',
},
{
title: '候选人状态',
dataIndex: 'interview_stage',
key: 'interview_stage',
},
{
title: '反馈结果',
dataIndex: 'teacher_back',
key: 'teacher_back',
render: (text, item) => {
switch (item.teacher_back) {
case 1:
return <span>满意</span>
break
case 2:
return <span>非常满意</span>
break
case 3:
return <span>不满意</span>
break
case 4:
return <span>非常满意</span>
break
default:
break
}
},
},
],
}
componentDidMount() {
PubSub.publish('headtitle', { headtitle: '面试安排' })
getjob().then(
(res) => {
this.setState({
getjoblist: res.data.job,
})
},
(err) => {}
)
this.postupdata()
}
postupdata = () => {
const { psges, name, job_name, result, star_time, userInfo } =
this.state
const postlist = {
date: {
name,
job_name,
result,
star_time,
interview_id: userInfo.user_id,
},
pages: psges,
time_type: 'later',
}
postcondition(postlist).then(
(res) => {
console.log(res.data)
},
(err) => {}
)
interviews({ date: postlist['date'] }).then(
(res) => {
// console.log(res.data)
var newdataSource = res.data['later'].map((item, key) => {
return { ...item, key: item.uid }
})
// console.log(newdataSource)
this.setState({
dataSource: newdataSource,
data: res.data,
})
},
(err) => {}
)
//
interview({ date: postlist['date'] }).then(
(res) => {
this.setState(res.data)
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
}
onValuesChange = (e) => {
this.setState(e)
}
handelonChange = (e) => {
const { data } = this.state
var newdataSource = data[e].map((item, key) => {
return { ...item, key: item.uid }
})
this.setState({
dataSource: newdataSource,
})
}
render() {
const {
getjoblist,
data,
later,
later_false,
later_invalid,
teacher_true,
} = this.state
const onSearch = (value) => console.log(value)
return (
<div className="Resumescreen-box">
<div className="">
<Form
layout="vertical"
onValuesChange={this.onValuesChange}
>
<Row gutter={24}>
<Col className="gutter-row" span={4}>
<Form.Item label="候选人" name="name">
<Search
placeholder="请输入候选人姓名"
onSearch={onSearch}
/>
</Form.Item>
</Col>
<Col className="gutter-row" span={4}>
<Form.Item label="应聘职位" name="job_name">
<Select>
<Option value="">全部职位</Option>
{getjoblist.length > 0
? getjoblist.map((item, key) => {
return (
<Option
value={
item.job_name
}
key={key}
>
{item.job_name}
</Option>
)
})
: ''}
</Select>
</Form.Item>
</Col>
<Col className="gutter-row" span={4}>
<Form.Item label="面试时间" name="time">
<DatePicker locale={locale} style={{'width':'210px'}}/>
</Form.Item>
</Col>
<Col className="gutter-row" span={4}>
<Form.Item label="反馈结果" name="result">
<Select>
<Option value={''}>全部</Option>
<Option value={0}>满意</Option>
<Option value={1}>一般</Option>
<Option value={2}>不满意</Option>
</Select>
</Form.Item>
</Col>
</Row>
</Form>
</div>
<div className="Resumescreen-list-data-box">
<div className="Resumescreen-list-tab-box">
<Tabs
defaultActiveKey={'later'}
onChange={(e) => this.handelonChange(e)}
>
<Tabs.TabPane
tab={
<div className="tabs-box">
<div className="num">{later}</div>
<div>今后的面试</div>
</div>
}
key={'later'}
></Tabs.TabPane>
<Tabs.TabPane
tab={
<div className="tabs-box">
<div className="num">{later_false}</div>
<div>未反馈的面试</div>
</div>
}
key={'later_false'}
></Tabs.TabPane>
<Tabs.TabPane
tab={
<div className="tabs-box">
<div className="num">
{teacher_true}
</div>
<div>已反馈的面试</div>
</div>
}
key={'teacher_true'}
></Tabs.TabPane>
<Tabs.TabPane
tab={
<div className="tabs-box">
<div className="num">
{later_invalid}
</div>
<div>已失效的面试</div>
</div>
}
key={'later_invalid'}
></Tabs.TabPane>
</Tabs>
</div>
<div className="Resumescreen-list-table-box">
<Table
dataSource={this.state.dataSource}
columns={this.state.columns}
pagination={false}
onRow={(record) => {
return {
onClick: (event) => {
this.setState({
interviewerInfo: true,
uid: record.key,
})
},
}
}}
/>
</div>
</div>
{/* 查看面试人信息弹窗 */}
<InterviewerInfoPop
visible={this.state.interviewerInfo}
onCreate={() => {
this.setState({
interviewerInfo: false,
})
}}
data={this.state.uid}
onCancel={() => {
this.setState({
interviewerInfo: false,
})
}}
/>
</div>
)
}
}

View File

@ -0,0 +1,108 @@
.Overview-box {
margin: 40px 180px;
}
.Overview-box p {
color: #121316;
margin-bottom: 0;
}
.Overview-top-box {
display: flex;
justify-content: space-between;
align-items: center;
}
.Overview-content-box {
display: flex;
justify-content: space-between;
}
.Overview-card-box {
display: flex;
align-items: center;
padding: 16px;
background-color: #fff;
margin-top: 30px;
box-shadow: 2px 2px 4px 1px rgba(000, 000, 000, 0.1);
}
.Overview-card-list-box {
width: 160px;
display: flex;
align-items: center;
}
.Overview-card-list {
margin-left: 14px;
}
.Overview-card-list p {
font-size: 16px;
color: #121316;
}
.Overview-card-list2 span {
color: #575d6a;
}
.Overview-card-list span {
font-size: 14px;
color: #575d6a;
}
.Overview-card-box .xian {
width: 1px;
height: 46px;
background-color: #f4f4f5;
margin: 0 6px;
}
.Overview-card-top-box {
width: 100%;
background-color: #f9f9fa;
padding: 8px 16px;
font-size: 16px;
}
.Overview-card-box2 {
/* display: flex;
align-items: center; */
/* padding: 16px 0; */
background-color: #fff;
margin-top: 18px;
box-shadow: 2px 2px 4px 1px rgba(000, 000, 000, 0.1);
}
.Overview-card-list-box2 {
display: flex;
align-items: center;
padding: 16px;
font-size: 14px;
}
.Overview-card-list2 {
min-width: 111px;
cursor: pointer;
}
.Overview-card-tips {
color: #575d6a;
font-size: 14px;
padding: 8px 16px;
border-top: 1px solid #f4f4f5;
}
.Overview-card-tips label {
margin-left: 8px;
}
.Overview-content-right-box {
background-color: #fff;
padding: 16px;
width: calc(100% - 425px);
margin-top: 30px;
}
.Overview-content-right-top-box {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 16px;
font-size: 18px;
color: #575d6a;
}
.allsign {
font-size: 14px;
cursor: pointer;
}
.Overview-tab-box .ant-tabs {
width: 100%;
}

View File

@ -1,9 +1,264 @@
import React from 'react'
import React, { Component } from 'react'
import PubSub from 'pubsub-js'
import {
Button,
Space,
Tabs,
Tag,
Avatar,
List,
Dropdown,
message,
Empty,
} from 'antd'
import {
ApartmentOutlined,
RightOutlined,
TeamOutlined,
CalendarOutlined,
FilterOutlined,
InfoCircleFilled,
CheckCircleFilled,
CalendarFilled,
} from '@ant-design/icons'
import { interviewstagenu } from '../../../util/requestURL'
import { Link } from 'react-router-dom'
import './index.css'
const { TabPane } = Tabs
const { CheckableTag } = Tag
const tagsData = [
'面试反馈',
'安排面试',
'通讯记录',
'面试官拒绝面试',
'其它',
]
function Overview() {
return (
<div>总览</div>
export default class Overview extends Component {
state = {
selectedTags: [],
datalist: [
{
title: 'Ant Design Title 1',
},
{
title: 'Ant Design Title 2',
},
{
title: 'Ant Design Title 3',
},
{
title: 'Ant Design Title 4',
},
],
data: [],
}
componentDidMount() {
PubSub.publish('headtitle', { headtitle: '总览' })
interviewstagenu({ date: {} }).then(
(res) => {
this.setState({
data: res.data,
})
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
}
}
export default Overview
render() {
const { data } = this.state
const onChange = (key) => {
console.log(key)
}
const handleChange = (tag, checked) => {
const nextSelectedTags = checked
? [...this.state.selectedTags, tag]
: this.state.selectedTags.filter((t) => t != tag)
console.log('You are interested in: ', tag, checked)
this.setState({
selectedTags: nextSelectedTags,
})
}
return (
<div className="Overview-box">
<div className="Overview-top-box">
<div>
<Space size={10}>
<Button className="allposition">
<ApartmentOutlined /> 默认招聘流程
<RightOutlined />
</Button>
<Button className="allposition">
<TeamOutlined /> 全部职位
<RightOutlined />
</Button>
</Space>
</div>
</div>
<div className="Overview-content-box">
<div className="Overview-content-left-box">
<div className="Overview-card-box">
<div className="Overview-card-list-box">
<div className="margin10">
<TeamOutlined
style={{
fontSize: '22px',
color: '#845ef7',
}}
/>
</div>
<div className="Overview-card-list">
<Link to={'/admin/candidate'}>
<p>{data[1]}</p>
<span>今日待筛选申请</span>
</Link>
</div>
</div>
<div className="xian"></div>
<div className="Overview-card-list-box">
<div className="margin10">
<CalendarFilled
style={{
fontSize: '22px',
color: '#22b8cf',
}}
/>
</div>
<div className="Overview-card-list">
<Link to={'/admin/interview'}>
<p>{data.now}</p>
<span>今日面试</span>
</Link>
</div>
</div>
</div>
<div className="Overview-card-box2">
<div className="Overview-card-top-box">
<CheckCircleFilled /> <span>录用</span>
</div>
<div className="Overview-card-list-box2">
<div className="Overview-card-list2">
<Link to={'/admin/candidate'}>
<p>{data.offer}</p>
<span>待发送Offer</span>
</Link>
</div>
<div className="Overview-card-list2">
<Link to={'/admin/candidate'}>
<p>{data[6]}</p>
<span>待入职</span>
</Link>
</div>
</div>
</div>
<div className="Overview-card-box2">
<div className="Overview-card-top-box">
<CalendarOutlined /> <span>面试</span>
</div>
<div className="Overview-card-list-box2">
<div className="color8 Overview-card-list2">
<Link to={'/admin/interview'}>
<p>0</p>
<span>待面试</span>
</Link>
</div>
<div className="Overview-card-list2">
<Link to={'/admin/interview'}>
<p>0</p>
<span>待反馈</span>
</Link>
</div>
<div className="Overview-card-list2">
<Link to={'/admin/candidate'}>
<p>0</p>
<span>待处理</span>
</Link>
</div>
</div>
</div>
</div>
<div className="Overview-content-right-box">
<div className="Overview-content-right-top-box">
<div>重要事项</div>
<div className="allsign color4">全部标记已读</div>
</div>
<div className="Overview-tab-box">
<Tabs defaultActiveKey="4" onChange={onChange}>
<TabPane tab="@提醒" key="1">
<Empty description="暂无数据" />
</TabPane>
<TabPane tab="招聘需求相关" key="2">
<Empty description="暂无数据" />
</TabPane>
<TabPane tab="职位相关" key="3">
<Empty description="暂无数据" />
</TabPane>
<TabPane tab="面试相关" key="4">
<div>
{tagsData.map((tag) => (
<CheckableTag
key={tag}
checked={
this.state.selectedTags.indexOf(
tag
) > -1
}
onChange={(checked) =>
handleChange(tag, checked)
}
>
{tag}
</CheckableTag>
))}
</div>
<div>
<Empty description="暂无数据" />
{/* <List
itemLayout="horizontal"
dataSource={this.state.datalist}
renderItem={(item) => (
<List.Item>
<List.Item.Meta
avatar={
<Avatar src="https://joeschmoe.io/api/v1/random" />
}
title={
<a href="https://ant.design">
{item.title}
</a>
}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/>
</List.Item>
)}
/> */}
</div>
</TabPane>
<TabPane tab="Offer相关" key="5">
<Empty description="暂无数据" />
</TabPane>
<TabPane tab="推荐相关" key="6">
<Empty description="暂无数据" />
</TabPane>
<TabPane tab="其他" key="7">
<Empty description="暂无数据" />
</TabPane>
</Tabs>
</div>
</div>
</div>
</div>
)
}
}

View File

@ -1,17 +1,19 @@
import React, { useState } from 'react'
import { Table, } from 'antd'
import React, { useState, useEffect } from 'react'
import { Table, message} from 'antd'
import { getjob } from '../../../../util/requestURL'
import { useNavigate } from 'react-router-dom'
import './index.css'
const columns = [
{
title: '基本资料',
dataIndex: 'basicdata',
dataIndex: 'job_name',
render: (text, record, index) => (
<div className='basicdata-box'>
<p className='basicdata-post'>
{text.post}
<span>({text.id})</span>
{record.job_name}
<span>({record.key})</span>
</p>
<p className='basicdata-des'> {text.des} </p>
<p className='basicdata-des'> {record.job_sector} </p>
</div>
),
},
@ -19,97 +21,91 @@ const columns = [
title: '招聘负责人',
dataIndex: 'principal',
render: (text, record, index) => (
<p className='font-size12 color1'>{text}</p>
),
},
{
title: '招聘渠道',
dataIndex: 'channel',
render: (text, record, index) => (
// console.log(text)
text.map((item,key)=>{
return <span key={key} className='font-size12 color1 '>{item}</span>
})
<p className='font-size12 color1'>{text[0]['name']}</p>
),
},
{
title: '职位来源',
dataIndex: 'source',
render: (text, record, index) => (
<p className='font-size12 color1'>{text}</p>
<p className='font-size12 color1'>手动添加</p>
),
},
{
title: '候选人总数',
dataIndex: 'candidatenum',
dataIndex: 'hou_num',
render: (text, record, index) => (
<p className='font-size12 color1'>{text}</p>
),
},
{
title: '入职人数',
dataIndex: 'inductionnum',
dataIndex: 'now_job_num',
render: (text, record, index) => (
<p className='font-size12 color1'>{text}</p>
),
},
{
title: '招聘人数',
dataIndex: 'recruitingnumbers',
dataIndex: 'job_num',
render: (text, record, index) => (
<p className='font-size12 color1'>{text}</p>
),
},
{
title: '候选人满意度',
dataIndex: 'satisfaction',
render: (text, record, index) => (
<div className='satisfaction-box'>
<p className='font-size12 color1'> <span className='font-size14 color3'>{text.satisfaction}</span>/5</p>
<p className='font-size12 color2'>{text.des}条评价</p>
</div>
),
},
}
]
const data = []
for (let i = 1; i <= 3; i++) {
data.push({
key: i,
basicdata: {
post: '高级游戏测试工程师',
id: 'MJ0000005',
des: '海外其他·技术部',
},
principal: '张三',
channel: ['BOSS'],
source: '手动添加',
candidatenum: '13',
inductionnum: '0',
recruitingnumbers: '3',
satisfaction: { satisfaction: 0, des: 0 },
})
}
const Inrecruitment = () => {
const Inrecruitment = (props) => {
const navigate = useNavigate()
const [hasData, setHasData] = useState(true)
const [data,setdata] = useState([])
const [loading,setloading] = useState(false)
const tableColumns = columns.map((item) => ({ ...item }))
console.log(tableColumns)
const tableProps = {
showHeader: true,
rowSelection: true,
Size: 'Small',
}
useEffect(()=>{
// console.log('aaaaa',props.data)
setloading(true)
postgender()
},[props.data])
const postgender = ()=>{
getjob(props.data).then(
(res) => {
setloading(false)
setdata(res.data.job)
props.handelnum(res)
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
}
return (
<div className='Inrecruitment'>
<Table
{...tableProps}
columns={tableColumns}
loading={loading}
dataSource={hasData ? data : []}
pagination={false}
onRow={record=>{
return {
onClick:(event)=>{
// console.log(record)
navigate('/admin/position/addposition/'+record.key)
}
}
}}
/>
</div>
)
}
export default Inrecruitment

View File

@ -1,7 +1,7 @@
.Position-box {
display: flex;
justify-content: space-between;
height: 100%;
/* height: 100%; */
}
.Position-left-box {
width: 250px;

View File

@ -1,55 +1,119 @@
import React, { useState, useEffect } from 'react'
import React, { Component } from 'react'
import { Outlet, useNavigate, useLocation } from 'react-router-dom'
import { Input, Space, Select, Button } from 'antd'
import { Input, Space, Select, Button, message } from 'antd'
import { getjob, getsection, getname } from '../../../util/requestURL'
import Inrecruitment from './Inrecruitment'
import PubSub from 'pubsub-js'
import './index.css'
const { Search } = Input
const { Option } = Select
const { Option, OptGroup } = Select
export default function Position() {
const [onmenu, setonmenu] = useState(true)
const navigate = useNavigate()
const location = useLocation()
const handelSwitchNav = (e, path) => {
navigate(path)
setonmenu((onmenu) => !onmenu)
export default class Position extends Component {
state = {
onmenu: true,
gatdata: { state: true, job_name: '', job_id: '', principal: [] },
section: [],
responsible: [],
end_num: 0,
start_num: 0,
}
const onSearch = (value) => {
console.log(value)
componentDidMount() {
getsection().then(
(res) => {
this.setState((state, props) => {
return {
section: res.data,
}
})
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
getname().then(
(res) => {
this.setState((state, props) => {
return {
responsible: res.data,
}
})
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
// getjob(this.state.gatdata).then(
// (res) => {
// this.setState({
// start_num: res.data.start_num,
// end_num: res.data.end_num
// })
// },
// (error) => {
// message.error('')
// }
// )
PubSub.publish('headtitle', { headtitle: '职位管理' })
}
useEffect(() => {
handelSwitchNav = (e, state) => {
// console.log(e,state)
const newGatdata = this.state.gatdata
this.setState({
onmenu: state,
gatdata: { ...newGatdata, state },
})
}
if (location.pathname.indexOf('Endrecruitment') >= 0) {
setonmenu((onmenu) => false)
onSearch = (e) => {
// console.log(e.target.value)
const newGatdata = this.state.gatdata
this.setState((state, props) => {
return {
gatdata: { ...newGatdata, job_name: e.target.value },
}
})
}
seleOnChange = (value, type) => {
const newGatdata = this.state.gatdata
if (type == 'job_id') {
var arr = value
} else {
setonmenu((onmenu) => true)
var arr = []
arr.push({user_id:value})
}
}, [location.pathname])
this.setState((state, props) => {
return {
gatdata: { ...newGatdata, [type]: arr },
}
})
}
render() {
// const location = useLocation()
const { onmenu, gatdata, section, responsible } = this.state
return (
<div className="Position-box">
<div className="Position-left-box">
<div className="Position-menu-box">
<div
className={onmenu ? 'on-menu-box' : ''}
onClick={(e) => handelSwitchNav(e, '/admin/position')}
onClick={(e) => this.handelSwitchNav(e, true)}
>
{/* <Link to="/admin/position"> */}
<p>1</p>
<p>{this.state.start_num}</p>
<span>招聘中职位</span>
{/* </Link> */}
</div>
<div
className={onmenu ? '' : 'on-menu-box'}
onClick={(e) =>
handelSwitchNav(e, '/admin/position/Endrecruitment')
}
onClick={(e) => this.handelSwitchNav(e, false)}
>
{/* <Link to="/admin/position/Endrecruitment"> */}
<p>2</p>
<p>{this.state.end_num}</p>
<span>已关闭职位</span>
{/* </Link> */}
</div>
</div>
@ -59,7 +123,7 @@ export default function Position() {
<Space direction="vertical">
<Search
placeholder="根据职位名称或职位编码搜索"
onSearch={onSearch}
onChange={this.onSearch}
style={{
width: 219,
}}
@ -69,62 +133,72 @@ export default function Position() {
<Select
style={{ width: 219 }}
showSearch
defaultValue="all"
defaultValue=""
optionFilterProp="children"
filterOption={(input, option) =>
option.children
.toLowerCase()
.includes(input.toLowerCase())
}
onChange={(e) => {
this.seleOnChange(e, 'job_id')
}}
>
<Option value="all">全部负责人</Option>
<Option value="jack">Jack</Option>
<Option value="lucy">Lucy</Option>
<Option value="tom">Tom</Option>
</Select>
<p className="title">渠道筛选</p>
<Select
style={{ width: 219 }}
showSearch
defaultValue="all"
optionFilterProp="children"
filterOption={(input, option) =>
option.children
.toLowerCase()
.includes(input.toLowerCase())
}
<Option value="">全部</Option>
{section.map((item, key) => {
return (
<OptGroup label={item.name} key={key}>
{item['position'].map((items, keys) => {
return (
<Option
value={items.job_id}
key={items.job_id}
>
<Option value="all">全部负责人</Option>
<Option value="jack">Jack</Option>
<Option value="lucy">Lucy</Option>
<Option value="tom">Tom</Option>
{items.job_name}
</Option>
)
})}
</OptGroup>
)
})}
</Select>
<p className="title">负责人筛选</p>
<Select
style={{ width: 219 }}
showSearch
defaultValue="all"
defaultValue=""
optionFilterProp="children"
filterOption={(input, option) =>
option.children
.toLowerCase()
.includes(input.toLowerCase())
}
onChange={(e) => {
this.seleOnChange(e, 'principal')
}}
>
<Option value="all">全部负责人</Option>
<Option value="jack">Jack</Option>
<Option value="lucy">Lucy</Option>
<Option value="tom">Tom</Option>
<Option value="">全部负责人</Option>
{responsible.map((item, key) => {
return item.rank == 1 ? (
<Option value={item.user_id} key={item.user_id}>
{item.name}
</Option>
) : (
''
)
})}
</Select>
<Button className='setfilteritem' block>设置筛选项</Button>
<Button className="setfilteritem" block>
设置筛选项
</Button>
</div>
</div>
<div className="Position-right-box">
<Outlet />
{/* <Outlet data={gatdata}/> */}
<Inrecruitment
data={gatdata}
handelnum={(res) => {
// console.log(res)
this.setState({
start_num: res.data.start_num,
end_num: res.data.end_num,
})
}}
/>
</div>
</div>
)
}
}

View File

@ -0,0 +1,19 @@
.adduser-box .ant-tabs{
width: 100%;
}
.adduser-list-box {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 0px;
}
.adduser-list-left {
display: flex;
align-items: center;
}
.adduser-list-left p {margin: 0;}
.name { font-size: 13px; height: 18px; }
.mail { font-size: 12px; color: #8c8e93; height: 18px; }
.right { font-size: 12px; color: #8c8e93; height: 18px; }
.adduser-info-box { margin-left: 8px; }
.right-box { font-size: 12px; color: #8c8e93; }

View File

@ -0,0 +1,56 @@
import React from 'react'
import { Tabs, Avatar } from 'antd'
import './index.css'
const { TabPane } = Tabs
export default function AddUser() {
const onChange = (key) => {
console.log(key)
}
return (
<div className="adduser-box">
<Tabs defaultActiveKey="1" onChange={onChange}>
<TabPane tab="用户" key="1">
<div className="adduser-list-box">
<div className="adduser-list-left">
<Avatar
style={{
backgroundColor: '#319dff',
}}
>
</Avatar>
<div className="adduser-info-box">
<p className="name">吴操</p>
<p className="mail">xxxxxxx.com</p>
<p className="right">超级管理员</p>
</div>
</div>
<div className="right-box">所有者</div>
</div>
</TabPane>
<TabPane tab="角色" key="2">
<div className="adduser-list-box">
<div className="adduser-list-left">
<Avatar
style={{
backgroundColor: '#319dff',
}}
>
</Avatar>
<div className="adduser-info-box">
<p className="name">吴操</p>
<p className="mail">xxxxxxx.com</p>
<p className="right">超级管理员</p>
</div>
</div>
<div className="right-box">所有者</div>
</div>
</TabPane>
</Tabs>
</div>
)
}

View File

@ -0,0 +1,25 @@
.Kanbandetails-box {
min-height: calc(100vh - 64px);
background-color: #fff;
}
.Kanbandetails-top-box {
padding: 16px 24px;
}
.Kanbandetails-title-box {
margin: 16px 24px;
}
.Kanbandetails-title-box .Kanbandetails-title {
font-size: 20px;
font-weight: bold;
color: #312b2e;
}
.Kanbandetails-title-box p {
font-size: 12px;
color: #75777d;
}
.Kanbandetails-actionbar-box {
margin: 16px 24px;
}
.Kanbandetails-table-box {
margin: 16px 24px;
}

View File

@ -0,0 +1,425 @@
import React, { Component } from 'react'
import PubSub from 'pubsub-js'
import { Button, Table, DatePicker, message, Empty } from 'antd'
import request from '../../../../util/request'
import {
LeftOutlined,
MoreOutlined,
MailOutlined,
TeamOutlined,
CloudDownloadOutlined,
EditOutlined,
ArrowsAltOutlined,
} from '@ant-design/icons'
//rcharts
import 'echarts/lib/chart/line'
import 'echarts/lib/chart/pie'
import 'echarts/lib/chart/bar'
import 'echarts/lib/component/tooltip'
import 'echarts/lib/component/title'
import 'echarts/lib/component/legend'
import 'echarts/lib/component/markPoint'
import ReactEcharts from 'echarts-for-react'
import moment from 'moment'
import 'moment/locale/zh-cn'
import locale from 'antd/es/date-picker/locale/zh_CN'
import './index.css'
const { RangePicker } = DatePicker
const dateFormat = 'YYYY/MM/DD'
export default class Kanbandetails extends Component {
state = {
data: {
table_data: {},
},
time: [
moment((Math.round(new Date() / 1000) - 7 * 86400) * 1000).format('YYYY-MM-DD'),
moment(Math.round(new Date() / 1000) * 1000).format('YYYY-MM-DD'),
],
timevalue: [
moment(
moment((Math.round(new Date() / 1000) - 7 * 86400) * 1000).format(
'YYYY-MM-DD'
),
dateFormat
),
moment(
moment(Math.round(new Date() / 1000) * 1000).format(
'YYYY-MM-DD'
),
dateFormat
),
],
}
componentDidMount() {
const urlParams = new URL(window.location.href)
const pathname = urlParams.hash
const pathnameArr = pathname.split('/')
const url = pathnameArr[pathnameArr.length - 2] //id
const tableid = pathnameArr[pathnameArr.length - 1] //id
PubSub.publish('headtitle', { headtitle: '报表中心' })
this.setState(
{
url,
tableid,
},
() => {
this.postdata()
}
)
}
postdata() {
const { url, tableid, time } = this.state
request('post', 'forms/' + url, {
data_in: { table_id: tableid },
interview_query: {
start_time: time[0],
end_time: time[1],
},
}).then(
(res) => {
var dataList = res.data
if(JSON.stringify(dataList) != "{}"){
if (dataList['table_data']['type'] === 'table' && dataList['table_data']['size'] != "small") {
var dataSource = this.handeldataSource(dataList['data'])
const columns = this.handelcolumns(dataList['data'])
dataList['table_data']['dataSource'] = dataSource
dataList['table_data']['columns'] = columns
}
}
console.log(dataList)
this.setState({
data: dataList,
})
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
}
handelback() {
window.history.back()
}
getOption = (type, data) => {
if (type === 'line') {
var seriesData = []
var xAxisData = []
var datalist = {}
for (let i in data) {
for (let j in data[i]) {
if (!datalist[j]) {
datalist[j] = []
}
datalist[j].push(data[i][j])
}
xAxisData.push(i)
}
for(let i in datalist){
var arr = {
name: i,
type: 'line', //typebar,pie
data: datalist[i],
}
seriesData.push(arr)
}
console.log(seriesData)
let option = {
tooltip: {
trigger: 'axis',
},
xAxis: {
data: xAxisData,
},
yAxis: {
type: 'value',
},
series: seriesData,
}
return option
} else if (type === 'pie') {
var datalist = []
for (let i in data) {
var arr = {}
arr['value'] = data[i].length
arr['name'] = i
datalist.push(arr)
}
let option = {
tooltip: {
trigger: 'item',
},
legend: {
top: '5%',
left: 'center',
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['40%', '70%'],
label: {
normal: {
show: true,
position: 'inner',
formatter: '{b}:{c}' + '\n\r' + '({d}%)',
color:"#fff"
}
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
data: datalist,
},
],
}
return option
} else if (type === 'bar') {
var seriesData = []
var xAxisData = []
for (let i in data) {
seriesData.push(data[i].length)
xAxisData.push(i)
}
let option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
grid: {
left: '3%',
right: '4%',
containLabel: true,
},
xAxis: {
type: 'category',
data: xAxisData,
},
yAxis: {
type: 'value',
},
series: [
{
data: seriesData,
type: 'bar',
barWidth: '30px',
},
],
}
return option
}else if (type === 'funnel') {
var seriesData = []
var xAxisData = []
for (let i in data) {
var arr = {
value: data[i],
name: i
}
seriesData.push(arr)
}
let option = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c}%',
},
legend: {
data: ['Show', 'Click', 'Visit', 'Inquiry', 'Order'],
},
series: [
{
name: 'Funnel',
type: 'funnel',
left: '10%',
top: 60,
bottom: 60,
width: '80%',
min: 0,
max: 100,
minSize: '50%',
maxSize: '80%',
sort: 'descending',
gap: 2,
label: {
show: true,
position: 'inside',
},
labelLine: {
length: 10,
lineStyle: {
width: 1,
type: 'solid',
},
},
itemStyle: {
borderColor: '#fff',
borderWidth: 1,
},
emphasis: {
label: {
fontSize: 20,
},
},
data: seriesData,
},
],
}
return option
}
}
handeldataSource = (data) => {
// key
const newdataSoure = data['data'].map((item, key) => {
return { ...item, key }
})
return newdataSoure
}
handelcolumns = (data) => {
const newcolumns = []
for (let i in data['level_list']) {
var arr = {
title: data['level_list'][i],
dataIndex: i,
key: i,
}
newcolumns.push(arr)
}
//
newcolumns[0]['fixed'] = 'left'
return newcolumns
}
handelexport = ()=>{
const { url, tableid, time } = this.state
request('ExportPost', 'forms/' + url, {
data_in: { table_id: tableid },
interview_query: {
start_time: time[0],
end_time: time[1],
out_form: 'out'
},
name: this.state.data.table_data.title
}).then(
(res) => {
console.log(res)
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
}
render() {
const { data } = this.state
return (
<div className="Kanbandetails-box">
<div className="Kanbandetails-top-box">
<div className="Reportcenter-top-cz-box">
<div className="cursor" onClick={this.handelback}>
<LeftOutlined /> 返回
</div>
<div>
<Button type="text" disabled>
<EditOutlined />
编辑
</Button>
<Button type="text" onClick={this.handelexport}>
<CloudDownloadOutlined /> 导出
</Button>
<Button type="text" disabled>
<TeamOutlined />
共享
</Button>
<Button type="text" disabled>
<MailOutlined />
订阅
</Button>
<Button type="text" disabled>
<MoreOutlined /> 更多
</Button>
</div>
</div>
</div>
<div className="Kanbandetails-title-box">
<div className="Kanbandetails-title">
{data.table_data.title}
</div>
<p>最后更新时间{data.table_data.mode_time}</p>
</div>
<div className="Kanbandetails-actionbar-box">
<RangePicker
value={this.state.timevalue}
onChange={(dates, dateStrings) => {
this.setState({
time: dateStrings,
timevalue: dates,
},()=>{
this.postdata()
})
}}
locale={locale}
/>
</div>
<div className="Kanbandetails-table-box">
{JSON.stringify(data) != '{}' ? (
data.table_data.type === 'table' && data.table_data.size != "small" ? (
<Table
columns={data.table_data.columns}
dataSource={data.table_data.dataSource}
pagination={{ pageSize: 6 }}
scroll={{
x: 1600,
y: '100vh - 212px ',
}}
/>
) : data.table_data.type === 'echarts' ? (
<ReactEcharts
option={this.getOption(
data.table_data.charts,
data.data
)}
theme="Imooc"
style={{
height: '500px',
}}
/>
) : data.table_data.size === "small"? (
<div>
<div className="card-small-box">
<p className="title">
{data.table_data.title}
</p>
<p className="num" >{ data.data? data.data.value : 0}</p>
</div>
</div>
):''
) : (
<Empty description="暂无数据" />
)}
</div>
</div>
)
}
}

View File

@ -0,0 +1,49 @@
import React from 'react'
import { Select, Form, Input, Modal } from 'antd'
const { Option } = Select
export default function Moveto({ visible, onCreate, onCancel }) {
const [form] = Form.useForm()
return (
<Modal
visible={visible}
title="移动到"
okText="Create"
cancelText="Cancel"
onCancel={onCancel}
onOk={() => {
form.validateFields()
.then((values) => {
form.resetFields()
onCreate(values)
})
.catch((info) => {
console.log('Validate Failed:', info)
})
}}
>
<Form
form={form}
layout="vertical"
name="form_in_modal"
initialValues={{}}
>
<Form.Item
name="title"
label="分组"
rules={[
{
required: true,
message: '标题必填!',
},
]}
>
<Select>
<Option value="jack">Jack</Option>
<Option value="lucy">Lucy</Option>
<Option value="Yiminghe">yiminghe</Option>
</Select>
</Form.Item>
</Form>
</Modal>
)
}

View File

@ -0,0 +1,51 @@
import { Button, Form, Input, Modal, Radio } from 'antd'
import React, { useState } from 'react'
const { TextArea } = Input;
export default function Rename({ visible, onCreate, onCancel }) {
const [form] = Form.useForm()
return (
<Modal
visible={visible}
title="重命名"
okText="Create"
cancelText="Cancel"
onCancel={onCancel}
onOk={() => {
form.validateFields()
.then((values) => {
form.resetFields()
onCreate(values)
})
.catch((info) => {
console.log('Validate Failed:', info)
})
}}
>
<Form
form={form}
layout="vertical"
name="form_in_modal"
initialValues={{
}}
>
<Form.Item
name="title"
label="标题"
rules={[
{
required: true,
message: '标题必填!',
},
]}
>
<Input />
</Form.Item>
<Form.Item name="describe" label="描述">
<TextArea rows={4} placeholder='请输入描述最多100字' />
</Form.Item>
</Form>
</Modal>
)
}

View File

@ -0,0 +1,54 @@
.Reportcenter-top-box {
background-color: #fff;
padding: 16px 24px;
}
.Reportcenter-top-cz-box {
display: flex;
justify-content: space-between;
align-items: center;
}
.Reportcenter-table-overflow {
width: 100%;
height: calc(100vh - 180px);
}
.Reportcenter-table-box {
/* display: flex; */
margin: 0px 0px 24px 0;
padding-right: 24px;
}
.ant-card { margin-left: 24px; margin-top: 24px; border-radius: 4px; }
.table-top-box { display: flex; justify-content: space-between; align-items: center; }
.table-name { font-weight: bold; font-size: 16px; color: #312b2e; }
.card-small-box { margin-top: 24px; }
.card-small-box .title{
text-align: center;
font-size: 20px;
color: #75777d;
margin: 0;
}
.card-small-box .num {
text-align: center;
font-size: 30px;
color: #312b2e;
margin: 0;
}
.card-small-box2 {
text-align: center;
font-size: 20px;
color: #75777d;
margin: 10px 0;
height: 300px;
}
.Empty-box {
height: 130px;
margin: auto;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}

View File

@ -0,0 +1,508 @@
import React, { Component } from 'react'
import PubSub from 'pubsub-js'
import { Button, Row, Col, Card, Table, DatePicker, message, Empty } from 'antd'
import {
LeftOutlined,
MoreOutlined,
MailOutlined,
TeamOutlined,
CloudDownloadOutlined,
EditOutlined,
ArrowsAltOutlined,
} from '@ant-design/icons'
import { Link } from 'react-router-dom'
import { findmodetables } from '../../../../util/requestURL'
import request from '../../../../util/request'
import moment from 'moment'
//rcharts
import 'echarts/lib/chart/line'
import 'echarts/lib/chart/pie'
import 'echarts/lib/chart/bar'
import 'echarts/lib/component/tooltip'
import 'echarts/lib/component/title'
import 'echarts/lib/component/legend'
import 'echarts/lib/component/markPoint'
import ReactEcharts from 'echarts-for-react'
import 'moment/locale/zh-cn'
import locale from 'antd/es/date-picker/locale/zh_CN'
import './index.css'
const { RangePicker } = DatePicker
const dateFormat = 'YYYY/MM/DD'
export default class Reportcenter extends Component {
state = {
time: [
moment((Math.round(new Date() / 1000) - 7 * 86400) * 1000).format(
'YYYY-MM-DD'
),
moment(Math.round(new Date() / 1000) * 1000).format('YYYY-MM-DD'),
],
timevalue: [
moment(
moment(
(Math.round(new Date() / 1000) - 7 * 86400) * 1000
).format('YYYY-MM-DD'),
dateFormat
),
moment(
moment(Math.round(new Date() / 1000) * 1000).format(
'YYYY-MM-DD'
),
dateFormat
),
],
data: [],
id:''
}
componentWillUnmount() {
// echarts.registerTheme('Imooc',echartTheme);
}
componentDidMount() {
const urlParams = new URL(window.location.href)
const pathname = urlParams.hash
const pathnameArr = pathname.split('/')
const id = pathnameArr[pathnameArr.length - 1] //id
let { time } = this.state
this.setState({
id: id
},()=>{
this.postData(id,time)
})
PubSub.publish('headtitle', { headtitle: '报表中心' })
const { data } = this.state
}
postData=(id,time)=>{
findmodetables({ mode_id: id, update_data: {} }).then(
(res) => {
let dataList = res.data
console.log(dataList)
for (let i in dataList) {
request('post', 'forms/' + res.data[i]['url'], {
data_in: { table_id: dataList[i]['table_id'] },
interview_query: {
start_time: time[0],
end_time: time[1],
},
}).then(
(res) => {
dataList[i]['data'] = res.data.data
if (dataList[i]['type'] === 'table' && dataList[i]['size'] != "small") {
var dataSource = this.handeldataSource(
dataList[i]['data']
)
const columns = this.handelcolumns(
dataList[i]['data']
)
dataList[i]['dataSource'] = dataSource
dataList[i]['columns'] = columns
}
this.setState({
data: dataList,
})
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
}
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
}
handelback() {
window.history.back()
}
getOption = (type, data) => {
if (type === 'line') {
var seriesData = []
var xAxisData = []
var datalist = {}
for (let i in data) {
for (let j in data[i]) {
if (!datalist[j]) {
datalist[j] = []
}
datalist[j].push(data[i][j])
}
xAxisData.push(i)
}
for(let i in datalist){
var arr = {
name: i,
type: 'line', //typebar,pie
data: datalist[i],
}
seriesData.push(arr)
}
let option = {
tooltip: {
trigger: 'axis',
},
xAxis: {
data: xAxisData,
},
yAxis: {
type: 'value',
},
series: seriesData,
}
return option
} else if (type === 'pie') {
var datalist = []
for (let i in data) {
var arr = {}
arr['value'] = data[i].length
arr['name'] = i
datalist.push(arr)
}
let option = {
tooltip: {
trigger: 'item',
},
legend: {
top: '5%',
left: 'center',
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['40%', '70%'],
label: {
// show: false,
// position: 'center',
normal: {
show: true,
position: 'inner',
formatter: '{b}:{c}' + '\n\r' + '({d}%)',
color: '#fff',
},
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)',
},
},
data: datalist,
},
],
}
return option
} else if (type === 'bar') {
var seriesData = []
var xAxisData = []
for (let i in data) {
seriesData.push(data[i].length)
xAxisData.push(i)
}
let option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
grid: {
left: '3%',
right: '4%',
containLabel: true,
},
xAxis: {
type: 'category',
data: xAxisData,
},
yAxis: {
type: 'value',
},
series: [
{
data: seriesData,
type: 'bar',
barWidth: '30px',
},
],
}
return option
} else if (type === 'funnel') {
var seriesData = []
var xAxisData = []
for (let i in data) {
var arr = {
value: data[i],
name: i
}
seriesData.push(arr)
}
let option = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c}%',
},
legend: {
data: ['Show', 'Click', 'Visit', 'Inquiry', 'Order'],
},
series: [
{
name: 'Funnel',
type: 'funnel',
left: '10%',
top: 60,
bottom: 60,
width: '80%',
min: 0,
max: 100,
minSize: '50%',
maxSize: '80%',
sort: 'descending',
gap: 2,
label: {
show: true,
position: 'inside',
},
labelLine: {
length: 10,
lineStyle: {
width: 1,
type: 'solid',
},
},
itemStyle: {
borderColor: '#fff',
borderWidth: 1,
},
emphasis: {
label: {
fontSize: 20,
},
},
data: seriesData,
},
],
}
return option
}
}
handeldataSource = (data) => {
// key
const newdataSoure = data['data'].map((item, key) => {
return { ...item, key }
})
return newdataSoure
}
handelcolumns = (data) => {
// const newcolumns = data['level_list'].map((item,key)=>{
// return { title: item, dataIndex: key,key }
// })
const newcolumns = []
for (let i in data['level_list']) {
var arr = {
title: data['level_list'][i],
dataIndex: i,
key: i,
}
newcolumns.push(arr)
}
//
newcolumns[0]['fixed'] = 'left'
return newcolumns
}
render() {
const { data } = this.state
return (
<div className="Reportcenter-box">
<div className="Reportcenter-top-box">
<div className="Reportcenter-top-cz-box">
<div className="cursor" onClick={this.handelback}>
<LeftOutlined /> 返回
</div>
<div>
<Button type="text" disabled>
<EditOutlined />
编辑
</Button>
<Button type="text" disabled>
<CloudDownloadOutlined /> 导出
</Button>
<Button type="text" disabled>
<TeamOutlined />
共享
</Button>
<Button type="text" disabled>
<MailOutlined />
订阅
</Button>
<Button type="text" disabled>
<MoreOutlined /> 更多
</Button>
</div>
</div>
<div className="margintop20">
<RangePicker
locale={locale}
value={this.state.timevalue}
onChange={(dates, dateStrings) => {
this.setState({
time: dateStrings,
timevalue: dates,
},()=>{
this.postData(this.state.id, dateStrings)
})
}}
/>
</div>
</div>
<div className="Reportcenter-table-overflow">
<div className="Reportcenter-table-box">
<Row justify="start">
{data.map((item, key) => {
return item.size == 'small' ? (
<Col span={6} key={item.table_id}>
<Card>
<div className="table-top-box">
<Link className="table-name" to={`/admin/reportforms/Kanbandetails/${item.url}/${item.table_id}`}>
{item.title}
</Link>
<Link
className="cursor"
to={`/admin/reportforms/Kanbandetails/${item.url}/${item.table_id}`}
>
<ArrowsAltOutlined />
</Link>
</div>
<div className="card-small-box">
<p className="title">
{item.title}
</p>
<p className="num" >{ item.data? item.data.value : 0}</p>
</div>
</Card>
</Col>
) : item.size == 'in' ? (
<Col span={12} key={item.table_id}>
<Card>
<div className="table-top-box">
<Link className="table-name" to={`/admin/reportforms/Kanbandetails/${item.url}/${item.table_id}`}>
{item.title}
</Link>
<Link
className="cursor"
to={`/admin/reportforms/Kanbandetails/${item.url}/${item.table_id}`}
>
<ArrowsAltOutlined />
</Link>
</div>
<div className="card-small-box2">
{item.type === 'echarts' ? (
JSON.stringify(item.data) !=
'{}' ? (
<ReactEcharts
option={this.getOption(
item.charts,
item.data
)}
theme="Imooc"
style={{
height: '350px',
}}
/>
) : (
<div className="Empty-box">
<Empty
description={
'暂无数据'
}
/>
</div>
)
) : (
<Table
dataSource={
item.dataSource
}
columns={item.columns}
scroll={{
x: 1600,
y: 250,
}}
pagination={false}
/>
)}
</div>
</Card>
</Col>
) : (
<Col span={24} key={item.table_id}>
<Card>
<div className="table-top-box">
<div className="table-name">
{item.title}
</div>
<Link
className="cursor"
to={`/admin/reportforms/Kanbandetails/${item.url}/${item.table_id}`}
>
<ArrowsAltOutlined />
</Link>
</div>
<div
className="card-small-box2"
id={item.table_id}
>
{item.type === 'echarts' ? (
<ReactEcharts
option={this.getOption(
item.charts,
item.data
)}
theme="Imooc"
style={{
height: '350px',
}}
/>
) : (
''
// <Table
// dataSource={
// item.dataSource
// }
// columns={item.columns}
// scroll={{
// x: 1600,
// y: 250,
// }}
// pagination={false}
// />
)}
</div>
</Card>
</Col>
)
})}
</Row>
</div>
</div>
</div>
)
}
}

View File

@ -0,0 +1,72 @@
.Reportforms-top-box {
width: 100%;
padding: 24px;
background-color: #fff;
}
.Reportforms-top-title {
font-size: 22px;
font-weight: bold;
}
.Reportforms-top-action {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 8px;
font-size: 12px;
color: #625f69;
}
.Reportforms-top-action button { margin-left: 8px; }
.Reportforms-list-box {
margin: 16px 40px;
display: grid;
grid-template-columns: 340px 340px 340px 340px 340px;
grid-row-gap:16px;
grid-column-gap:16px;
}
.card-box {
width: 340px;
background-color: #fff;
padding: 20px 0px 0 0px;
border-radius: 4px;
box-shadow: 0 0 6px 0px rgba(000, 000, 000, 0.2);
}
.card-top-box {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
}
.card-top-box div {
cursor: pointer;
}
.card-top-title {
font-size: 20px;
font-weight: bold;
color: #312b2e;
}
.card-bottom-box {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 20px;
border-top: 1px solid #f6f6f5;
color: #8c8e93;
font-size: 13px;
}
.card-bottom-box div {
display: flex;
align-items: center;
}
.card-name-box{
margin-left: 6px;
}
.card-time {
margin-top: 35px;
color: #8c8e93;
font-size: 13px;
padding: 0 20px;
}

View File

@ -1,9 +1,255 @@
import React from 'react'
import React, { Component } from 'react'
import PubSub, { publish } from 'pubsub-js'
import { Button, Avatar, Dropdown, Menu, Drawer, Modal, message } from 'antd'
import { findmode } from '../../../util/requestURL'
import { Link } from 'react-router-dom'
import Rename from './Rename'
import Moveto from './Moveto'
import AddUser from './AddUser'
import {
PlusOutlined,
MoreOutlined,
TeamOutlined,
ExclamationCircleOutlined,
} from '@ant-design/icons'
import './index.css'
function Report_forms() {
return (
<div>报表中心</div>
export default class Report_forms extends Component {
state = {
Renamevisible: false,
Movetovisible: false,
AddUservisible: false,
datalist:[]
}
componentDidMount() {
PubSub.publish('headtitle', { headtitle: '报表分析' })
findmode({hr_uid:'16291104873568418', where: {}}).then(
(res) => {
console.log(res)
this.setState({
datalist: res.data
})
},
(error) => {
message.error('网络加载错误,请稍后再试')
}
)
}
export default Report_forms
}
onVisibleChange = (e) => {
console.log(e)
}
onMovetoCreate = (e) => {
this.setState({
Movetovisible: false,
})
}
onRenameCreate = (e) => {
this.setState({
Renamevisible: false,
})
}
onmoveto = (e) => {
this.setState({
Movetovisible: true,
})
}
onrename = (e) => {
this.setState({
Renamevisible: true,
})
}
AddUseronClose = () => {
this.setState({
AddUservisible: false,
})
}
handelAddUser = () => {
this.setState({
AddUservisible: true,
})
}
handelDelTable = () => {
console.log('点击删除')
}
pushReportcenter = (id) => {
// this.props.history.push(`/admin/reportforms/Reportcenter`,{id})
// this.props.history.push
console.log(this.props)
}
render() {
const {datalist} = this.state
const menu = (
<Menu
items={[
{
key: '1',
label: (
<a rel="noopener noreferrer" href="#">
另存为
</a>
),
},
{
key: '2',
label: (
<div onClick={() => this.onrename()}>重命名</div>
),
},
{
key: '3',
label: (
<div onClick={() => this.onmoveto()}>移动到</div>
),
},
{
key: '4',
label: (
<div className="color6" onClick={() => confirm()}>
删除
</div>
),
},
]}
/>
)
const confirm = () => {
Modal.confirm({
title: '删除',
icon: <ExclamationCircleOutlined />,
content: '确定删除当前看板',
okText: '确认',
cancelText: '取消',
onOk() {
console.log('OK')
},
})
}
return (
<div className="Reportforms-box">
<div className="Reportforms-top-box">
<div className="Reportforms-top-title">报表中心</div>
<div className="Reportforms-top-action">
<div>
legu报表中心旨在自动化的检索分析各类指标情况并进行可视化的展示提高洞察力帮助您做出更好的决策
</div>
<div>
<Button type="primary">
{' '}
<PlusOutlined /> 新建
</Button>
<Button>分组管理</Button>
</div>
</div>
</div>
<div className="Reportforms-list-box">
{
datalist.map((item,key)=>{
return (
<div className="card-box" key={key}>
<div className="card-top-box">
<Link
to={`/admin/reportforms/Reportcenter/${item.mode_id}`}
className="card-top-title"
onClick={() => this.pushReportcenter(item.mode_id)}
>
{item.mode_name}
</Link>
<div>
<Dropdown
overlay={menu}
placement="bottom"
onVisibleChange={(e) => {
this.onVisibleChange('1')
}}
>
<MoreOutlined
style={{
fontSize: '16px',
color: '#5e5e5e',
}}
/>
</Dropdown>
</div>
</div>
<p className="card-time">
最近修改时间 {item.mode_time}
</p>
<div className="card-bottom-box">
<div>
<Avatar
size="small"
icon={item.hr_name.charAt(0)}
style={{
backgroundColor: '#319dff',
}}
/>
<span className="card-name-box">
{item.hrname} 所有
</span>
</div>
<div
className="cursor"
onClick={() => this.handelAddUser()}
>
<TeamOutlined />
</div>
</div>
</div>
)
})
}
</div>
<Rename
visible={this.state.Renamevisible}
onCreate={this.onRenameCreate}
onCancel={() => {
// setVisible(false)
this.setState({
Renamevisible: false,
})
}}
/>
<Moveto
visible={this.state.Movetovisible}
onCreate={this.onMovetoCreate}
onCancel={() => {
// setVisible(false)
this.setState({
Movetovisible: false,
})
}}
/>
<Drawer
title="共享"
placement="right"
onClose={() => this.AddUseronClose()}
visible={this.state.AddUservisible}
>
<AddUser />
</Drawer>
</div>
)
}
}

View File

@ -0,0 +1,23 @@
.Resumescreen-box {
margin: 60px 230px;
}
.Resumescreen-list-data-box {
background-color: #fff;
min-height: 600px;
}
.Resumescreen-list-tab-box {
margin: 0 30px;
}
.Resumescreen-list-tab-box .ant-tabs-tab-btn {
color: #312b2e !important;
}
.Resumescreen-list-tab-box .num {
font-size: 17px;
}
.Resumescreen-list-tab-box .ant-tabs { width: 100%; }
.Resumescreen-list-table-box { margin: 0 30px; }
.Resumescreen-list-data-box .CandidateTable-owner { width: 10%; }
.Resumescreen-list-data-box .CandidateTable-list-box { align-items: center; }
.Resumescreen-list-data-box .CandidateTable-info { margin-left: 0; }

View File

@ -0,0 +1,415 @@
import React, { Component } from 'react'
import {
Input,
Space,
Form,
Select,
Col,
Row,
Tabs,
Table,
Empty,
Checkbox,
Tag,
Button,
message,
} from 'antd'
import { AudioOutlined, ShoppingFilled, BankOutlined } from '@ant-design/icons'
import PubSub from 'pubsub-js'
import {
getjob,
interviewfind,
interviewupdate,
interviewmannernum,
} from '../../../util/requestURL'
import storageUtils from '../../../util/storageUtils'
import InterviewerInfoPop from '../../../components/InterviewerInfoPop'
import './index.css'
const { Search } = Input
const { Option } = Select
export default class Resumescreen extends Component {
state = {
getjoblist: {},
data: [],
name: '',
job_id: '',
hr_manner: 2,
userInfo: storageUtils.getUser(),
interviewerInfo: false,
uid: 0
}
componentDidMount() {
PubSub.publish('headtitle', { headtitle: '简历筛选' })
getjob().then(
(res) => {
this.setState({
getjoblist: res.data.job,
})
},
(err) => {}
)
this.Updatalist()
}
Updatalist = () => {
const { name, job_id, hr_manner, userInfo } = this.state
interviewfind({
interview_query: {
interview_stage: 1,
department: userInfo.user_id,
name: name,
job_id: job_id,
hr_manner: hr_manner,
},
data_in: {},
}).then(
(res) => {
this.setState({
data: res.data,
})
},
(err) => {}
)
interviewmannernum({
interview_query: {
interview_stage: 1,
department: userInfo.user_id,
name: name,
job_id: job_id
},
data_in: {},
}).then(
(res) => {
console.log(res.data)
this.setState({
manner_2_num : res.data.manner_2_num,
manner_01_num : res.data.manner_01_num
})
},
(err) => {}
)
}
onValuesChange = (e) => {
// console.log(e)
this.setState(e,()=>{
this.Updatalist()
})
}
handeleditstate = (uid, hr_manner) => {
interviewupdate({
data_in: { hr_manner: hr_manner, interview_stage:2 },
interview_query: { uid: uid },
}).then(
(res) => {
if (res.msg === 'ok') {
message.success('操作成功!')
setTimeout(() => {
this.Updatalist()
}, 1000)
}
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
}
handelonChange = (e) => {
this.setState(
{
hr_manner: e,
},
() => {
this.Updatalist()
}
)
}
handelCandidateClick = (uid) =>{
console.log(uid)
this.setState({
interviewerInfo: true,
uid : uid
})
}
render() {
const { getjoblist, data, manner_2_num, manner_01_num } = this.state
const onSearch = (value) => console.log(value)
return (
<div className="Resumescreen-box">
<div className="">
<Form
layout="vertical"
onValuesChange={this.onValuesChange}
>
<Row gutter={24}>
<Col className="gutter-row" span={4}>
<Form.Item label="候选人" name="name">
<Search
placeholder="请输入候选人姓名"
onSearch={onSearch}
/>
</Form.Item>
</Col>
<Col className="gutter-row" span={4}>
<Form.Item label="应聘职位" name="job_id">
<Select>
<Option value="">全部职位</Option>
{getjoblist.length > 0
? getjoblist.map((item, key) => {
return (
<Option
value={item.key}
key={key}
>
{item.job_name}
</Option>
)
})
: ''}
</Select>
</Form.Item>
</Col>
<Col className="gutter-row" span={4}>
<Form.Item label="筛选状态" name="hr_manner">
<Select>
<Option value={''}>全部</Option>
<Option value={1}>通过</Option>
<Option value={0}>拒绝</Option>
{/* <Option value="Yiminghe">待定</Option> */}
</Select>
</Form.Item>
</Col>
</Row>
</Form>
</div>
<div className="Resumescreen-list-data-box">
<div className="Resumescreen-list-tab-box">
<Tabs
defaultActiveKey="2"
onChange={(e) => this.handelonChange(e)}
>
<Tabs.TabPane
tab={
<div className="tabs-box">
<div className="num">
{manner_2_num}
</div>
<div>待筛选的候选人</div>
</div>
}
key={2}
></Tabs.TabPane>
<Tabs.TabPane
tab={
<div className="tabs-box">
<div className="num">
{manner_01_num}
</div>
<div>筛选记录</div>
</div>
}
key=""
></Tabs.TabPane>
</Tabs>
</div>
<div className="Resumescreen-list-table-box">
<div className="CandidateTable-list-box ">
<div className="CandidateTable-info-box">
基本资料
</div>
<div className="CandidateTable-owner">申请日期</div>
<div className="CandidateTable-owner">应聘职位</div>
{/* <div className='CandidateTable-owner'>推荐详情</div> */}
<div className="CandidateTable-owner">
候选人状态
</div>
<div className="CandidateTable-owner">反馈结果</div>
</div>
<div style={{ width: 'calc(100% - 4px)' }}>
{data.length > 0 ? (
data.map((item, key) => {
return (
<div
className="CandidateTable-list-box"
key={item.uid}
onClick={() =>
this.handelCandidateClick(item.uid)
}
>
<div className="CandidateTable-info-box">
<div className="CandidateTable-info">
<div>
<span className="name">
{item.name}
</span>
<span>
{' '}
{item.gender} |
</span>
<span>
{' '}
{item.age} |{' '}
</span>
{item.work_exp == 0 ? (
<span>无经验</span>
) : (
<span>
{item.work_exp}
工作经验
</span>
)}
</div>
{item['work_list'].map(
(items, key) => {
return (
<div key={key}>
<ShoppingFilled />
<span>
{
items.company_name
}{' '}
|{' '}
{
items.position_name
}{' '}
|{' '}
{
items.time
}
</span>
</div>
)
}
)}
<div>
<BankOutlined />
{item.school != '' ? (
<span>
{' '}
{item.school}
</span>
) : (
''
)}
{item.specialty !=
'' ? (
<span>
{' '}
|{' '}
{item.specialty}
</span>
) : (
''
)}
{item.education !=
'' ? (
<span>
{' '}
|{' '}
{item.education}
</span>
) : (
''
)}
{item.graduate_time !=
'' ? (
<span>
{' '}
|{' '}
{
item.graduate_time
}
</span>
) : (
''
)}
</div>
<div>
<Tag color="processing">
{item.education}
</Tag>
</div>
</div>
</div>
<div className="CandidateTable-owner">
<span>{item.star_time}</span>
</div>
<div className="CandidateTable-owner">
<span>{item.job_names}</span>
</div>
{/* <div className="CandidateTable-owner">
<span>推荐详情</span>
</div> */}
<div className="CandidateTable-owner">
<span>
{item.interview_stage}
</span>
</div>
<div className="CandidateTable-owner">
<Button
type="text"
style={{ color: '#0c8cf6' }}
onClick={(e) => {
e.stopPropagation()
this.handeleditstate(
item.uid,
1
)
}}
>
同意
</Button>
<Button
type="text"
style={{ color: '#0c8cf6' }}
onClick={(e) => {
e.stopPropagation()
this.handeleditstate(
item.uid,
0
)
}}
>
拒绝
</Button>
</div>
</div>
)
})
) : (
<div className="margintop30">
<Empty description={'暂无数据'} />
</div>
)}
</div>
</div>
</div>
{/* 查看面试人信息弹窗 */}
<InterviewerInfoPop
visible={this.state.interviewerInfo}
onCreate={() => {
this.setState({
interviewerInfo: false,
})
}}
data={this.state.uid}
onCancel={() => {
this.setState({
interviewerInfo: false,
})
}}
/>
</div>
)
}
}

View File

@ -0,0 +1,68 @@
import React from 'react'
import { Form, Input, message, Modal } from 'antd'
import { set_userinfo } from '../../../../util/requestURL'
export default function Editpassword({ visible, onCreate, onCancel }) {
const [form] = Form.useForm()
return (
<div className="Editpassword-box">
<Modal
visible={visible}
title="修改密码"
okText="确认"
cancelText="取消"
onCancel={onCancel}
onOk={() => {
form.validateFields()
.then((values) => {
form.resetFields()
if(values.newpassword === values.confirmpassword){
set_userinfo({password:values.newpassword,nickname:"",tel:"",email:''}).then(
(res) => {
message.success('修改成功!')
},
(err) => {
message.error('网络出错,请稍后再试!')
}
)
onCreate(values)
}else {
message.error('两次密码输入不一致!')
}
})
.catch((info) => {
console.log('Validate Failed:', info)
})
}}
>
<Form form={form} layout="vertical" name="form_in_modal">
<Form.Item
name="newpassword"
label="新密码"
rules={[
{
required: true,
message: '请填写修改后的密码!',
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="confirmpassword"
label="确认密码"
rules={[
{
required: true,
message: '请填写修改后的密码!',
},
]}
>
<Input />
</Form.Item>
</Form>
</Modal>
</div>
)
}

View File

@ -0,0 +1,5 @@
.set-form-box {
margin: 60px;
}
.set-form-button-box { margin-left: 580px; }
.set-form-button-box button { margin-left: 10px; }

View File

@ -1,8 +1,145 @@
import React from 'react'
import React, { useState, useEffect } from 'react'
import PubSub from 'pubsub-js'
import { Form, Input, Button, InputNumber, message } from 'antd'
import Editpassword from './Editpassword'
import storageUtils from '../../../util/storageUtils'
import { set_userinfo } from '../../../util/requestURL'
import './index.css'
function Set_up() {
const [componentDisabled, setComponentDisabled] = useState(true)
const [userInfo, setuserInfo] = useState(storageUtils.getUser())
const [ismmpop, setismmpop] = useState(false)
const [postdata, setposrdata] = useState({
password: '',
nickname: '',
tel: '',
email: '',
})
useEffect(() => {
PubSub.publish('headtitle', { headtitle: '设置' })
}, [])
const onFormLayoutChange = (disabled) => {
// message.error("")
setComponentDisabled(disabled)
if (disabled) {
if (
postdata.password === '' &&
postdata.nickname === '' &&
postdata.tel === '' &&
postdata.email === ''
) {
} else {
set_userinfo(postdata).then(
(res) => {
message.error('修改成功!请退出后重新登录')
},
(err) => {
message.error('网络请求错误,请稍后再试!')
}
)
}
}
}
return (
<div>设置</div>
<div className="set-box">
<div className="set-form-box">
<Form
labelCol={{
span: 4,
}}
wrapperCol={{
span: 14,
}}
layout="horizontal"
initialValues={userInfo}
>
<Form.Item label="账号" name={'name'}>
<Input style={{ width: 304 }} disabled={true} />
</Form.Item>
<Form.Item label="用户名" name={'nickname'}>
<Input
style={{ width: 304 }}
disabled={componentDisabled}
onChange={(e) => {
setposrdata({
...postdata,
nickname: e.target.value,
})
}}
/>
</Form.Item>
<Form.Item label="电话" name={'tel'}>
<InputNumber
style={{ width: 304 }}
disabled={componentDisabled}
onChange={(e) => {
setposrdata({
...postdata,
tel: e,
})
}}
/>
</Form.Item>
<Form.Item label="邮箱" name={'email'}>
<Input
style={{ width: 304 }}
disabled={componentDisabled}
onChange={(e) => {
setposrdata({
...postdata,
email: e.target.value,
})
}}
/>
</Form.Item>
<Form.Item label="密码">
<Button
disabled={componentDisabled}
onClick={() => {
console.log(123456789)
setismmpop(true)
}}
>
更改密码
</Button>
</Form.Item>
<div className="set-form-button-box">
{componentDisabled ? (
<Button
onClick={() => {
onFormLayoutChange(false)
message.warning("修改信息后需要重新登录!")
}}
>
编辑
</Button>
) : (
<Button
type="primary"
onClick={() => {
onFormLayoutChange(true)
}}
>
保存
</Button>
)}
</div>
</Form>
</div>
<Editpassword
visible={ismmpop}
onCreate={() => {
setismmpop(false)
}}
onCancel={() => {
setismmpop(false)
}}
/>
</div>
)
}

View File

@ -0,0 +1,5 @@
.Addscreen-box {
width: 200px;
height: 100px;
background-color: #000;
}

View File

@ -0,0 +1,62 @@
import { Select, Modal, message } from 'antd'
import React, { useState, useEffect } from 'react'
import { up_option } from '../../../../util/requestURL'
import './index.css'
const { Option } = Select
export default function Addscreen({ visible, data, onCancel }) {
const [defaultValue, setdefaultValue] = useState([])
useEffect(() => {
const isCheck = []
data.map((item, key) => {
if (item.type) return isCheck.push(item.id)
})
setdefaultValue(isCheck)
}, [visible])
const handleChange = (value) => {
console.log(value)
setdefaultValue(value)
}
return (
<Modal
title="添加筛选项"
visible={visible}
onCancel={onCancel}
okText="确定"
cancelText="取消"
onOk={() => {
up_option({where:defaultValue}).then(
(res) => {
message.success('更改成功')
onCancel()
},
(err) => {
message.error('网络加载错误,请稍后再试')
}
)
}}
>
<Select
mode="multiple"
style={{
width: '100%',
}}
placeholder="select one country"
value={defaultValue}
onChange={handleChange}
optionLabelProp="label"
>
{data.map((item, key) => {
return (
<Option value={item.id} key={key}>
{item.name}
</Option>
)
})}
</Select>
</Modal>
)
}

View File

@ -0,0 +1,192 @@
.talentpool-Empty-box { height: 122px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; }
.talentpool-box {
display: flex;
justify-content: space-between;
height: 100%;
}
.talentpool-left-box {
width: 250px;
height: 100%;
}
.talentpool-right-box {
width: calc(100% - 250px);
background-color: #fff;
height: 100%;
}
.filter_criteria-top-box {
padding: 10px 16px;
background-color: #e9eaec;
display: flex;
justify-content: space-between;
align-items: center;
color: #008bff;
}
.filter_criteria-top-img {
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
border-radius: 50%;
background-color: #fff;
font-size: 17px;
color: #008bff;
margin-right: 8px;
}
.filter_criteria-top-left-box{
color: #008bff;
}
.talentpool-add-screen-box {
padding: 20px 16px;
box-shadow: 1px 1px 4px rgb(0 0 0 / 8%);
}
.talentpool-add-screen-box p {
color: #333;
}
.talentpool-add-screen-tab-box {
display: flex;
justify-content: space-between;
align-items: center;
}
.talentpool-add-but-box {
color: #008bff;
cursor: pointer;
}
.talentpool-screen-list-box {
height: calc(100% - 150px);
}
.talentpool-content-box {
padding: 30px 24px;
}
.talentpool-title {
font-size: 24px;
color: #4b505d;
}
.talentpool-tishi {
font-size: 14px;
color: #4b505d;
}
.talentpool-tab-box {
display: flex;
justify-content: space-between;
align-items: center;
}
.talentpool-tab {
width: 30%;
padding: 26px 18px;
border: 1px solid #f1f1f1;
border-radius: 4px;
margin-top: 18px;
box-shadow: 1px 1px 4px rgb(0 0 0 / 8%);
}
.talentpool-tab .tit {
font-size: 26px;
font-weight: bold;
}
.talentpool-con-box {
display: flex;
justify-content: space-between;
align-items: center;
}
.talentpool-con-box .tit {
font-size: 16px;
}
.talentpool-con-box .data {
font-size: 14px;
color: #96989d;
}
.talentpool-con-box .data span {
color: #008bff;
}
.qxcheck-box {
border: 0;
}
.talentpool-screen-list-select {
width: 100%;
margin-top: 10px;
}
.talentpool-list-box {
height: 570px;
/* overflow: hidden; */
/* height: calc(100% - 32px); */
overflow-y: auto;
position: relative;
}
.talentpool-screen-list {
padding: 20px 16px;
border-bottom: 1px solid #e1e1e1;
}
.talentpool-screen-list-tit {
display: flex;
justify-content: space-between;
align-items: center;
}
.talentpool-table-action {
display: flex;
border-bottom: 1px solid #f4f4f5; padding: 10px 0;
}
.talentpool-info-box {
display: flex;
justify-content: space-between;
padding: 20px 18px;
}
.talentpool-info {
margin-left: 34px;
}
.talentpool-info .name {
font-size: 16px;
color: #4b505d;
font-weight: bold;
}
.talentpool-info .sex {
color: #9294a2;
margin-left: 6px;
}
.talentpool-info .age {
color: #9294a2;
margin-left: 6px;
}
.talentpool-info .exp {
color: #9294a2;
margin-left: 6px;
}
.similar {
display: inline-block;
width: 20px;
height: 20px;
text-align: center;
line-height: 20px;
font-size: 12px;
background-color: #008bff;
color: #fff;
border-radius: 50%;
margin-left: 6px;
}
.similar-num {
color: #008bff;
}
.work-exp-box {
color: #9294a2;
margin-top: 6px;
}
.work-exp-box span {
margin-right: 6px;
}
.apply_info-box {
color: #4b505d;
}
.apply_info-box span {
color: #312b2e;
}
.talentpool-list-left {
display: flex;
}
.page-box {
margin-top: 12px;
display: flex;
justify-content: end;
}

View File

@ -1,9 +1,471 @@
import React from 'react'
import React, { Component } from 'react'
import {
Checkbox,
Select,
Input,
Empty,
message,
Pagination,
Modal,
} from 'antd'
import Addscreen from './Addscreen'
import {
option_data,
talent_pool,
talent_pool_nu,
option,
get_number
} from '../../../util/requestURL'
import {
PrinterFilled,
DownOutlined,
FunnelPlotOutlined,
CaretDownFilled,
BlockOutlined,
CalendarFilled,
BankFilled,
CloseOutlined,
} from '@ant-design/icons'
import PubSub from 'pubsub-js'
import './index.css'
const { Option } = Select
const { confirm } = Modal
function Talent_pool() {
return (
<div>人才库</div>
export default class Talent_pool extends Component {
state = {
isaddscreen: false,
data: [],
screencondition: {},
datalist: [],
pages: 1,
total: 0,
candidate:0,
now:0
}
componentDidMount() {
PubSub.publish('headtitle', { headtitle: '人才库' })
get_number().then((res)=>{
console.log(res)
this.setState({
candidate: res.data.candidate,
now: res.data.now
})
},
(err)=>{
message.error('网络错误,请稍后再试')
})
this.handelupdatascreen()
this.posttalentpool()
}
handelAddscreen = () => {
// console.log(1111)
this.setState({ isaddscreen: true })
}
handelupdatascreen = () => {
option().then(
(res) => {
var data = res.data
var where = []
for (let i in data) {
if (data[i]['type'] && data[i]['style'] === 'select') {
where.push(data[i]['id'])
}
}
option_data({ where }).then(
(res) => {
const datalist = res.data
const newdata = data.map((item, key) => {
if (datalist[item.id]) {
return { ...item, list: datalist[item.id] }
} else {
return item
}
})
this.setState({ data: newdata })
},
(err) => {
message.error('网络错误,请稍后再试')
}
)
}
},
(err) => {
message.error('网络错误,请稍后再试')
}
)
}
export default Talent_pool
handelobtainscreen = (e, id) => {
const { screencondition } = this.state
for (let i in screencondition) {
if (i === id) {
screencondition[i] = e
}
}
screencondition[id] = e
// console.log(screencondition)
this.setState({ screencondition: screencondition }, () => {
this.posttalentpool()
})
}
posttalentpool = (e, id) => {
const { screencondition, pages } = this.state
talent_pool({ date: screencondition, pages: pages }).then(
(res) => {
console.log(res.data)
this.setState({
datalist: res.data,
})
},
(err) => {
message.error('网络错误,请稍后再试')
}
)
talent_pool_nu({ date: screencondition }).then(
(res) => {
// console.log(res.data)
this.setState({
total: res.data,
})
},
(err) => {
message.error('网络错误,请稍后再试')
}
)
}
onChangePage = (e) => {
this.setState(
{
pages: e,
},
() => {
this.posttalentpool()
}
)
// console.log(e)
}
render() {
const { data, datalist,candidate,now } = this.state
return (
<div className="talentpool-box">
{/* <Empty description="该功能暂未开放" className='talentpool-Empty-box'/> */}
<div className="talentpool-left-box">
<div className="filter_criteria-top-box">
<div className="divdisplay filter_criteria-top-left-box">
<div className="filter_criteria-top-img">
<PrinterFilled />
</div>
<span className="">全部 {candidate}</span>
</div>
<div>
<DownOutlined />
</div>
</div>
<div className="talentpool-add-screen-box">
<p>筛选条件</p>
<div className="talentpool-add-screen-tab-box">
<div
className="talentpool-add-but-box"
onClick={() => {
this.handelAddscreen()
}}
>
+ 添加筛选项
</div>
<div>
<FunnelPlotOutlined />
</div>
</div>
</div>
<div className="talentpool-screen-list-box">
{data.map((item, key) => {
return item.type ? (
item.style === 'select' ? (
<div
className="talentpool-screen-list"
key={key}
>
<div className="talentpool-screen-list-tit">
<span>{item.name}</span>
<CloseOutlined />
</div>
<div className="talentpool-screen-list-select">
<Select
defaultValue=""
style={{
width: 218,
}}
onChange={(e) => {
this.handelobtainscreen(
e,
item.id
)
}}
>
{item.list
? item.list.map(
(items, keys) => {
return (
<Option
value={
items.id
}
key={
keys
}
>
{
items.name
}
</Option>
)
}
)
: ''}
</Select>
</div>
</div>
) : (
<div
className="talentpool-screen-list"
key={key}
>
<div className="talentpool-screen-list-tit">
<span>{item.name}</span>
<CloseOutlined />
</div>
<div className="talentpool-screen-list-select">
<Input
placeholder={
'请输入' + item.name
}
onChange={(e) => {
this.handelobtainscreen(
e.target.value,
item.id
)
}}
/>
</div>
</div>
)
) : (
''
)
})}
</div>
</div>
<div className="talentpool-right-box">
<div className="talentpool-content-box">
<div className="talentpool-title">人才库概览</div>
<div className="talentpool-tishi">
列表中显示候选人数量上限10000人您可以使用左方的筛选栏位快速寻找符合需求的候选人
</div>
<div className="talentpool-tab-box">
<div className="talentpool-tab">
<div className="tit">{candidate}</div>
<div className="talentpool-con-box">
<div className="tit">候选人总数</div>
<div className="data">
今日归档人数<span>{now}</span>{' '}
</div>
</div>
</div>
{/* <div className='talentpool-tab'>
<div className='tit'>22</div>
<div className='talentpool-con-box'>
<div className='tit'>候选人总数</div>
<div className='data'>今日归档人数<span>0</span> </div>
</div>
</div> */}
</div>
<div className="talentpool-list-box">
<div className="talentpool-table-action">
<div className="qxcheck-box">
<Checkbox>
<CaretDownFilled
style={{ color: '#424954' }}
/>
</Checkbox>
</div>
</div>
<div>
{datalist.map((item, key) => {
return (
<div
className="talentpool-info-box"
key={key}
>
<div className="talentpool-list-left">
<div>
<Checkbox></Checkbox>
</div>
<div className="talentpool-info">
<div>
<span className="name">
{item.name}
</span>
<span className="sex">
{item.gander}
</span>
<span className="age">
| {item.age}
</span>
<span className="exp">
{item.work_exp === 0
? '| 实习生'
: item.work_exp ===
1
? '1-3年'
: item.work_exp ===
2
? '3-5年'
: '5年以上'}
</span>
<span className="similar">
<BlockOutlined />{' '}
</span>{' '}
<span className="similar-num">
{item.similarity}
位相似候选人
</span>
</div>
{item.work_list.map(
(items, index) => {
return (
<div
className="work-exp-box"
key={index}
>
<CalendarFilled />
<span>
{
items.company_name
}
</span>
<span>
|{' '}
{
items.position_name
}
</span>
<span>
|
{
items.time
}
</span>
</div>
)
}
)}
<div className="work-exp-box">
<BankFilled />
<span>
{item.school}
</span>
<span>
| {item.specialty}
</span>
<span>
| {item.education}
</span>
<span>
|{item.at_school}
</span>
</div>
</div>
</div>
<div className="apply_info-box">
<div>
申请日期
<span>
{item.event_time}
</span>
</div>
<div className="margintop5">
应聘职位
<span>{item.job_name}</span>
</div>
<div className="margintop5">
归档人才库
<span>系统公共人才库</span>
</div>
</div>
<div className="apply_info-box">
<div>
归档日期
<span>
{item.pass_time}
</span>
</div>
<div className="margintop5">
归档前阶段
<span>
{item.ago === 0
? '人才推荐'
: item.ago === 1
? '初筛'
: item.ago === 2
? '复筛'
: item.ago === 3
? '面试'
: item.ago === 4
? '沟通Offer'
: '待入职'}
</span>
</div>
<div className="margintop5">
归档原因
<span>
{item.pass_why}
</span>
</div>
</div>
</div>
)
})}
</div>
</div>
<div className="page-box">
<Pagination
defaultCurrent={1}
current={1}
total={this.state.total}
onChange={(e) => {
this.onChangePage(e)
}}
/>
</div>
</div>
</div>
<Addscreen
visible={this.state.isaddscreen}
data={this.state.data}
onCancel={() => {
this.setState({
isaddscreen: false,
},()=>{
this.handelupdatascreen()
})
}}
/>
</div>
)
}
}

View File

@ -1,4 +1,34 @@
.components-form-demo-normal-login { width: 300px; height: 222px; position: fixed; top: 0; bottom: 0; left: 0; right: 0; margin: auto; }
.login-box { background: url('../images/bg_login.png'); width: 100%;height: 100%;}
.login-boxs {width: 69%;
height: 70%;
position: absolute;
margin: auto;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: #fff;
border-radius: 8px;
display: flex;
}
.login-left {
width: 55.5%;
height: 100%;
background-color: #e0f1ff;
border-radius: 8px 0 0 8px;
line-height: 100%;
display: flex;
background: url('../images/left-logo.jpg');
}
.components-form-demo-normal-login {width: 44.5%;
height: 150px; position: absolute; top: 90px; bottom: 0; right: 70px; margin: auto; }
/* .components-form-demo-normal-login input { height: 40px; max-height: 40px; } */
.login-logo { background: url('../images/logo_2.png') no-repeat; width: 70%; height: 75px; position: absolute; top: -170px; right: -40px;}
.components-form-demo-normal-login .login-form {
max-width: 300px;

View File

@ -3,6 +3,6 @@ import React from 'react'
import Redirect from './Redirect'
export default function AuthComponent({children }) {
const islogin = localStorage.getItem("token");
const islogin = localStorage.getItem("userInfo");
return islogin ? children : <Redirect to="/login" />
}

View File

@ -1,5 +1,5 @@
import React from 'react'
import {useRoutes, Navigate} from 'react-router-dom'
import { useRoutes, Navigate } from 'react-router-dom'
import AppLayOut from '../AppLayOut'
import AuthComponent from './AuthComponent'
import Login from '../pages/Login'
@ -12,27 +12,65 @@ import SetUp from '../pages/admin/Set_up'
import TalentPool from '../pages/admin/Talent_pool'
import Endrecruitment from '../pages/admin/Position/Endrecruitment'
import Inrecruitment from '../pages/admin/Position/Inrecruitment'
import AddPosition from '../components/AddPosition'
import Reportcenter from '../pages/admin/Report_forms/Reportcenter'
import Kanbandetails from '../pages/admin/Report_forms/Kanbandetails'
import Resumescreen from '../pages/admin/Resumescreen'
import Interview_manager from '../pages/admin/Interview_manager'
// import PageNotFound from '../pages/PageNotFound'
function GlobalRouter() {
return useRoutes([
{
path: '/',
element: <AuthComponent> <AppLayOut /> </AuthComponent> ,
element: (
<AuthComponent>
{' '}
<AppLayOut />{' '}
</AuthComponent>
),
children: [
{ path: '/', element: <Overview /> },
{ path: '/admin/candidate',element: <Candidate />,},
{ path: '/admin/candidate', element: <Candidate /> },
{
path: `/admin/resumescreen`,
element: <Resumescreen />,
},
{
path: `/admin/Interview_manager`,
element: <Interview_manager />,
},
{ path: '/admin/interview', element: <Interview /> },
{ path: '/admin/position', element: <Position />, children:[
{ index:true, element: <Inrecruitment /> },
{ path: '/admin/position/Endrecruitment', element: <Endrecruitment /> }
]},
{
path: '/admin/position',
element: <Position />,
children: [
{ index: true, element: <Inrecruitment /> },
{
path: '/admin/position/Endrecruitment',
element: <Endrecruitment />,
},
],
},
{ path: '/admin/reportforms', element: <ReportForms /> },
{ path: '/admin/setup', element: <SetUp /> },
{ path: '/admin/talentPool', element: <TalentPool /> },
{
path: `/admin/position/addposition/:id`,
element: <AddPosition />,
},
{
path: `/admin/position/addposition`,
element: <AddPosition />,
},
{
path: `/admin/reportforms/Reportcenter/:id`,
element: <Reportcenter />,
},{
path:'/admin/reportforms/Kanbandetails/:url/:id',
element: <Kanbandetails />,
}
],
},
{ path: '/login', element: <Login /> },

12
src/setupProxy.js Normal file
View File

@ -0,0 +1,12 @@
const { createProxyMiddleware } = require('http-proxy-middleware')
// https://legu-cdn-source.obs.cn-east-2.myhuaweicloud.com
module.exports = function(app){
debugger
app.use(
createProxyMiddleware('/pdf',{ //遇见/api1前缀的请求就会触发该代理配置
target:'https://legu-cdn-source.obs.cn-east-2.myhuaweicloud.com', //请求转发给谁
changeOrigin:true,//控制服务器收到的请求头中Host的值
pathRewrite:{'^/pdf':''} //重写请求路径(必须)
}),
)
}

Some files were not shown because too many files have changed in this diff Show More