feat: 工作日历/考勤管理重构/AI人力报告/工作台员工分布/筛选优化/导入导出增强
- 新增工作日历页面(月历视图、事件管理、自定义事件) - 考勤管理重构为6 Tab模块(班次/排班/每日出勤/月度报表/休假记录) - AI顾问新增人力报告Tab,支持流式生成+Word导出 - 工作台总览新增员工分布统计(性别/年龄/学历/司龄饼图)+部门成本拆分 - 花名册/合同/解聘补偿新增部门和状态筛选 - 薪税管理新增工资表导入模板下载、银行代发CSV导出 - 社保公积金支持多公积金账户类型显示 - 数据导出新增花名册/解聘记录导出,中文文件名编码修复 - 数据导入新增模板下载(员工/增减员/工资表)+错误日志导出 - 移除工作台日历卡片(已迁移至独立工作日历页面) - 新增20260728/20260729更新测试指导文档
This commit is contained in:
@@ -34,15 +34,20 @@ interface EmployeeListResponse {
|
||||
export default function Contracts() {
|
||||
const queryClient = useQueryClient()
|
||||
const [search, setSearch] = useState('')
|
||||
const [filterDepartment, setFilterDepartment] = useState('')
|
||||
const [filterContractStatus, setFilterContractStatus] = useState('')
|
||||
const [page, setPage] = useState(1)
|
||||
const [showAddModal, setShowAddModal] = useState(false)
|
||||
const [selectedEmpId, setSelectedEmpId] = useState<string | null>(null)
|
||||
|
||||
const { data, isLoading } = useQuery<EmployeeListResponse>({
|
||||
queryKey: ['employees', search, page],
|
||||
queryKey: ['employees', search, filterDepartment, filterContractStatus, page],
|
||||
queryFn: async () => {
|
||||
const res = await api.get('/employees', { params: { search, page, pageSize: 20 } }) as any
|
||||
return res.data
|
||||
const params: any = { search, page, pageSize: 20 }
|
||||
if (filterDepartment) params.department = filterDepartment
|
||||
if (filterContractStatus) params.contractStatus = filterContractStatus
|
||||
const res = await api.get('/roster', { params }) as any
|
||||
return res
|
||||
},
|
||||
})
|
||||
|
||||
@@ -57,6 +62,14 @@ export default function Contracts() {
|
||||
},
|
||||
})
|
||||
|
||||
const { data: departmentList } = useQuery<string[]>({
|
||||
queryKey: ['roster-departments'],
|
||||
queryFn: async () => {
|
||||
const res = await api.get('/roster/departments') as any
|
||||
return res.data || []
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -73,8 +86,8 @@ export default function Contracts() {
|
||||
</div>
|
||||
|
||||
{/* 搜索栏 */}
|
||||
<div className="flex gap-2">
|
||||
<div className="relative flex-1">
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
<div className="relative flex-1 min-w-[200px]">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" />
|
||||
<Input
|
||||
placeholder="搜索员工姓名或手机号"
|
||||
@@ -83,6 +96,30 @@ export default function Contracts() {
|
||||
className="pl-9"
|
||||
/>
|
||||
</div>
|
||||
<select
|
||||
value={filterDepartment}
|
||||
onChange={(e) => { setFilterDepartment(e.target.value); setPage(1) }}
|
||||
className="h-9 rounded-md border border-gray-200 bg-white px-3 text-sm text-gray-700 shadow-sm outline-none transition focus:border-primary focus:ring-2 focus:ring-primary/10"
|
||||
>
|
||||
<option value="">全部部门</option>
|
||||
{departmentList?.map((d: string) => <option key={d} value={d}>{d}</option>)}
|
||||
</select>
|
||||
<select
|
||||
value={filterContractStatus}
|
||||
onChange={(e) => { setFilterContractStatus(e.target.value); setPage(1) }}
|
||||
className="h-9 rounded-md border border-gray-200 bg-white px-3 text-sm text-gray-700 shadow-sm outline-none transition focus:border-primary focus:ring-2 focus:ring-primary/10"
|
||||
>
|
||||
<option value="">全部合同</option>
|
||||
<option value="active">正常</option>
|
||||
<option value="expiring">即将到期</option>
|
||||
<option value="expired">已到期</option>
|
||||
<option value="unsigned">未签合同</option>
|
||||
<option value="unsigned_over_30">未签合同(超30天)</option>
|
||||
<option value="unsigned_over_year">未签合同(已满一年)</option>
|
||||
</select>
|
||||
{(search || filterDepartment || filterContractStatus) && (
|
||||
<button onClick={() => { setSearch(''); setFilterDepartment(''); setFilterContractStatus(''); setPage(1) }} className="text-xs text-gray-500 hover:text-primary">清除筛选</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 员工列表 */}
|
||||
|
||||
Reference in New Issue
Block a user