Files
shz-admin/src/components/JobTitleSelector.tsx
chenshaohua 30c8a7e8cf
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
first commit
2025-11-10 16:28:01 +08:00

192 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;