first commit
Some checks failed
Node CI / build (14.x, macOS-latest) (push) Has been cancelled
Node CI / build (14.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (14.x, windows-latest) (push) Has been cancelled
Node CI / build (16.x, macOS-latest) (push) Has been cancelled
Node CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (16.x, windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
coverage CI / build (push) Has been cancelled
Node pnpm CI / build (16.x, macOS-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, windows-latest) (push) Has been cancelled
Some checks failed
Node CI / build (14.x, macOS-latest) (push) Has been cancelled
Node CI / build (14.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (14.x, windows-latest) (push) Has been cancelled
Node CI / build (16.x, macOS-latest) (push) Has been cancelled
Node CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (16.x, windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
coverage CI / build (push) Has been cancelled
Node pnpm CI / build (16.x, macOS-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, windows-latest) (push) Has been cancelled
This commit is contained in:
191
src/components/JobTitleSelector.tsx
Normal file
191
src/components/JobTitleSelector.tsx
Normal file
@@ -0,0 +1,191 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Modal, Input, Row, Col, Spin, message } from 'antd';
|
||||
import type { ModalProps } from 'antd';
|
||||
import { request } from '@umijs/max';
|
||||
|
||||
interface JobTitle {
|
||||
id: number;
|
||||
label: string;
|
||||
children?: JobTitle[];
|
||||
}
|
||||
|
||||
export interface JobTitleSelectorProps extends Omit<ModalProps, 'onOk'|'onCancel'> {
|
||||
open: boolean;
|
||||
value?: {id:number, label:string};
|
||||
onOk: (job: {id:number, label:string})=>void;
|
||||
onCancel: ()=>void;
|
||||
}
|
||||
|
||||
const JobTitleSelector: React.FC<JobTitleSelectorProps> = ({ open, onOk, onCancel, value }) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState<JobTitle[]>([]);
|
||||
const [search, setSearch] = useState('');
|
||||
const [filtered, setFiltered] = useState<JobTitle[]>([]); // 筛选后数据
|
||||
const [selectedCat, setSelectedCat] = useState<JobTitle|null>(null); // 左列选中一级
|
||||
const [showSubId, setShowSubId] = useState<number|null>(null); // 右列当前展开二级ID
|
||||
const [selected, setSelected] = useState<{id:number, label:string}|null>(value||null);
|
||||
|
||||
useEffect(()=>{
|
||||
if(open){
|
||||
setLoading(true);
|
||||
request('/api/app/common/jobTitle/treeselect').then(res=>{
|
||||
setData(res.data||[]);
|
||||
setFiltered(res.data||[]);
|
||||
setSelectedCat(null);
|
||||
setShowSubId(null);
|
||||
setSelected(value||null);
|
||||
setSearch('');
|
||||
}).catch(()=>message.error('加载岗位数据失败')).finally(()=>setLoading(false));
|
||||
}
|
||||
},[open]);
|
||||
|
||||
// 搜索时过滤全部三级,依然保持原树结构
|
||||
useEffect(()=>{
|
||||
if(search&&search.length>0){
|
||||
// 返回新的树,只在三级岗位匹配关键词的二/一级保留
|
||||
function filterTree(nodes:JobTitle[]):JobTitle[]{
|
||||
return nodes.map(cat=>{
|
||||
// cat: 一级
|
||||
if(!cat.children)return null;
|
||||
const subFiltered=cat.children.map(sub=>{
|
||||
if(!sub.children)return null;
|
||||
const jobs=sub.children.filter(j=>j.label.includes(search));
|
||||
if(jobs.length>0){ return {...sub,children:jobs}; }
|
||||
return null;
|
||||
}).filter(Boolean) as JobTitle[];
|
||||
if(subFiltered.length>0)return {...cat,children:subFiltered};
|
||||
return null;
|
||||
}).filter(Boolean) as JobTitle[];
|
||||
}
|
||||
setFiltered(filterTree(data));
|
||||
setSelectedCat(null);
|
||||
setShowSubId(null);
|
||||
}else{
|
||||
setFiltered(data);
|
||||
}
|
||||
},[search,data]);
|
||||
|
||||
// 左列,一级点击切换
|
||||
const handleCat = (cat:JobTitle) => {
|
||||
setSelectedCat(cat); setShowSubId(null);
|
||||
};
|
||||
// 右列,点二级分类
|
||||
const handleSub = (sub:JobTitle) => {
|
||||
setShowSubId(sub.id===showSubId?null:sub.id);
|
||||
};
|
||||
// 右列,选三级岗位
|
||||
const handleJob = (job:JobTitle) => {
|
||||
setSelected({id:job.id,label:job.label});
|
||||
};
|
||||
const handleOk = () => {
|
||||
if(selected){ onOk(selected); } else { message.warning('请选择岗位'); }
|
||||
};
|
||||
|
||||
// 获取当前分类下所有二级
|
||||
const curSubs = selectedCat?.children || [];
|
||||
|
||||
// 搜索时过滤所有二级岗位的分组,全局(不再依赖selectedCat)
|
||||
const getGlobalSearchGroups = ()=>{
|
||||
if (!search) return [];
|
||||
const groups: {cat:JobTitle, sub: JobTitle, jobs: JobTitle[]}[] = [];
|
||||
data.forEach(cat=>{
|
||||
cat.children?.forEach(sub=>{
|
||||
if(sub.children){
|
||||
const jobs=sub.children.filter(j=>j.label.includes(search));
|
||||
if(jobs.length>0)groups.push({cat,sub,jobs});
|
||||
}
|
||||
});
|
||||
});
|
||||
return groups;
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal open={open} width={800} onCancel={onCancel} onOk={handleOk} title="请选择岗位名称" okText="确定" cancelText="取消">
|
||||
<Spin spinning={loading}>
|
||||
<Input
|
||||
placeholder="请输入职位关键词"
|
||||
allowClear
|
||||
value={search}
|
||||
onChange={e=>setSearch(e.target.value)}
|
||||
style={{marginBottom:10,width:280}}
|
||||
/>
|
||||
<div style={{display:'flex',minHeight:410, maxHeight:520, overflow:'hidden'}}>
|
||||
{/* 左侧一级分类 */}
|
||||
<div style={{width:180,borderRight:'1px solid #f0f0f0',overflowY:'auto',paddingTop:2,maxHeight:500}}>
|
||||
{filtered.map(cat=>(
|
||||
<div
|
||||
key={cat.id}
|
||||
style={{padding:'10px 16px', cursor:'pointer', background:selectedCat?.id===cat.id?'#e6f7ff':'', fontWeight:selectedCat?.id===cat.id?'bold':undefined}}
|
||||
onClick={()=>handleCat(cat)}
|
||||
>{cat.label}</div>
|
||||
))}
|
||||
</div>
|
||||
{/* 右侧内容区优化 */}
|
||||
<div style={{flex:1, padding:'16px 20px 10px 20px', minHeight:380, maxHeight:500,overflowY:'auto'}}>
|
||||
{(search) ? (
|
||||
(()=>{
|
||||
const groups = getGlobalSearchGroups();
|
||||
return groups.length===0?
|
||||
<div style={{color:'#bbb',textAlign:'center'}}>无相关岗位</div>:
|
||||
groups.map(({cat,sub,jobs})=>(
|
||||
<div key={cat.id+'-'+sub.id} style={{marginBottom:16}}>
|
||||
<div style={{fontWeight:600,margin:'4px 0'}}>
|
||||
{cat.label} / {sub.label}
|
||||
</div>
|
||||
<div style={{display:'flex',flexWrap:'wrap',flexDirection:'row',gap:'8px 6px',marginTop: 8}}>
|
||||
{jobs.map(job=>(
|
||||
<div key={job.id}
|
||||
style={{padding:'6px 18px',borderRadius:16,background:selected?.id===job.id?'#1890ff':'#f6f6f6',color:selected?.id===job.id?'#fff':'#222',cursor:'pointer',whiteSpace:'nowrap',fontSize:14}}
|
||||
onClick={()=>handleJob(job)}
|
||||
>{job.label}</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
})()
|
||||
) : (
|
||||
!selectedCat?
|
||||
<div style={{color:'#bbb',padding:'100px 0',textAlign:'center'}}>请先选择左侧所属行业类别</div>:
|
||||
<div>
|
||||
{curSubs.length===0?
|
||||
<div style={{color:'#bbb',textAlign:'center'}}>暂无子分类</div>:
|
||||
<div style={{overflow:'visible',maxWidth:'100%'}}>
|
||||
<div style={{display:'flex',flexWrap:'wrap',paddingBottom:12,gap:'0 36px'}}>
|
||||
{curSubs.map(sub=>(
|
||||
<div key={sub.id} style={{marginBottom:16, minWidth:150}}>
|
||||
<div
|
||||
style={{display:'flex',alignItems:'center', cursor:sub.children?'pointer':'default',fontWeight:600,padding:'4px 0',color:showSubId===sub.id?'#1890ff':'#333'}}
|
||||
onClick={()=>sub.children&&handleSub(sub)}
|
||||
>
|
||||
<span>{sub.label}</span>
|
||||
{sub.children && (
|
||||
<span style={{fontSize:20, color:'#999', marginLeft:4, fontWeight:'bold'}}>
|
||||
{showSubId===sub.id?'-':'+'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{/* 只在当前展开的二级下 横向显示三级 */}
|
||||
{showSubId===sub.id&&sub.children&&(
|
||||
<div style={{display:'flex',flexWrap:'wrap',flexDirection:'row',gap:'8px 6px',marginTop: 8}}>
|
||||
{sub.children.map(job=>(
|
||||
<div key={job.id}
|
||||
style={{padding:'6px 18px',borderRadius:16,background:selected?.id===job.id?'#1890ff':'#f6f6f6',color:selected?.id===job.id?'#fff':'#222',cursor:'pointer',whiteSpace:'nowrap',fontSize:14}}
|
||||
onClick={()=>handleJob(job)}
|
||||
>{job.label}</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Spin>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
export default JobTitleSelector;
|
||||
Reference in New Issue
Block a user