refactor: 全量迁移前端 API 调用到统一 api-services 服务层

- 新建 api-services-raw.ts 导出原始 axios 方法供特殊端点使用
- 完成 api-services.ts 全领域覆盖(auth/employee/roster/dashboard/attendance/payroll/socialInsurance/commercialInsurance/termination/policies/evidence/audit/calendar/companyFiles/notifications/settings/ai/platform/portal/survey/search)
- 迁移所有 47+ 页面文件:pages/、pages/roster/、pages/portal/、pages/platform/、pages/auth/、pages/dashboard/、pages/compliance/
- 移除所有直接 import api from '../../lib/api' 引用
- 修复 Termination.tsx / WorkProcess.tsx 中 string|null 类型错误
- 修复 SocialInsurance.tsx 中 api-services-raw delete 导入名
- 修复 PlatformLogin.tsx 变量遮蔽问题
- tsc --noEmit 零错误,vite build 成功
This commit is contained in:
selfrelease
2026-08-01 16:13:19 +08:00
parent fb924dea98
commit 16f22e6622
64 changed files with 1197 additions and 597 deletions
+34 -41
View File
@@ -7,8 +7,8 @@ import remarkGfm from 'remark-gfm'
import rehypeRaw from 'rehype-raw'
import { Document, Packer, Paragraph, HeadingLevel, TextRun, Table, TableRow, TableCell, WidthType, BorderStyle, AlignmentType } from 'docx'
import { saveAs } from 'file-saver'
import api from '../lib/api'
import { employeeApi } from '../lib/api-services'
import { aiApi, rosterApi, employeeApi } from '../lib/api-services'
import { post as apiPost } from '../lib/api-services-raw'
import { useAuthStore } from '../store/authStore'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
@@ -107,30 +107,28 @@ function useAIHistory(type: 'predict' | 'review' | 'case') {
const { data: history } = useQuery<any[]>({
queryKey,
queryFn: async () => {
const res = await api.get(`/ai/conversations?type=${type}`) as any
return res.data
return await aiApi.conversations(type)
},
})
const saveMutation = useMutation({
mutationFn: async ({ title, input, result }: { title: string; input: string; result: string }) => {
const res = await api.post('/ai/conversations', {
const res = await aiApi.createConversation({
title: `${type}:${title}`,
messages: [{ role: 'user', content: input }, { role: 'assistant', content: result }],
}) as any
return res.data
return res
},
onSuccess: () => queryClient.invalidateQueries({ queryKey }),
})
const deleteMutation = useMutation({
mutationFn: (id: string) => api.delete(`/ai/conversations/${id}`),
mutationFn: (id: string) => aiApi.removeConversation(id),
onSuccess: () => queryClient.invalidateQueries({ queryKey }),
})
const loadHistory = useCallback(async (id: string) => {
const res = await api.get(`/ai/conversations/${id}`) as any
return res.data
return await aiApi.conversation(id)
}, [])
return { history, saveMutation, deleteMutation, loadHistory }
@@ -238,20 +236,18 @@ function ChatTab() {
const { data: conversations } = useQuery<any[]>({
queryKey: ['ai-conversations'],
queryFn: async () => {
const res = await api.get('/ai/conversations?type=chat') as any
return res.data
return await aiApi.conversations('chat')
},
})
const deleteConvMutation = useMutation({
mutationFn: (id: string) => api.delete(`/ai/conversations/${id}`),
mutationFn: (id: string) => aiApi.removeConversation(id),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['ai-conversations'] }),
})
const consultMutation = useMutation({
mutationFn: async (data: typeof consultForm) => {
const res = await api.post('/ai/consultation', data) as any
return res.data
return await aiApi.consult(data)
},
onSuccess: () => {
toast.success('已提交咨询请求,专业律师将尽快与您联系')
@@ -274,11 +270,11 @@ function ChatTab() {
saveTimerRef.current = setTimeout(async () => {
const title = `chat:${messages.find(m => m.role === 'user')?.content.slice(0, 30) || '新对话'}`
if (currentConvId) {
await api.put(`/ai/conversations/${currentConvId}`, { messages }).catch(() => {})
await aiApi.updateConversation(currentConvId, { messages }).catch(() => {})
} else {
const res = await api.post('/ai/conversations', { title, messages }) as any
if (res.data?.id) {
setCurrentConvId(res.data.id)
const res = await aiApi.createConversation({ title, messages }) as any
if (res?.id) {
setCurrentConvId(res.id)
queryClient.invalidateQueries({ queryKey: ['ai-conversations'] })
}
}
@@ -288,9 +284,9 @@ function ChatTab() {
const loadConversation = async (id: string) => {
try {
const res = await api.get(`/ai/conversations/${id}`) as any
if (res.data?.messages) {
setMessages(res.data.messages)
const res = await aiApi.conversation(id) as any
if (res?.messages) {
setMessages(res.messages)
setCurrentConvId(id)
setShowHistory(false)
}
@@ -679,8 +675,8 @@ function PredictTab() {
// 4. 获取违纪记录(列表接口未含明细,需调用专用接口)
try {
const res = await api.get(`/roster/${emp.id}/disciplinary`) as any
const records = res?.data || res || []
const res = await rosterApi.disciplinary(emp.id) as any
const records = res || []
if (Array.isArray(records) && records.length > 0) {
const typeMap: Record<string, string> = { LATE: '迟到', ABSENT: '旷工', INSUBORDINATION: '不服从管理', MISCONDUCT: '违纪', VIOLATE_POLICY: '违反制度', OTHER: '其他' }
const actionMap: Record<string, string> = { ORAL_WARNING: '口头警告', WRITTEN_WARNING: '书面警告', DEDUCTION: '扣款', DEMOTION: '降职', TERMINATION: '辞退' }
@@ -1365,10 +1361,8 @@ function ReviewTab() {
try {
const formData = new FormData()
formData.append('file', file)
const res = await api.post('/ai/review/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
}) as any
if (res.data?.text) {
const res = await aiApi.reviewUpload(formData) as any
if (res?.text) {
setContractText(res.data.text)
setFileName(file.name)
toast.success(`已提取文件内容(${res.data.text.length} 字)`)
@@ -1391,10 +1385,10 @@ function ReviewTab() {
setLoading(true)
setResult(null)
try {
const res = await api.post('/ai/review', { contractText }) as any
setResult(res.data)
const res = await aiApi.review(contractText) as any
setResult(res)
// 自动保存到历史
if (res.data && !res.data.error) {
if (res && !res.error) {
const title = contractText.slice(0, 30).replace(/\n/g, ' ')
saveMutation.mutate({ title, input: contractText, result: res.data.text || JSON.stringify(res.data) })
}
@@ -1421,7 +1415,7 @@ function ReviewTab() {
const handleSave = async () => {
if (!saveEmployeeId || !result) return
try {
await api.post('/ai/review/save', { employeeId: saveEmployeeId, type: 'REVIEW', input: contractText, result: result.text || JSON.stringify(result) })
await aiApi.reviewSave({ employeeId: saveEmployeeId, type: 'REVIEW', input: contractText, result: result.text || JSON.stringify(result) })
setShowSaveModal(false)
setSaveEmployeeId('')
toast.success('已保存到员工档案')
@@ -1584,10 +1578,10 @@ function CaseTab() {
setLoading(true)
setResult('')
try {
const res = await api.post('/ai/match-case', { scenario }) as any
setResult(res.data.result)
const res = await aiApi.matchCase(scenario) as any
setResult(res.result)
// 自动保存到历史
if (res.data?.result && !res.data.result.startsWith('出错了')) {
if (res?.result && !res.result.startsWith('出错了')) {
const title = scenario.slice(0, 30).replace(/\n/g, ' ')
saveMutation.mutate({ title, input: scenario, result: res.data.result })
}
@@ -1612,7 +1606,7 @@ function CaseTab() {
const handleSave = async () => {
if (!saveEmployeeId || !result) return
try {
await api.post('/ai/review/save', { employeeId: saveEmployeeId, type: 'CASE', input: scenario, result })
await aiApi.reviewSave({ employeeId: saveEmployeeId, type: 'CASE', input: scenario, result })
setShowSaveModal(false)
setSaveEmployeeId('')
toast.success('已保存到员工档案')
@@ -1625,7 +1619,7 @@ function CaseTab() {
if (!todoEmployeeId || !todoTitle) return
setCreatingTodo(true)
try {
await api.post('/ai/case-to-todo', {
await aiApi.caseToTodo({
employeeId: todoEmployeeId,
title: todoTitle,
description: result.slice(0, 500),
@@ -1761,14 +1755,13 @@ function KnowledgeTab() {
const { data: knowledgeList, isLoading } = useQuery<any[]>({
queryKey: ['rag-knowledge'],
queryFn: async () => {
const res = await api.get('/ai/rag/list') as any
return res.data
return await aiApi.ragList()
},
})
const addMutation = useMutation({
mutationFn: async (data: typeof newItem) => {
return await api.post('/ai/rag/add', data)
return await aiApi.ragAdd(data)
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['rag-knowledge'] })
@@ -1778,12 +1771,12 @@ function KnowledgeTab() {
})
const deleteMutation = useMutation({
mutationFn: (id: string) => api.delete(`/ai/rag/${id}`),
mutationFn: (id: string) => aiApi.ragRemove(id),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['rag-knowledge'] }),
})
const seedMutation = useMutation({
mutationFn: () => api.post('/ai/rag/seed'),
mutationFn: () => aiApi.ragSeed(),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['rag-knowledge'] }),
})
+22 -37
View File
@@ -2,8 +2,7 @@ import { useState, useRef } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { CalendarCheck, CheckCircle, Clock, AlertCircle, Plus, Trash2, Calendar, Users, BarChart3, Plane, Upload, Download, X, Send, Loader2, CheckCheck } from 'lucide-react'
import api from '../lib/api'
import { employeeApi, rosterApi } from '../lib/api-services'
import { attendanceApi, employeeApi, rosterApi } from '../lib/api-services'
import { useAuthStore } from '../store/authStore'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
@@ -110,16 +109,14 @@ function ConfirmTab() {
const params: any = { month }
if (filterDepartment) params.department = filterDepartment
if (filterStatus) params.status = filterStatus
const res = await api.get('/attendance', { params }) as any
return res.data
return await attendanceApi.list({ month, department: filterDepartment || undefined, status: filterStatus || undefined })
},
})
const { data: stats } = useQuery<any>({
queryKey: ['attendance-stats', month],
queryFn: async () => {
const res = await api.get(`/attendance/stats?month=${month}`) as any
return res.data
return await attendanceApi.stats(month)
},
})
@@ -131,15 +128,13 @@ function ConfirmTab() {
const { data: publishRecords } = useQuery<any[]>({
queryKey: ['attendance-publish-records'],
queryFn: async () => {
const res = await api.get('/attendance/publish-records') as any
return res.data
return await attendanceApi.publishRecords()
},
})
const publishMutation = useMutation({
mutationFn: async () => {
const res = await api.post('/attendance/publish', { month }) as any
return res.data
return await attendanceApi.publish(month)
},
onSuccess: () => {
toast.success(`${month}月考勤表已发布`)
@@ -150,8 +145,7 @@ function ConfirmTab() {
const cancelPublishMutation = useMutation({
mutationFn: async (id: string) => {
const res = await api.post(`/attendance/publish/${id}/cancel`) as any
return res.data
return await attendanceApi.cancelPublish(id)
},
onSuccess: () => {
toast.success('已取消发布')
@@ -162,8 +156,7 @@ function ConfirmTab() {
const batchConfirmMutation = useMutation({
mutationFn: async (params: { all?: boolean; ids?: string[] }) => {
const res = await api.post('/attendance/batch-confirm', { month, ...params }) as any
return res.data
return await attendanceApi.batchConfirm({ month, ...params })
},
onSuccess: (data: any) => {
toast.success(`已批量确认 ${data.count} 条考勤记录`)
@@ -176,8 +169,7 @@ function ConfirmTab() {
const singleConfirmMutation = useMutation({
mutationFn: async (id: string) => {
const res = await api.post('/attendance/confirm', { employeeId: list.find((i: any) => i.id === id)?.employeeId, month }) as any
return res.data
return await attendanceApi.confirm({ employeeId: list.find((i: any) => i.id === id)?.employeeId, month })
},
onSuccess: () => {
toast.success('已确认')
@@ -520,17 +512,16 @@ function ShiftsTab() {
const { data: shifts, isLoading } = useQuery<any>({
queryKey: ['shifts'],
queryFn: async () => {
const res = await api.get('/attendance/shifts') as any
return res.data
return await attendanceApi.shifts()
},
})
const saveMutation = useMutation({
mutationFn: async (data: any) => {
if (editShift) {
return api.put(`/attendance/shifts/${editShift.id}`, data)
return attendanceApi.saveShift(data, editShift.id)
}
return api.post('/attendance/shifts', data)
return attendanceApi.saveShift(data)
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['shifts'] })
@@ -541,7 +532,7 @@ function ShiftsTab() {
})
const deleteMutation = useMutation({
mutationFn: (id: string) => api.delete(`/attendance/shifts/${id}`),
mutationFn: (id: string) => attendanceApi.removeShift(id),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['shifts'] }),
})
@@ -637,29 +628,26 @@ function ScheduleTab() {
const { data: shifts } = useQuery<any>({
queryKey: ['shifts'],
queryFn: async () => {
const res = await api.get('/attendance/shifts') as any
return res.data
return await attendanceApi.shifts()
},
})
const { data: assignments, isLoading } = useQuery<any>({
queryKey: ['shift-assignments', date],
queryFn: async () => {
const res = await api.get(`/attendance/shift-assignments?date=${date}`) as any
return res.data
return await attendanceApi.shiftAssignments(date)
},
})
const { data: dailyData } = useQuery<any>({
queryKey: ['daily-attendance', date],
queryFn: async () => {
const res = await api.get(`/attendance/daily?date=${date}`) as any
return res.data
return await attendanceApi.daily(date)
},
})
const batchAssignMutation = useMutation({
mutationFn: (items: any[]) => api.post('/attendance/shift-assignments/batch', { items }),
mutationFn: (items: any[]) => attendanceApi.batchAssign(items),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['shift-assignments'] })
queryClient.invalidateQueries({ queryKey: ['daily-attendance'] })
@@ -671,7 +659,7 @@ function ScheduleTab() {
})
const deleteAssignmentMutation = useMutation({
mutationFn: (id: string) => api.delete(`/attendance/shift-assignments/${id}`),
mutationFn: (id: string) => attendanceApi.removeAssignment(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['shift-assignments'] })
queryClient.invalidateQueries({ queryKey: ['daily-attendance'] })
@@ -805,8 +793,7 @@ function DailyTab() {
const { data, isLoading } = useQuery<any>({
queryKey: ['daily-attendance', date],
queryFn: async () => {
const res = await api.get(`/attendance/daily?date=${date}`) as any
return res.data
return await attendanceApi.daily(date)
},
})
@@ -880,8 +867,7 @@ function MonthlyTab() {
const { data, isLoading } = useQuery<any>({
queryKey: ['monthly-report', month],
queryFn: async () => {
const res = await api.get(`/attendance/monthly-report?month=${month}`) as any
return res.data
return await attendanceApi.monthlyReport(month)
},
})
@@ -974,8 +960,7 @@ function LeavesTab() {
const { data: leaves, isLoading } = useQuery<any>({
queryKey: ['leave-records'],
queryFn: async () => {
const res = await api.get('/attendance/leaves') as any
return res.data
return await attendanceApi.leaves()
},
})
@@ -985,7 +970,7 @@ function LeavesTab() {
})
const createMutation = useMutation({
mutationFn: (data: any) => api.post('/attendance/leaves', data),
mutationFn: (data: any) => attendanceApi.createLeave(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['leave-records'] })
setShowAdd(false)
@@ -995,7 +980,7 @@ function LeavesTab() {
})
const deleteMutation = useMutation({
mutationFn: (id: string) => api.delete(`/attendance/leaves/${id}`),
mutationFn: (id: string) => attendanceApi.removeLeave(id),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['leave-records'] }),
})
+8 -10
View File
@@ -1,7 +1,7 @@
import { useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { ScrollText, Search, Filter } from 'lucide-react'
import api from '../lib/api'
import { auditApi } from '../lib/api-services'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
import EmptyState from '../components/ui/EmptyState'
@@ -128,21 +128,19 @@ export default function AuditLog() {
const { data, isLoading } = useQuery<any>({
queryKey: ['audit-logs', page, pageSize, action, entity, dateFrom, dateTo],
queryFn: async () => {
const params = new URLSearchParams({ page: String(page), pageSize: String(pageSize) })
if (action) params.set('action', action)
if (entity) params.set('entity', entity)
if (dateFrom) params.set('dateFrom', dateFrom)
if (dateTo) params.set('dateTo', dateTo)
const res = await api.get(`/audit?${params}`) as any
return res.data
const p: any = { page, pageSize }
if (action) p.action = action
if (entity) p.entity = entity
if (dateFrom) p.dateFrom = dateFrom
if (dateTo) p.dateTo = dateTo
return await auditApi.list(p)
},
})
const { data: stats } = useQuery<any>({
queryKey: ['audit-stats'],
queryFn: async () => {
const res = await api.get('/audit/stats') as any
return res.data
return await auditApi.stats()
},
})
+5 -11
View File
@@ -3,7 +3,7 @@ import { toast } from 'sonner'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { Link } from 'react-router-dom'
import { CalendarDays, Plus, Trash2, ChevronLeft, ChevronRight, X, MapPin, User } from 'lucide-react'
import api from '../lib/api'
import { dashboardApi, calendarApi } from '../lib/api-services'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
import { Input, Label } from '../components/ui/Input'
@@ -66,22 +66,16 @@ export default function Calendar() {
const { data: calendarData } = useQuery<any>({
queryKey: ['calendar', calendarMonth],
queryFn: async () => {
const res = await api.get(`/dashboard/calendar?month=${calendarMonth}`) as any
return res.data
},
queryFn: () => dashboardApi.calendar(calendarMonth),
})
const { data: customEvents } = useQuery<any[]>({
queryKey: ['custom-events', calendarMonth],
queryFn: async () => {
const res = await api.get(`/calendar?month=${calendarMonth}`) as any
return res.data
},
queryFn: () => calendarApi.events(calendarMonth),
})
const createEventMutation = useMutation({
mutationFn: (data: any) => api.post('/calendar', data),
mutationFn: (data: any) => calendarApi.createEvent(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['custom-events'] })
queryClient.invalidateQueries({ queryKey: ['calendar'] })
@@ -93,7 +87,7 @@ export default function Calendar() {
})
const deleteEventMutation = useMutation({
mutationFn: (id: string) => api.delete(`/calendar/${id}`),
mutationFn: (id: string) => calendarApi.removeEvent(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['custom-events'] })
queryClient.invalidateQueries({ queryKey: ['calendar'] })
+4 -5
View File
@@ -2,7 +2,7 @@ import { useState, useRef } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { Building2, Upload, Trash2, FileText, AlertCircle, Calendar } from 'lucide-react'
import api from '../lib/api'
import { companyFilesApi } from '../lib/api-services'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
import { Input, Label, Select } from '../components/ui/Input'
@@ -29,13 +29,12 @@ export default function CompanyFiles() {
const { data: files, isLoading } = useQuery<any[]>({
queryKey: ['company-files', filterType],
queryFn: async () => {
const res = await api.get('/company-files', { params: filterType ? { fileType: filterType } : {} }) as any
return res.data || []
return await companyFilesApi.list(filterType ? { fileType: filterType } : {})
},
})
const addMutation = useMutation({
mutationFn: (data: any) => api.post('/company-files', data),
mutationFn: (data: any) => companyFilesApi.add(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['company-files'] })
toast.success('文件上传成功')
@@ -46,7 +45,7 @@ export default function CompanyFiles() {
})
const deleteMutation = useMutation({
mutationFn: (id: string) => api.delete(`/company-files/${id}`),
mutationFn: (id: string) => companyFilesApi.remove(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['company-files'] })
toast.success('已删除')
+1 -2
View File
@@ -1,7 +1,6 @@
import { useState, useMemo } from 'react'
import { useQuery } from '@tanstack/react-query'
import { Calculator, Info, AlertCircle } from 'lucide-react'
import api from '../lib/api'
import { rosterApi } from '../lib/api-services'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
@@ -24,7 +23,7 @@ function useEmployees() {
return useQuery<EmployeeOption[]>({
queryKey: ['roster-for-compensation'],
queryFn: async () => {
const res = await api.get('/roster') as any
const res = await rosterApi.list({} as any) as any
return res.data
},
})
+7 -10
View File
@@ -2,8 +2,7 @@ import { useState, useRef } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { Plus, Search, Paperclip, Trash2, X, FileText, Download, Eye } from 'lucide-react'
import { toast } from 'sonner'
import api from '../lib/api'
import { rosterApi } from '../lib/api-services'
import { rosterApi, employeeApi, attachmentApi } from '../lib/api-services'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
import { Input, Label, Select } from '../components/ui/Input'
@@ -49,13 +48,13 @@ export default function Contracts() {
const params: any = { search, page, pageSize }
if (filterDepartment) params.department = filterDepartment
if (filterContractStatus) params.contractStatus = filterContractStatus
const res = await api.get('/roster', { params }) as any
const res = await rosterApi.list({ search, page, pageSize, department: filterDepartment || undefined, contractStatus: filterContractStatus || undefined } as any) as any
return res
},
})
const addMutation = useMutation({
mutationFn: (data: any) => api.post('/employees', data),
mutationFn: (data: any) => employeeApi.create(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['employees'] })
queryClient.invalidateQueries({ queryKey: ['roster'] })
@@ -384,26 +383,24 @@ function EmployeeDetailDrawer({ employeeId, onClose }: { employeeId: string; onC
const { data: employee } = useQuery<any>({
queryKey: ['employee-detail', employeeId],
queryFn: async () => {
const res = await api.get(`/employees/${employeeId}`) as any
return res.data
return await employeeApi.detail(employeeId)
},
})
const { data: attachments } = useQuery<any[]>({
queryKey: ['employee-attachments', employeeId],
queryFn: async () => {
const res = await api.get(`/attachments/${employeeId}`) as any
return res.data
return await attachmentApi.list(employeeId)
},
})
const addAttachmentMutation = useMutation({
mutationFn: (data: any) => api.post('/attachments', data),
mutationFn: (data: any) => attachmentApi.add(data),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['employee-attachments', employeeId] }),
})
const deleteAttachmentMutation = useMutation({
mutationFn: (id: string) => api.delete(`/attachments/${id}`),
mutationFn: (id: string) => attachmentApi.remove(id),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['employee-attachments', employeeId] }),
})
+12 -18
View File
@@ -4,8 +4,8 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { Link } from 'react-router-dom'
import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, RadialBarChart, RadialBar, PolarAngleAxis } from 'recharts'
import { Users, AlertTriangle, CheckSquare, DollarSign, ArrowRight, RefreshCw, FileText, Calendar, TrendingUp, Briefcase, Calculator, Wallet, Building2, Receipt, Check, X, Clock, LayoutDashboard, ListTodo, ShieldAlert, UserPlus, AlertCircle, Download, ChevronRight, TrendingDown, ShieldCheck, Lightbulb, BookOpen, Sparkles, Repeat, XCircle, Loader2 } from 'lucide-react'
import api from '../lib/api'
import { dashboardApi, rosterApi } from '../lib/api-services'
import { dashboardApi, rosterApi, workProcessApi } from '../lib/api-services'
import { post as apiPost, patch as apiPatch } from '../lib/api-services-raw'
import { useAuthStore } from '../store/authStore'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
@@ -50,10 +50,7 @@ export default function Dashboard() {
const [dismissedExpiring, setDismissedExpiring] = useState(false)
const { data, isLoading, refetch, isFetching } = useQuery<DashboardData>({
queryKey: ['dashboard'],
queryFn: async () => {
const res = await api.get('/dashboard') as any
return res.data
},
queryFn: () => dashboardApi.data(),
})
const { data: expiringContracts } = useQuery<any>({
@@ -65,10 +62,7 @@ export default function Dashboard() {
const { data: costAnalysis } = useQuery<any>({
queryKey: ['cost-analysis', currentMonth],
queryFn: async () => {
const res = await api.get(`/dashboard/cost-analysis?month=${currentMonth}`) as any
return res.data
},
queryFn: () => dashboardApi.costAnalysis(currentMonth),
})
const { data: complianceScore } = useQuery<any>({
@@ -82,17 +76,17 @@ export default function Dashboard() {
})
const resolveMutation = useMutation({
mutationFn: (id: string) => api.patch(`/dashboard/todos/${id}/resolve`),
mutationFn: (id: string) => apiPatch(`/dashboard/todos/${id}/resolve`),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['dashboard'] }),
})
const ignoreMutation = useMutation({
mutationFn: (id: string) => api.patch(`/dashboard/todos/${id}/ignore`),
mutationFn: (id: string) => apiPatch(`/dashboard/todos/${id}/ignore`),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['dashboard'] }),
})
const batchResolveMutation = useMutation({
mutationFn: (ids: string[]) => api.patch('/dashboard/todos/batch-resolve', { ids }),
mutationFn: (ids: string[]) => apiPatch('/dashboard/todos/batch-resolve', { ids }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
setSelectedIds(new Set())
@@ -100,7 +94,7 @@ export default function Dashboard() {
})
const batchIgnoreMutation = useMutation({
mutationFn: (ids: string[]) => api.patch('/dashboard/todos/batch-ignore', { ids }),
mutationFn: (ids: string[]) => apiPatch('/dashboard/todos/batch-ignore', { ids }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
setSelectedIds(new Set())
@@ -1066,7 +1060,7 @@ export default function Dashboard() {
<div className="flex items-center gap-1">
<button
onClick={() => {
api.post('/work-processes', {
workProcessApi.create({
type: 'RENEW',
title: `合同续签-${c.employeeName}`,
employeeId: c.employeeId,
@@ -1075,7 +1069,7 @@ export default function Dashboard() {
}).then(() => {
toast.success(`已创建 ${c.employeeName} 的续签流程`)
setShowExpiringModal(false)
}).catch((err) => {
}).catch((err: any) => {
toast.error(err?.response?.data?.error?.message || '创建失败')
})
}}
@@ -1085,7 +1079,7 @@ export default function Dashboard() {
</button>
<button
onClick={() => {
api.post('/work-processes', {
workProcessApi.create({
type: 'TERMINATE',
title: `合同终止-${c.employeeName}`,
employeeId: c.employeeId,
@@ -1094,7 +1088,7 @@ export default function Dashboard() {
}).then(() => {
toast.success(`已创建 ${c.employeeName} 的终止流程`)
setShowExpiringModal(false)
}).catch((err) => {
}).catch((err: any) => {
toast.error(err?.response?.data?.error?.message || '创建失败')
})
}}
+3 -5
View File
@@ -1,7 +1,7 @@
import { useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { ShieldCheck, FileText, AlertCircle, CheckCircle, XCircle } from 'lucide-react'
import api from '../lib/api'
import { evidenceApi } from '../lib/api-services'
import Card from '../components/ui/Card'
import EmptyState from '../components/ui/EmptyState'
import Pagination from '../components/ui/Pagination'
@@ -19,8 +19,7 @@ export default function Evidence() {
queryFn: async () => {
const params: any = { page, pageSize }
params.category = refType || 'ALL'
const res = await api.get('/evidence', { params }) as any
return res.data
return await evidenceApi.list(params)
},
})
const list = listData?.records || []
@@ -29,8 +28,7 @@ export default function Evidence() {
const { data: verifyResult, refetch: verifyAll } = useQuery<any>({
queryKey: ['evidence-verify-all'],
queryFn: async () => {
const res = await api.get('/evidence/verify-all') as any
return res.data
return await evidenceApi.verifyAll()
},
enabled: false,
})
+37 -48
View File
@@ -6,8 +6,7 @@ import * as XLSX from 'xlsx'
import { Calculator, AlertCircle, Info, Check, Upload, Layers, Settings as SettingsIcon, Archive, Plus, Trash2, AlertTriangle, Download, FileText, X, ChevronLeft, Wallet, LayoutTemplate, Clock, Receipt, Users, TrendingDown, TrendingUp, BadgeCheck } from 'lucide-react'
import { Stepper, type Step } from '../components/ui/Stepper'
import { InlineAlert } from '../components/ui/InlineAlert'
import api from '../lib/api'
import { rosterApi } from '../lib/api-services'
import { payrollApi, rosterApi, employeeApi } from '../lib/api-services'
import { useAuthStore } from '../store/authStore'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
@@ -162,8 +161,7 @@ function BatchManager() {
const { data: checkResult } = useQuery<any>({
queryKey: ['batch-check', month],
queryFn: async () => {
const res = await api.get('/payroll2/batches/check', { params: { month } }) as any
return res.data
return await payrollApi.batchCheck(month)
},
})
@@ -176,22 +174,20 @@ function BatchManager() {
if (monthTo) params.monthTo = monthTo
if (filterStatus) params.status = filterStatus
if (filterType) params.type = filterType
const res = await api.get('/payroll2/batches', { params }) as any
return res.data
return await payrollApi.batches(params)
},
})
const { data: archivedBatches } = useQuery<any[]>({
queryKey: ['archived-batches'],
queryFn: async () => {
const res = await api.get('/payroll2/batches/archived/list') as any
return res.data
return await payrollApi.archivedBatches()
},
enabled: createMode === 'copy_batch',
})
const createMutation = useMutation({
mutationFn: (data: any) => api.post('/payroll2/batches', data),
mutationFn: (data: any) => payrollApi.createBatch(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['batches'] })
queryClient.invalidateQueries({ queryKey: ['batch-check'] })
@@ -202,7 +198,7 @@ function BatchManager() {
})
const deleteBatchMutation = useMutation({
mutationFn: (batchId: string) => api.delete(`/payroll2/batches/${batchId}`),
mutationFn: (batchId: string) => payrollApi.removeBatch(batchId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['batches'] })
queryClient.invalidateQueries({ queryKey: ['batch-check'] })
@@ -213,7 +209,7 @@ function BatchManager() {
const renameBatchMutation = useMutation({
mutationFn: ({ batchId, name }: { batchId: string; name: string }) =>
api.put(`/payroll2/batches/${batchId}/name`, { name }),
payrollApi.renameBatch(batchId, name),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['batches'] })
toast.success('批次名称已更新')
@@ -222,7 +218,7 @@ function BatchManager() {
})
const unarchiveBatchMutation = useMutation({
mutationFn: (batchId: string) => api.post(`/payroll2/batches/${batchId}/unarchive`),
mutationFn: (batchId: string) => payrollApi.unarchiveBatch(batchId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['batches'] })
queryClient.invalidateQueries({ queryKey: ['batch-check'] })
@@ -503,14 +499,13 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
const { data: batch, isLoading } = useQuery<any>({
queryKey: ['batch-detail', batchId],
queryFn: async () => {
const res = await api.get(`/payroll2/batches/${batchId}`) as any
return res.data
return await payrollApi.batchDetail(batchId)
},
})
const updateEntryMutation = useMutation({
mutationFn: ({ employeeId, data }: { employeeId: string; data: any }) =>
api.put(`/payroll2/batches/${batchId}/entries/${employeeId}`, data),
payrollApi.updateBatchEntry(batchId, employeeId, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
queryClient.invalidateQueries({ queryKey: ['batches'] })
@@ -518,7 +513,7 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
})
const removeEmployeeMutation = useMutation({
mutationFn: (employeeId: string) => api.delete(`/payroll2/batches/${batchId}/employees/${employeeId}`),
mutationFn: (employeeId: string) => payrollApi.removeBatchEmployee(batchId, employeeId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
queryClient.invalidateQueries({ queryKey: ['batches'] })
@@ -526,7 +521,7 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
})
const archiveMutation = useMutation({
mutationFn: () => api.post(`/payroll2/batches/${batchId}/archive`),
mutationFn: () => payrollApi.archiveBatch(batchId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
queryClient.invalidateQueries({ queryKey: ['batches'] })
@@ -537,7 +532,7 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
})
const unarchiveMutation = useMutation({
mutationFn: () => api.post(`/payroll2/batches/${batchId}/unarchive`),
mutationFn: () => payrollApi.unarchiveBatch(batchId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
queryClient.invalidateQueries({ queryKey: ['batches'] })
@@ -549,7 +544,7 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
})
const publishPayslipMutation = useMutation({
mutationFn: () => api.post(`/payroll2/batches/${batchId}/publish`),
mutationFn: () => payrollApi.publishPayslip(batchId),
onSuccess: (res: any) => {
toast.success(`已发布 ${res.data?.published || 0} 条工资条`)
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
@@ -560,7 +555,7 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
const [showScheduleModal, setShowScheduleModal] = useState(false)
const [scheduleDate, setScheduleDate] = useState('')
const schedulePayslipMutation = useMutation({
mutationFn: () => api.post(`/payroll2/batches/${batchId}/schedule`, { scheduledAt: scheduleDate }),
mutationFn: () => payrollApi.schedulePayslip(batchId, scheduleDate),
onSuccess: (res: any) => {
toast.success(`已设定定时发送 ${res.data?.scheduled || 0} 条工资条`)
setShowScheduleModal(false)
@@ -570,7 +565,7 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
})
const importOvertimeMutation = useMutation({
mutationFn: () => api.post(`/payroll/overtime/import-to-batch/${batchId}`),
mutationFn: () => payrollApi.importOvertimeToBatch(batchId),
onSuccess: (res: any) => {
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
queryClient.invalidateQueries({ queryKey: ['batches'] })
@@ -615,7 +610,7 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
})
const deleteBatchMutation = useMutation({
mutationFn: () => api.delete(`/payroll2/batches/${batchId}`),
mutationFn: () => payrollApi.removeBatch(batchId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['batches'] })
queryClient.invalidateQueries({ queryKey: ['batch-check'] })
@@ -845,8 +840,8 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
size="sm"
onClick={async () => {
try {
const res = await api.get(`/payroll/batch/${batchId}/summary`) as any
const { departments, grandTotal } = res.data
const res = await payrollApi.batchSummary(batchId) as any
const { departments, grandTotal } = res
const headers = ['部门', '人数', '应发合计', '实发合计', '个人社保', '单位社保', '个人公积金', '单位公积金', '个税合计']
const rows = departments.map((d: any) => [
d.department, d.headcount, d.totalPay.toFixed(2), d.totalNetPay.toFixed(2),
@@ -874,8 +869,8 @@ function BatchDetail({ batchId, onBack }: { batchId: string; onBack: () => void
size="sm"
onClick={async () => {
try {
const res = await api.get(`/payroll/batch/${batchId}/detail`) as any
const { details } = res.data
const res = await payrollApi.batchDetailExport(batchId) as any
const { details } = res
const headers = ['姓名', '部门', '基本工资', '岗位工资', '绩效工资', '工龄工资', '加班费', '交通补贴', '餐补', '住房补贴', '通讯补贴', '其他津贴', '奖金', '扣款', '其他扣款', '个人社保', '个人公积金', '个税', '应发合计', '实发工资']
const rows = details.map((d: any) => [
d.name, d.department,
@@ -1161,13 +1156,12 @@ function AddEmployeeToBatch({ batchId, onClose }: { batchId: string; onClose: ()
const { data: employees } = useQuery<any>({
queryKey: ['employees-for-batch'],
queryFn: async () => {
const res = await api.get('/employees', { params: { pageSize: 100 } }) as any
return res.data
return await employeeApi.paged({ pageSize: 100 })
},
})
const addMutation = useMutation({
mutationFn: (employeeIds: string[]) => api.post(`/payroll2/batches/${batchId}/employees`, { employeeIds }),
mutationFn: (employeeIds: string[]) => payrollApi.addBatchEmployees(batchId, employeeIds),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['batch-detail'] })
queryClient.invalidateQueries({ queryKey: ['batches'] })
@@ -1225,13 +1219,12 @@ function TemplateManager() {
const { data: items, isLoading } = useQuery<any[]>({
queryKey: ['payslip-template'],
queryFn: async () => {
const res = await api.get('/payroll2/template') as any
return res.data
return await payrollApi.template()
},
})
const deleteMutation = useMutation({
mutationFn: (id: string) => api.delete(`/payroll2/template/${id}`),
mutationFn: (id: string) => payrollApi.removeTemplateItem(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['payslip-template'] })
toast.success('已删除')
@@ -1240,7 +1233,7 @@ function TemplateManager() {
})
const createMutation = useMutation({
mutationFn: (data: any) => api.post('/payroll2/template', data),
mutationFn: (data: any) => payrollApi.createTemplateItem(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['payslip-template'] })
setShowForm(false)
@@ -1250,7 +1243,7 @@ function TemplateManager() {
})
const updateMutation = useMutation({
mutationFn: ({ id, data }: { id: string; data: any }) => api.put(`/payroll2/template/${id}`, data),
mutationFn: ({ id, data }: { id: string; data: any }) => payrollApi.updateTemplateItem(id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['payslip-template'] })
setShowForm(false)
@@ -1491,13 +1484,12 @@ function OvertimeCalculator() {
const { data: config, isLoading: configLoading } = useQuery<any>({
queryKey: ['overtime-config'],
queryFn: async () => {
const res = await api.get('/payroll/overtime/config') as any
return res.data
return await payrollApi.overtimeConfig()
},
})
const saveConfigMutation = useMutation({
mutationFn: (data: any) => api.post('/payroll/overtime/config', data),
mutationFn: (data: any) => payrollApi.saveOvertimeConfig(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['overtime-config'] })
},
@@ -1507,8 +1499,7 @@ function OvertimeCalculator() {
const { data: employees } = useQuery<{ items: { id: string; name: string; department: string }[] }>({
queryKey: ['employees-for-overtime'],
queryFn: async () => {
const res = await api.get('/employees', { params: { pageSize: 100 } }) as any
return res.data
return await employeeApi.paged({ pageSize: 100 })
},
})
@@ -1516,14 +1507,13 @@ function OvertimeCalculator() {
const { data: overtimeRecords, refetch } = useQuery<any[]>({
queryKey: ['overtime-records', month],
queryFn: async () => {
const res = await api.get('/payroll/overtime', { params: { month } }) as any
return res.data
return await payrollApi.overtimeRecords({ month })
},
enabled: step === 3,
})
const batchImportMutation = useMutation({
mutationFn: (data: any[]) => api.post('/payroll/overtime/batch', data),
mutationFn: (data: any[]) => payrollApi.batchImportOvertime(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['overtime-records'] })
setPreviewData([])
@@ -1534,7 +1524,7 @@ function OvertimeCalculator() {
// 更新单条加班记录
const updateOvertimeMutation = useMutation({
mutationFn: ({ id, data }: { id: string; data: any }) =>
api.put(`/payroll/overtime/${id}`, data),
payrollApi.updateOvertime(id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['overtime-records'] })
setEditingId(null)
@@ -1939,18 +1929,17 @@ function PayslipManager() {
const { data: payslips, isLoading } = useQuery<any[]>({
queryKey: ['payslips', month],
queryFn: async () => {
const res = await api.get('/payroll/payslip', { params: { month } }) as any
return res.data
return await payrollApi.payslips({ month })
},
})
const deleteMutation = useMutation({
mutationFn: (id: string) => api.delete(`/payroll/payslip/${id}`),
mutationFn: (id: string) => payrollApi.removePayslip(id),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['payslips'] }),
})
const generateFromBatchMutation = useMutation({
mutationFn: (data: any) => api.post('/payroll2/payslips/generate', data),
mutationFn: (data: any) => payrollApi.generatePayslips(data?.month || data),
onSuccess: (res: any) => {
queryClient.invalidateQueries({ queryKey: ['payslips'] })
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
@@ -1960,7 +1949,7 @@ function PayslipManager() {
})
const taxPreviewMutation = useMutation({
mutationFn: (data: any) => api.post('/payroll/tax-preview', data),
mutationFn: (data: any) => payrollApi.taxPreview(data),
onSuccess: (res: any) => {
setTaxResult(res.data)
setShowTaxPreview(true)
+6 -7
View File
@@ -2,7 +2,8 @@ import { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { Bell, CheckCircle, AlertCircle, Send, Settings as SettingsIcon, X } from 'lucide-react'
import api from '../lib/api'
import { notificationsApi } from '../lib/api-services'
import { post as apiPost } from '../lib/api-services-raw'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
import EmptyState from '../components/ui/EmptyState'
@@ -39,21 +40,19 @@ export default function Notifications() {
const { data, isLoading } = useQuery<any>({
queryKey: ['notification-logs', page, pageSize],
queryFn: async () => {
const res = await api.get(`/notifications/logs?page=${page}&pageSize=${pageSize}`) as any
return res.data
return await notificationsApi.logs({ page, pageSize })
},
})
const { data: settings } = useQuery<any>({
queryKey: ['notification-settings'],
queryFn: async () => {
const res = await api.get('/notifications/settings') as any
return res.data
return await notificationsApi.settings()
},
})
const checkContractsMutation = useMutation({
mutationFn: () => api.post('/notifications/check-contracts'),
mutationFn: () => apiPost('/notifications/check-contracts'),
onSuccess: () => {
toast.success('合同到期检查已触发')
queryClient.invalidateQueries({ queryKey: ['notification-logs'] })
@@ -174,7 +173,7 @@ function SettingsModal({ settings, onClose, onSuccess }: { settings: any; onClos
})
const saveMutation = useMutation({
mutationFn: () => api.put('/notifications/settings', form),
mutationFn: () => notificationsApi.updateSettings(form),
onSuccess: () => { toast.success('通知设置已保存'); onSuccess() },
onError: () => toast.error('保存失败'),
})
+6 -8
View File
@@ -2,7 +2,7 @@ import { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { FileText, Plus, ChevronRight, CheckCircle, Clock, X } from 'lucide-react'
import api from '../lib/api'
import { policiesApi } from '../lib/api-services'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
import EmptyState from '../components/ui/EmptyState'
@@ -30,21 +30,20 @@ export default function Policies() {
const { data: listData, isLoading } = useQuery<any>({
queryKey: ['policies', page, pageSize],
queryFn: async () => {
const res = await api.get('/policies', { params: { page, pageSize } }) as any
return res.data
return await policiesApi.list({ page, pageSize })
},
})
const list = listData?.items || []
const total = listData?.total || 0
const advanceMutation = useMutation({
mutationFn: ({ id, step, note }: { id: string; step: number; note?: string }) => api.post(`/policies/${id}/advance-step`, { step, note }),
mutationFn: ({ id, step, note }: { id: string; step: number; note?: string }) => policiesApi.advanceStep(id, step, note),
onSuccess: () => {
toast.success('流程步骤已推进')
queryClient.invalidateQueries({ queryKey: ['policies'] })
// 刷新选中制度详情
if (selectedPolicy) {
api.get(`/policies/${selectedPolicy.id}`).then((res: any) => setSelectedPolicy(res.data))
policiesApi.detail(selectedPolicy.id).then((res: any) => setSelectedPolicy(res))
}
},
onError: (err: any) => toast.error(err?.response?.data?.error?.message || '操作失败'),
@@ -196,7 +195,7 @@ function CreatePolicyModal({ onClose, onSuccess }: { onClose: () => void; onSucc
const [type, setType] = useState('RULES')
const createMutation = useMutation({
mutationFn: () => api.post('/policies', { title, content, type }),
mutationFn: () => policiesApi.create({ title, content, type }),
onSuccess: () => { toast.success('制度已创建'); onSuccess() },
onError: (err: any) => toast.error(err?.response?.data?.error?.message || '创建失败'),
})
@@ -243,8 +242,7 @@ function ReadStats({ policyId }: { policyId: string }) {
const { data, isLoading } = useQuery<any>({
queryKey: ['policy-read-stats', policyId],
queryFn: async () => {
const res = await api.get(`/policies/${policyId}/read-stats`) as any
return res.data
return await policiesApi.readStats(policyId)
},
})
+14 -15
View File
@@ -4,7 +4,7 @@ import { toast } from 'sonner'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { useConfirm } from '../hooks/useConfirm'
import { Users, FileText, AlertTriangle, Calendar, TrendingUp, Scale, X, Plus, Check, UserX, UserPlus, DollarSign, Building2, RotateCcw, History, Upload, Wallet, Download } from 'lucide-react'
import api from '../lib/api'
import { rosterApi, employeeApi, terminationApi } from '../lib/api-services'
import { useAuthStore } from '../store/authStore'
import { useDebouncedValue } from '../hooks/useDebouncedValue'
import Card from '../components/ui/Card'
@@ -58,7 +58,7 @@ export default function Roster() {
if (filterStatus === 'PROBATION') params.status = 'ACTIVE'
if (filterContractStatus) params.contractStatus = filterContractStatus
if (filterDepartment) params.department = filterDepartment
const res = await api.get('/roster', { params }) as any
const res = await rosterApi.list({ search: debouncedSearch, page, pageSize, status: filterStatus === 'PROBATION' ? 'ACTIVE' : filterStatus || undefined, contractStatus: filterContractStatus || undefined, department: filterDepartment || undefined } as any) as any
return res
},
})
@@ -83,7 +83,7 @@ export default function Roster() {
}, [employeeParam, debouncedSearch, isLoading, employees, selectedId, setSearchParams])
const addMutation = useMutation({
mutationFn: (data: any) => api.post('/employees', data),
mutationFn: (data: any) => employeeApi.create(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['roster'] })
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
@@ -93,7 +93,7 @@ export default function Roster() {
})
const resignMutation = useMutation({
mutationFn: (data: any) => api.post('/termination/draft', {
mutationFn: (data: any) => terminationApi.createDraft({
employeeId: data.employeeId,
type: 'RESIGNATION',
reason: 'RESIGNATION',
@@ -113,7 +113,7 @@ export default function Roster() {
})
const revokeMutation = useMutation({
mutationFn: (recordId: string) => api.delete(`/termination/${recordId}/revoke`),
mutationFn: (recordId: string) => terminationApi.revoke(recordId),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['roster'] })
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
@@ -122,7 +122,7 @@ export default function Roster() {
})
const rehireMutation = useMutation({
mutationFn: (data: any) => api.post(`/employees/${rehireEmployee?.id}/rehire`, data),
mutationFn: (data: any) => employeeApi.rehire(rehireEmployee?.id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['roster'] })
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
@@ -133,7 +133,7 @@ export default function Roster() {
})
const salaryChangeMutation = useMutation({
mutationFn: (data: any) => api.post(`/roster/${salaryEmployee?.id}/salary-change`, data),
mutationFn: (data: any) => rosterApi.salaryChange(salaryEmployee?.id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['roster'] })
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
@@ -144,7 +144,7 @@ export default function Roster() {
})
const deptChangeMutation = useMutation({
mutationFn: (data: any) => api.post(`/roster/${deptEmployee?.id}/department-change`, data),
mutationFn: (data: any) => rosterApi.departmentChange(deptEmployee?.id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['roster'] })
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
@@ -155,7 +155,7 @@ export default function Roster() {
})
const batchRenewMutation = useMutation({
mutationFn: (data: { contractIds: string[]; years: number }) => api.post('/employees/contracts/batch-renew', data),
mutationFn: (data: { contractIds: string[]; years: number }) => employeeApi.batchRenew(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['roster'] })
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
@@ -167,7 +167,7 @@ export default function Roster() {
})
const previewRenewMutation = useMutation({
mutationFn: (contractIds: string[]) => api.post('/employees/contracts/preview-renew', { contractIds }),
mutationFn: (contractIds: string[]) => employeeApi.previewRenew(contractIds),
onSuccess: (data: any) => {
setPreviewData(data.data)
},
@@ -175,7 +175,7 @@ export default function Roster() {
const previewTerminateMutation = useMutation({
mutationFn: (items: Array<{ employeeId: string; reason: string; terminationDate: string }>) =>
api.post('/termination/batch/preview', { items }),
terminationApi.batchPreview(items),
onSuccess: (data: any) => {
setTerminatePreviewData(data.data)
},
@@ -184,12 +184,12 @@ export default function Roster() {
const batchTerminateMutation = useMutation({
mutationFn: async (items: Array<{ employeeId: string; reason: string; terminationDate: string }>) => {
const results = await Promise.all(
items.map(item => api.post('/termination/draft', {
items.map(item => terminationApi.createDraft({
employeeId: item.employeeId,
type: 'TERMINATION',
reason: item.reason,
terminationDate: item.terminationDate,
}).catch(err => ({ error: err, employeeId: item.employeeId })))
}).catch((err: any) => ({ error: err, employeeId: item.employeeId })))
)
return results
},
@@ -258,8 +258,7 @@ export default function Roster() {
const { data: departmentList } = useQuery<string[]>({
queryKey: ['roster-departments'],
queryFn: async () => {
const res = await api.get('/roster/departments') as any
return res.data || []
return await rosterApi.departments()
},
})
+2 -3
View File
@@ -9,7 +9,7 @@ import {
import Card from '../components/ui/Card'
import { Select } from '../components/ui/Input'
import { InlineAlert } from '../components/ui/InlineAlert'
import api from '../lib/api'
import { salaryDashboardApi } from '../lib/api-services'
/** 金额格式化 */
const fmt = (n: number) => (n || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
@@ -21,8 +21,7 @@ export default function SalaryDashboard() {
const { data, isLoading } = useQuery<any>({
queryKey: ['salary-dashboard', year],
queryFn: async () => {
const res = await api.get('/salary/dashboard', { params: { year } }) as any
return res.data
return await salaryDashboardApi.data(Number(year))
},
})
+18 -23
View File
@@ -2,7 +2,8 @@ import { useState, useEffect } from 'react'
import { toast } from 'sonner'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { Building2, Users, CreditCard, Plus, Bell, Download, Upload, FileSpreadsheet, Clock, CheckCircle, AlertCircle, ClipboardList } from 'lucide-react'
import api from '../lib/api'
import { settingsApi, notificationsApi } from '../lib/api-services'
import { post as apiPost, patch as apiPatch } from '../lib/api-services-raw'
import { useAuthStore } from '../store/authStore'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
@@ -17,21 +18,19 @@ export default function Settings() {
const { data: orgData } = useQuery<any>({
queryKey: ['org-settings'],
queryFn: async () => {
const res = await api.get('/settings/org') as any
return res.data
return await settingsApi.org()
},
})
const { data: usersData } = useQuery<any>({
queryKey: ['users'],
queryFn: async () => {
const res = await api.get('/settings/users') as any
return res.data
return await settingsApi.users()
},
})
const updateOrgMutation = useMutation({
mutationFn: (data: any) => api.put('/settings/org', data),
mutationFn: (data: any) => settingsApi.updateOrg(data),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['org-settings'] }),
})
@@ -153,14 +152,13 @@ function RetirementSection({ enabled, onToggle }: { enabled: boolean; onToggle:
const { data: policyData, isLoading } = useQuery<any>({
queryKey: ['retirement-policy'],
queryFn: async () => {
const res = await api.get('/settings/retirement-policy') as any
return res.data
return await settingsApi.retirementPolicy()
},
enabled,
})
const confirmMutation = useMutation({
mutationFn: (id: string) => api.post(`/settings/retirement-policy/${id}/confirm`),
mutationFn: (id: string) => apiPost(`/settings/retirement-policy/${id}/confirm`),
onSuccess: () => {
toast.success('退休政策已确认生效')
setConfirming(false)
@@ -272,12 +270,12 @@ function UserSettings({ usersData }: { usersData: any }) {
const users = usersData || []
const updateMutation = useMutation({
mutationFn: ({ id, data }: { id: string; data: any }) => api.put(`/settings/users/${id}`, data),
mutationFn: ({ id, data }: { id: string; data: any }) => settingsApi.updateUser(id, data),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
})
const toggleDisableMutation = useMutation({
mutationFn: (id: string) => api.patch(`/settings/users/${id}/toggle-disable`),
mutationFn: (id: string) => settingsApi.toggleDisable(id),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] }),
})
@@ -407,7 +405,7 @@ function AddUserModal({ open, onClose }: { open: boolean; onClose: () => void })
setLoading(true)
setError('')
try {
await api.post('/settings/users', form)
await settingsApi.addUser(form)
onClose()
} catch (err: any) {
setError(err.response?.data?.error?.message || '添加失败')
@@ -658,13 +656,12 @@ function PlanSettings({ orgData }: { orgData: any }) {
const { data: usageData } = useQuery<any>({
queryKey: ['usage'],
queryFn: async () => {
const res = await api.get('/settings/usage') as any
return res.data
return await settingsApi.usage()
},
})
const planMutation = useMutation({
mutationFn: (newPlan: string) => api.put('/settings/plan', { plan: newPlan }),
mutationFn: (newPlan: string) => settingsApi.updatePlan(newPlan),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['org'] })
queryClient.invalidateQueries({ queryKey: ['usage'] })
@@ -744,16 +741,14 @@ function NotificationSettings() {
const { data: setting } = useQuery<any>({
queryKey: ['notification-settings'],
queryFn: async () => {
const res = await api.get('/notifications/settings') as any
return res.data
return await notificationsApi.settings()
},
})
const { data: logsData } = useQuery<any>({
queryKey: ['notification-logs'],
queryFn: async () => {
const res = await api.get('/notifications/logs', { params: { pageSize: 10 } }) as any
return res.data
return await notificationsApi.logs({ pageSize: 10 })
},
})
@@ -762,12 +757,12 @@ function NotificationSettings() {
}, [setting])
const updateMutation = useMutation({
mutationFn: (data: any) => api.put('/notifications/settings', data),
mutationFn: (data: any) => notificationsApi.updateSettings(data),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['notification-settings'] }),
})
const checkMutation = useMutation({
mutationFn: () => api.post('/notifications/check-contracts') as any,
mutationFn: () => apiPost('/notifications/check-contracts') as any,
onSuccess: (res: any) => {
setCheckResult(`检查完成:发现 ${res.data.checked} 个即将到期的合同,已发送 ${res.data.notified} 条通知`)
queryClient.invalidateQueries({ queryKey: ['notification-logs'] })
@@ -775,14 +770,14 @@ function NotificationSettings() {
})
const testWechatMutation = useMutation({
mutationFn: () => api.post('/notifications/test', { channel: 'wechat' }) as any,
mutationFn: () => apiPost('/notifications/test', { channel: 'wechat' }) as any,
onSuccess: (res: any) => {
toast.success(res.success ? res.data.message : (res.error?.message || '测试失败'))
},
})
const testEmailMutation = useMutation({
mutationFn: () => api.post('/notifications/test', { channel: 'email' }) as any,
mutationFn: () => apiPost('/notifications/test', { channel: 'email' }) as any,
onSuccess: (res: any) => {
toast.success(res.success ? res.data.message : (res.error?.message || '测试失败'))
},
+43 -57
View File
@@ -4,7 +4,8 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { useConfirm } from '../hooks/useConfirm'
import { Calculator, Info, Check, Settings as SettingsIcon, Plus, History, Download, AlertCircle, Clock, MapPin, Sparkles, Upload, X, Shield } from 'lucide-react'
import { InlineAlert } from '../components/ui/InlineAlert'
import api from '../lib/api'
import { socialInsuranceApi, commercialInsuranceApi } from '../lib/api-services'
import { get as apiGet, post as apiPost, put as apiPut, del as apiDel } from '../lib/api-services-raw'
import { useAuthStore } from '../store/authStore'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
@@ -54,31 +55,28 @@ export default function SocialInsurance() {
const { data: cities = [] } = useQuery<string[]>({
queryKey: ['social-config-cities'],
queryFn: async () => {
const res = await api.get('/social/config/cities') as any
return res.data
return await socialInsuranceApi.cities()
},
})
const { data: config, isLoading: configLoading } = useQuery<any>({
queryKey: ['social-config', city],
queryFn: async () => {
const res = await api.get('/social/config', { params: { city } }) as any
return res.data
return await socialInsuranceApi.config(city)
},
})
const { data: housingConfig, isLoading: housingLoading } = useQuery<any>({
queryKey: ['housing-config', city],
queryFn: async () => {
const res = await api.get('/social/housing-config', { params: { city } }) as any
return res.data
return await socialInsuranceApi.housingConfig(city)
},
})
const { data: housingAllAccounts } = useQuery<any[]>({
queryKey: ['housing-config-all-accounts', city],
queryFn: async () => {
const res = await api.get('/social/housing-config/versions', { params: { city } }) as any
const current = (res.data || []).filter((v: any) => v.isCurrent)
const res = await socialInsuranceApi.housingConfigVersions(city) as any
const current = (res || []).filter((v: any) => v.isCurrent)
return current
},
})
@@ -86,8 +84,7 @@ export default function SocialInsurance() {
const { data: versions } = useQuery<any[]>({
queryKey: ['social-config-versions', city],
queryFn: async () => {
const res = await api.get('/social/config/versions', { params: { city } }) as any
return res.data
return await socialInsuranceApi.configVersions(city)
},
enabled: showVersions && tab === 'social',
})
@@ -95,8 +92,7 @@ export default function SocialInsurance() {
const { data: housingVersions } = useQuery<any[]>({
queryKey: ['housing-config-versions'],
queryFn: async () => {
const res = await api.get('/social/housing-config/versions') as any
return res.data
return await socialInsuranceApi.housingConfigVersions()
},
enabled: showVersions && tab === 'housing',
})
@@ -105,8 +101,7 @@ export default function SocialInsurance() {
const { data: processedList, refetch: refetchProcessedList } = useQuery<any[]>({
queryKey: ['monthly-process-list'],
queryFn: async () => {
const res = await api.get('/social/monthly-process/list') as any
return res.data
return await socialInsuranceApi.monthlyProcessList()
},
enabled: tab === 'monthly',
})
@@ -114,8 +109,8 @@ export default function SocialInsurance() {
// 进入月度办理Tab时自动查询当前月状态
useEffect(() => {
if (tab === 'monthly') {
api.get('/social/monthly-process/status', { params: { month: monthlyMonth } }).then((res: any) => {
setProcessStatus(res.data)
socialInsuranceApi.monthlyProcessStatus(monthlyMonth).then((res: any) => {
setProcessStatus(res)
}).catch(() => {})
refetchProcessedList()
}
@@ -124,10 +119,10 @@ export default function SocialInsurance() {
const { mutateAsync: fetchMonthlyChanges, isPending: monthlyLoading, data: monthlyChanges } = useMutation<any>({
mutationFn: async () => {
const [socialRes, housingRes, socialActiveRes, housingActiveRes] = await Promise.all([
api.get('/social/monthly-changes', { params: { month: monthlyMonth } }) as any,
api.get('/social/housing/monthly-changes', { params: { month: monthlyMonth } }) as any,
api.get('/social/active-declaration', { params: { month: monthlyMonth } }) as any,
api.get('/social/housing/active-declaration', { params: { month: monthlyMonth } }) as any,
apiGet('/social/monthly-changes', { params: { month: monthlyMonth } }) as any,
apiGet('/social/housing/monthly-changes', { params: { month: monthlyMonth } }) as any,
apiGet('/social/active-declaration', { params: { month: monthlyMonth } }) as any,
apiGet('/social/housing/active-declaration', { params: { month: monthlyMonth } }) as any,
])
return {
social: socialRes.data,
@@ -143,8 +138,8 @@ export default function SocialInsurance() {
await fetchMonthlyChanges()
setMonthlyProcessed(true)
// 查询该月办理状态
const statusRes = await api.get('/social/monthly-process/status', { params: { month: monthlyMonth } }) as any
setProcessStatus(statusRes.data)
const statusRes = await socialInsuranceApi.monthlyProcessStatus(monthlyMonth) as any
setProcessStatus(statusRes)
} catch {
toast.error('获取月度办理数据失败')
}
@@ -154,7 +149,7 @@ export default function SocialInsurance() {
mutationFn: async (type: 'SOCIAL' | 'HOUSING') => {
const snapshot = type === 'SOCIAL' ? monthlyChanges.social : monthlyChanges.housing
const activeSnapshot = type === 'SOCIAL' ? monthlyChanges.socialActive : monthlyChanges.housingActive
const res = await api.post('/social/monthly-process/complete', {
const res = await socialInsuranceApi.completeMonthlyProcess({
month: monthlyMonth,
type,
snapshot: { changes: snapshot, active: activeSnapshot },
@@ -174,20 +169,18 @@ export default function SocialInsurance() {
const { data: result, mutate: calcMutate, isPending } = useMutation<any>({
mutationFn: async () => {
const res = await api.post('/social/calculate', { base, city }) as any
return res.data
return await socialInsuranceApi.calculate(base, city)
},
})
const { data: housingResult, mutate: calcHousingMutate, isPending: housingCalcPending } = useMutation<any>({
mutationFn: async () => {
const res = await api.post('/social/housing-calculate', { base, city }) as any
return res.data
return await socialInsuranceApi.housingCalculate(base, city)
},
})
const createVersionMutation = useMutation({
mutationFn: (data: any) => api.post('/social/config/versions', data),
mutationFn: (data: any) => socialInsuranceApi.createConfigVersion(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['social-config'] })
queryClient.invalidateQueries({ queryKey: ['social-config-versions'] })
@@ -197,7 +190,7 @@ export default function SocialInsurance() {
})
const createHousingVersionMutation = useMutation({
mutationFn: (data: any) => api.post('/social/housing-config/versions', data),
mutationFn: (data: any) => socialInsuranceApi.createHousingConfigVersion(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['housing-config'] })
queryClient.invalidateQueries({ queryKey: ['housing-config-versions'] })
@@ -208,8 +201,7 @@ export default function SocialInsurance() {
const aiSuggestMut = useMutation<any, any, { city: string; effectiveFrom: string; type: 'social' | 'housing' }>({
mutationFn: async (vars: { city: string; effectiveFrom: string; type: 'social' | 'housing' }) => {
const res = await api.post('/social/ai-suggest', vars) as any
return res.data
return await socialInsuranceApi.aiSuggest(vars)
},
onSuccess: (data) => {
if (isHousing) {
@@ -247,8 +239,7 @@ export default function SocialInsurance() {
const previewAdjustMutation = useMutation({
mutationFn: async () => {
const res = await api.get(`/social/config/${config?.id}/adjust-preview`) as any
return res.data
return await socialInsuranceApi.adjustPreview(config?.id)
},
onSuccess: (data) => {
setAdjustData(data)
@@ -258,8 +249,7 @@ export default function SocialInsurance() {
const previewHousingAdjustMutation = useMutation({
mutationFn: async () => {
const res = await api.get(`/social/housing-config/${housingConfig?.id}/adjust-preview`) as any
return res.data
return await socialInsuranceApi.housingAdjustPreview(housingConfig?.id)
},
onSuccess: (data) => {
setAdjustData(data)
@@ -269,7 +259,7 @@ export default function SocialInsurance() {
const applyAdjustMutation = useMutation({
mutationFn: (data: { items: { employeeId: string; newBase: number }[] }) =>
api.post(`/social/config/${config?.id}/adjust-apply`, data),
socialInsuranceApi.applyAdjust(config?.id, data),
onSuccess: (res: any) => {
queryClient.invalidateQueries({ queryKey: ['social-config'] })
queryClient.invalidateQueries({ queryKey: ['social-config-versions'] })
@@ -285,7 +275,7 @@ export default function SocialInsurance() {
const applyHousingAdjustMutation = useMutation({
mutationFn: (data: { items: { employeeId: string; newBase: number }[] }) =>
api.post(`/social/housing-config/${housingConfig?.id}/adjust-apply`, data),
socialInsuranceApi.applyHousingAdjust(housingConfig?.id, data),
onSuccess: (res: any) => {
queryClient.invalidateQueries({ queryKey: ['housing-config'] })
queryClient.invalidateQueries({ queryKey: ['housing-config-versions'] })
@@ -300,7 +290,7 @@ export default function SocialInsurance() {
})
const resetAdjustMutation = useMutation({
mutationFn: () => api.post(`/social/config/${config?.id}/reset-adjustment`, { city }),
mutationFn: () => socialInsuranceApi.resetAdjust(config?.id, city),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['social-config', city] })
queryClient.invalidateQueries({ queryKey: ['social-config-versions', city] })
@@ -309,7 +299,7 @@ export default function SocialInsurance() {
})
const resetHousingAdjustMutation = useMutation({
mutationFn: () => api.post(`/social/housing-config/${housingConfig?.id}/reset-adjustment`, { city }),
mutationFn: () => socialInsuranceApi.resetHousingAdjust(housingConfig?.id, city),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['housing-config', city] })
queryClient.invalidateQueries({ queryKey: ['housing-config-versions', city] })
@@ -1162,7 +1152,7 @@ function MonthlyRow({ item: i, type, onCorrected }: { item: any; type: 'add' | '
const d = i.detail
const correctMutation = useMutation({
mutationFn: (data: { base: number }) => api.put(`/social/records/social/${i.recordId}/correct`, data),
mutationFn: (data: { base: number }) => socialInsuranceApi.correctRecord('social', i.recordId, data),
onSuccess: () => {
setEditing(false)
toast.success('基数已修改')
@@ -1250,7 +1240,7 @@ function MonthlyHousingRow({ item: i, type, onCorrected }: { item: any; type: 'a
const d = i.detail
const correctMutation = useMutation({
mutationFn: (data: { base: number }) => api.put(`/social/records/housing/${i.recordId}/correct`, data),
mutationFn: (data: { base: number }) => socialInsuranceApi.correctRecord('housing', i.recordId, data),
onSuccess: () => {
setEditing(false)
toast.success('基数已修改')
@@ -1314,8 +1304,7 @@ function SpecialDeductionTab({ month, setMonth }: { month: string; setMonth: (m:
const { data: records = [], isLoading } = useQuery<any[]>({
queryKey: ['special-deduction', month],
queryFn: async () => {
const res = await api.get('/social/special-deduction/batch', { params: { month } }) as any
return res.data
return await socialInsuranceApi.specialDeductionBatch(month)
},
})
@@ -1323,8 +1312,8 @@ function SpecialDeductionTab({ month, setMonth }: { month: string; setMonth: (m:
const { data: employees = [] } = useQuery<any[]>({
queryKey: ['active-social-employees', month],
queryFn: async () => {
const res = await api.get('/social/active-declaration', { params: { month } }) as any
return (res.data?.items || []).map((item: any) => ({
const res = await socialInsuranceApi.activeDeclaration(month) as any
return (res?.items || []).map((item: any) => ({
id: item.employeeId,
name: item.name,
department: item.department,
@@ -1341,14 +1330,13 @@ function SpecialDeductionTab({ month, setMonth }: { month: string; setMonth: (m:
const { data: prevRecords = [] } = useQuery<any[]>({
queryKey: ['special-deduction', prevMonth],
queryFn: async () => {
const res = await api.get('/social/special-deduction/batch', { params: { month: prevMonth } }) as any
return res.data || []
return await socialInsuranceApi.specialDeductionBatch(prevMonth)
},
})
const prevRecordMap = new Map(prevRecords.map((r: any) => [r.employeeId, r]))
const saveMutation = useMutation({
mutationFn: (data: any) => api.post('/social/special-deduction', { ...data, month }),
mutationFn: (data: any) => socialInsuranceApi.saveSpecialDeduction({ ...data, month }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['special-deduction'] })
setEditing(null)
@@ -1376,7 +1364,7 @@ function SpecialDeductionTab({ month, setMonth }: { month: string; setMonth: (m:
mutationFn: async () => {
let copied = 0
for (const prev of prevRecords) {
await api.post('/social/special-deduction', {
await socialInsuranceApi.saveSpecialDeduction({
employeeId: prev.employeeId,
month,
children: prev.children || 0,
@@ -1661,8 +1649,7 @@ function CommercialInsuranceTab() {
const { data: plans = [], isLoading } = useQuery<any[]>({
queryKey: ['commercial-insurance-plans'],
queryFn: async () => {
const res = await api.get('/commercial-insurance/plans') as any
return res.data || []
return await commercialInsuranceApi.plans()
},
})
@@ -1671,8 +1658,7 @@ function CommercialInsuranceTab() {
queryKey: ['commercial-insurance-enrollments', selectedPlanId],
queryFn: async () => {
if (!selectedPlanId) return []
const res = await api.get(`/commercial-insurance/plans/${selectedPlanId}/enrollments`) as any
return res.data || []
return await commercialInsuranceApi.enrollments(selectedPlanId)
},
enabled: !!selectedPlanId,
})
@@ -1681,9 +1667,9 @@ function CommercialInsuranceTab() {
const savePlanMutation = useMutation({
mutationFn: async (data: any) => {
if (editingPlan) {
return api.put(`/commercial-insurance/plans/${editingPlan.id}`, data) as any
return commercialInsuranceApi.savePlan(data, editingPlan.id) as any
}
return api.post('/commercial-insurance/plans', data) as any
return commercialInsuranceApi.savePlan(data) as any
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['commercial-insurance-plans'] })
@@ -1697,7 +1683,7 @@ function CommercialInsuranceTab() {
/** 删除商险方案 */
const deletePlanMutation = useMutation({
mutationFn: (id: string) => api.delete(`/commercial-insurance/plans/${id}`),
mutationFn: (id: string) => commercialInsuranceApi.removePlan(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['commercial-insurance-plans'] })
setSelectedPlanId(null)
+11 -11
View File
@@ -4,7 +4,7 @@
*/
import { useEffect, useState } from 'react'
import { Search, Plus, Edit2, Trash2, AlertTriangle, Clock, Baby, HeartPulse, Activity, X } from 'lucide-react'
import api from '../lib/api'
import { specialStatusApi, employeeApi } from '../lib/api-services'
import { Input, Select, Label } from '../components/ui/Input'
import Button from '../components/ui/Button'
@@ -140,9 +140,9 @@ export default function SpecialStatus() {
if (search) params.search = search
if (typeFilter) params.type = typeFilter
if (statusFilter) params.status = statusFilter
const res = await api.get('/special-statuses', { params }) as any
setList(res.data.list)
setTotal(res.data.total)
const res = await specialStatusApi.list({ page, pageSize, search: search || undefined, type: typeFilter || undefined, status: statusFilter || undefined } as any) as any
setList(res.list)
setTotal(res.total)
} finally {
setLoading(false)
}
@@ -150,8 +150,8 @@ export default function SpecialStatus() {
const fetchStats = async () => {
try {
const res = await api.get('/special-statuses/stats/overview') as any
setStats(res.data)
const res = await specialStatusApi.stats() as any
setStats(res)
} catch {
// 忽略
}
@@ -159,8 +159,8 @@ export default function SpecialStatus() {
const fetchEmployees = async () => {
try {
const res = await api.get('/employees/all-lite') as any
setEmployees(res.data || [])
const res = await employeeApi.allLite() as any
setEmployees(res || [])
} catch {
// 忽略
}
@@ -212,9 +212,9 @@ export default function SpecialStatus() {
if (data.medicalMonths) data.medicalMonths = parseInt(data.medicalMonths)
if (editing) {
await api.put(`/special-statuses/${editing.id}`, data)
await specialStatusApi.update(editing.id, data)
} else {
await api.post('/special-statuses', data)
await specialStatusApi.create(data)
}
setEditOpen(false)
fetchList()
@@ -227,7 +227,7 @@ export default function SpecialStatus() {
const handleDelete = async () => {
if (!deleteTarget) return
try {
await api.delete(`/special-statuses/${deleteTarget.id}`)
await specialStatusApi.remove(deleteTarget.id)
setDeleteTarget(null)
fetchList()
fetchStats()
+12 -19
View File
@@ -2,7 +2,7 @@ import { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { FileText, Copy, X, ChevronRight, Download, BookOpen, HelpCircle, Plus, Edit, Trash2, Building2 } from 'lucide-react'
import { toast } from 'sonner'
import api from '../lib/api'
import { templatesApi } from '../lib/api-services'
import { useAuthStore } from '../store/authStore'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
@@ -110,17 +110,14 @@ function SystemTemplates() {
const { data: list, isLoading } = useQuery<any>({
queryKey: ['templates', category],
queryFn: async () => {
const params = category ? `?category=${category}` : ''
const res = await api.get(`/templates${params}`) as any
return res.data
return await templatesApi.list(category || undefined)
},
})
const { data: detail } = useQuery<any>({
queryKey: ['template-detail', selected?.id],
queryFn: async () => {
const res = await api.get(`/templates/${selected.id}`) as any
return res.data
return await templatesApi.detail(selected.id)
},
enabled: !!selected,
})
@@ -128,8 +125,8 @@ function SystemTemplates() {
const handleRender = async () => {
if (!selected) return
try {
const res = await api.post(`/templates/${selected.id}/render`, { variables }) as any
setRendered(res.data.content)
const res = await templatesApi.render(selected.id, variables) as any
setRendered(res.content)
} catch (err: any) {
toast.error('渲染失败')
}
@@ -318,8 +315,7 @@ function EnterpriseTemplates() {
queryFn: async () => {
const params: any = { page, pageSize }
if (category) params.category = category
const res = await api.get('/enterprise-templates', { params }) as any
return res.data
return await templatesApi.enterpriseList({ page, pageSize, category: category || undefined } as any)
},
})
const list = listData?.items || []
@@ -328,8 +324,7 @@ function EnterpriseTemplates() {
const { data: detail } = useQuery<any>({
queryKey: ['enterprise-template-detail', selected?.id],
queryFn: async () => {
const res = await api.get(`/enterprise-templates/${selected.id}`) as any
return res.data
return await templatesApi.enterpriseDetail(selected.id)
},
enabled: !!selected,
})
@@ -337,11 +332,9 @@ function EnterpriseTemplates() {
const saveMutation = useMutation({
mutationFn: async (data: any) => {
if (editItem) {
const res = await api.put(`/enterprise-templates/${editItem.id}`, data) as any
return res.data
return await templatesApi.saveEnterprise(data, editItem.id)
} else {
const res = await api.post('/enterprise-templates', data) as any
return res.data
return await templatesApi.saveEnterprise(data)
}
},
onSuccess: () => {
@@ -356,7 +349,7 @@ function EnterpriseTemplates() {
const deleteMutation = useMutation({
mutationFn: async (id: string) => {
await api.delete(`/enterprise-templates/${id}`)
await templatesApi.removeEnterprise(id)
},
onSuccess: () => {
toast.success('已删除')
@@ -368,8 +361,8 @@ function EnterpriseTemplates() {
const handleRender = async () => {
if (!selected) return
try {
const res = await api.post(`/enterprise-templates/${selected.id}/render`, { variables }) as any
setRendered(res.data.content)
const res = await templatesApi.renderEnterprise(selected.id, variables) as any
setRendered(res.content)
} catch {
toast.error('渲染失败')
}
+15 -22
View File
@@ -5,8 +5,7 @@ import { AlertTriangle, Check, ChevronRight, ChevronLeft, Shield, Info, Calculat
import { Stepper } from '../components/ui/Stepper'
import { InlineAlert } from '../components/ui/InlineAlert'
import jsPDF from 'jspdf'
import api from '../lib/api'
import { rosterApi } from '../lib/api-services'
import { rosterApi, terminationApi } from '../lib/api-services'
import { useAuthStore } from '../store/authStore'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
@@ -236,8 +235,7 @@ export default function Termination() {
const { data: profile } = useQuery<EmployeeProfile>({
queryKey: ['employee-profile', employeeId],
queryFn: async () => {
const res = await api.get(`/roster/${employeeId}/profile`) as any
return res.data
return await rosterApi.profile(employeeId)
},
enabled: !!employeeId,
})
@@ -305,8 +303,7 @@ export default function Termination() {
}[]>({
queryKey: ['checklist', reason, employeeId],
queryFn: async () => {
const res = await api.get(`/termination/checklist/${reason}`, { params: { employeeId } }) as any
return res.data
return await terminationApi.checklist(reason, employeeId)
},
enabled: !!reason && !!employeeId && step >= 2,
})
@@ -326,14 +323,13 @@ export default function Termination() {
const { data: riskAssessment } = useQuery<{ level: string; warnings: string[] }>({
queryKey: ['assess', employeeId, reason],
queryFn: async () => {
const res = await api.get(`/termination/assess/${employeeId}`, { params: { reason } }) as any
return res.data
return await terminationApi.assess(employeeId, reason)
},
enabled: !!employeeId && !!reason && step >= 1,
})
const saveMutation = useMutation({
mutationFn: (data: any) => api.post('/termination', data),
mutationFn: (data: any) => terminationApi.create(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
queryClient.invalidateQueries({ queryKey: ['employees'] })
@@ -352,8 +348,7 @@ export default function Termination() {
if (filterStatus) params.status = filterStatus
if (filterDepartment) params.department = filterDepartment
if (searchTerm) params.search = searchTerm
const res = await api.get('/termination/drafts', { params }) as any
return res.data
return await terminationApi.drafts(params)
},
enabled: view === 'list',
})
@@ -364,8 +359,7 @@ export default function Termination() {
const { data: draftDetail } = useQuery({
queryKey: ['termination-detail', draftId],
queryFn: async () => {
const res = await api.get(`/termination/detail/${draftId}`) as any
return res.data
return await terminationApi.detail(draftId!)
},
enabled: !!draftId && view === 'detail',
})
@@ -373,8 +367,8 @@ export default function Termination() {
// 保存草稿
const saveDraftMutation = useMutation({
mutationFn: (data: any) => draftId
? api.put(`/termination/draft/${draftId}`, data)
: api.post('/termination/draft', data),
? terminationApi.updateDraft(draftId!, data)
: terminationApi.createDraft(data),
onSuccess: (res: any) => {
const newId = draftId || res?.data?.id
setDraftId(newId)
@@ -386,7 +380,7 @@ export default function Termination() {
// 提交审批
const submitMutation = useMutation({
mutationFn: () => api.post(`/termination/draft/${draftId}/submit`),
mutationFn: () => terminationApi.submit(draftId!),
onSuccess: () => {
toast.success('已提交审批')
queryClient.invalidateQueries({ queryKey: ['termination-drafts'] })
@@ -398,7 +392,7 @@ export default function Termination() {
// 审批通过
const approveMutation = useMutation({
mutationFn: (comment: string) => api.post(`/termination/draft/${draftId}/approve`, { comment }),
mutationFn: (comment: string) => terminationApi.approve(draftId!, comment),
onSuccess: () => {
toast.success('审批通过')
queryClient.invalidateQueries({ queryKey: ['termination-drafts'] })
@@ -409,7 +403,7 @@ export default function Termination() {
// 审批驳回
const rejectMutation = useMutation({
mutationFn: (comment: string) => api.post(`/termination/draft/${draftId}/reject`, { comment }),
mutationFn: (comment: string) => terminationApi.reject(draftId!, comment),
onSuccess: () => {
toast.success('已驳回')
queryClient.invalidateQueries({ queryKey: ['termination-drafts'] })
@@ -420,7 +414,7 @@ export default function Termination() {
// 执行解聘
const executeMutation = useMutation({
mutationFn: () => api.post(`/termination/draft/${draftId}/execute`),
mutationFn: () => terminationApi.execute(draftId!),
onSuccess: () => {
toast.success('解聘已执行')
queryClient.invalidateQueries({ queryKey: ['termination-drafts'] })
@@ -436,7 +430,7 @@ export default function Termination() {
// 撤销
const cancelMutation = useMutation({
mutationFn: () => api.post(`/termination/draft/${draftId}/cancel`),
mutationFn: () => terminationApi.cancel(draftId!),
onSuccess: () => {
toast.success('已撤销')
queryClient.invalidateQueries({ queryKey: ['termination-drafts'] })
@@ -451,8 +445,7 @@ export default function Termination() {
const { data: evidenceChain } = useQuery({
queryKey: ['evidence-chain', employeeId],
queryFn: async () => {
const res = await api.get(`/roster/${employeeId}/evidence-chain`) as any
return res.data
return await rosterApi.evidenceChain(employeeId)
},
enabled: !!employeeId && step === 5 && saveMutation.isSuccess,
})
+8 -14
View File
@@ -6,7 +6,7 @@ import {
Repeat, Pause, FileText, XCircle, UserX, FileMinus, Briefcase,
Loader2, ChevronRight, Trash2, Send, X, Eye,
} from 'lucide-react'
import api from '../lib/api'
import { workProcessApi } from '../lib/api-services'
import Card from '../components/ui/Card'
import Button from '../components/ui/Button'
import { Input, Label, Select } from '../components/ui/Input'
@@ -155,15 +155,13 @@ export default function WorkProcess() {
const params: any = { page, pageSize }
if (filterType) params.type = filterType
if (filterStatus) params.status = filterStatus
const res = await api.get('/work-processes', { params }) as any
return res.data
return await workProcessApi.list({ page, pageSize, type: filterType || undefined, status: filterStatus || undefined } as any)
},
})
const createMutation = useMutation({
mutationFn: async (data: any) => {
const res = await api.post('/work-processes', data) as any
return res.data
return await workProcessApi.create(data)
},
onSuccess: () => {
toast.success('已创建草稿')
@@ -177,8 +175,7 @@ export default function WorkProcess() {
const submitMutation = useMutation({
mutationFn: async (id: string) => {
const res = await api.post(`/work-processes/${id}/submit`) as any
return res.data
return await workProcessApi.submit(id)
},
onSuccess: () => {
toast.success('已提交并执行')
@@ -190,8 +187,7 @@ export default function WorkProcess() {
const cancelMutation = useMutation({
mutationFn: async (id: string) => {
const res = await api.post(`/work-processes/${id}/cancel`) as any
return res.data
return await workProcessApi.cancel(id)
},
onSuccess: () => {
toast.success('已撤销')
@@ -203,7 +199,7 @@ export default function WorkProcess() {
const deleteMutation = useMutation({
mutationFn: async (id: string) => {
await api.delete(`/work-processes/${id}`)
await workProcessApi.remove(id)
},
onSuccess: () => {
toast.success('已删除')
@@ -214,8 +210,7 @@ export default function WorkProcess() {
const previewMutation = useMutation({
mutationFn: async (id: string) => {
const res = await api.get(`/work-processes/${id}/preview`) as any
return res.data
return await workProcessApi.preview(id)
},
onSuccess: (data) => {
setPreviewContent(data.content)
@@ -426,8 +421,7 @@ function DetailContent({ id, previewContent, onPreview, onSubmit, onCancel, onDe
const { data, isLoading } = useQuery({
queryKey: ['work-process', id],
queryFn: async () => {
const res = await api.get(`/work-processes/${id}`) as any
return res.data
return await workProcessApi.detail(id!)
},
enabled: !!id,
})
+4 -4
View File
@@ -2,7 +2,7 @@ import { useState } from 'react'
import { Link } from 'react-router-dom'
import { Eye, EyeOff } from 'lucide-react'
import Logo from '../../components/ui/Logo'
import api from '../../lib/api'
import { authApi } from '../../lib/api-services'
import { Input, Label } from '../../components/ui/Input'
import Button from '../../components/ui/Button'
@@ -25,8 +25,8 @@ export default function ForgotPassword() {
}
setLoading(true)
try {
const res = await api.post('/auth/forgot-password/send-code', { phone }) as any
setSentCode(res.data?.code || '')
const res = await authApi.forgotPasswordSendCode(phone) as any
setSentCode(res?.code || '')
setStep(2)
} catch (err: any) {
setError(err.response?.data?.error?.message || '发送失败,请稍后重试')
@@ -47,7 +47,7 @@ export default function ForgotPassword() {
}
setLoading(true)
try {
await api.post('/auth/forgot-password/verify', { phone, code, newPassword })
await authApi.forgotPasswordVerify({ phone, code, newPassword })
setSuccess(true)
} catch (err: any) {
setError(err.response?.data?.error?.message || '重置失败,请稍后重试')
+3 -3
View File
@@ -6,7 +6,7 @@ import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { useAuthStore } from '../../store/authStore'
import api from '../../lib/api'
import { authApi } from '../../lib/api-services'
import { Input, Label } from '../../components/ui/Input'
import Button from '../../components/ui/Button'
@@ -52,13 +52,13 @@ export default function Login() {
setError('')
setLoading(true)
try {
const res = await api.post('/auth/login', data) as any
const res = await authApi.login(data) as any
if (remember) {
localStorage.setItem('admin-login-remember', JSON.stringify({ phone: data.phone, password: data.password }))
} else {
localStorage.removeItem('admin-login-remember')
}
setAuth(res.data.user, res.data.accessToken, res.data.refreshToken)
setAuth(res.user, res.accessToken, res.refreshToken)
navigate('/')
} catch (err: any) {
setError(err.response?.data?.error?.message || '登录失败,请稍后重试')
+3 -3
View File
@@ -6,7 +6,7 @@ import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { useAuthStore } from '../../store/authStore'
import api from '../../lib/api'
import { authApi } from '../../lib/api-services'
import { Input, Label } from '../../components/ui/Input'
import Button from '../../components/ui/Button'
@@ -37,8 +37,8 @@ export default function Register() {
setError('')
setLoading(true)
try {
const res = await api.post('/auth/register', data) as any
setAuth(res.data.user, res.data.accessToken, res.data.refreshToken)
const res = await authApi.register(data) as any
setAuth(res.user, res.accessToken, res.refreshToken)
navigate('/')
} catch (err: any) {
setError(err.response?.data?.error?.message || '注册失败,请稍后重试')
+2 -3
View File
@@ -12,7 +12,7 @@ import {
import Card from '../../components/ui/Card'
import Button from '../../components/ui/Button'
import { InlineAlert } from '../../components/ui/InlineAlert'
import api from '../../lib/api'
import { dashboardApi } from '../../lib/api-services'
/** 风险等级配置 */
const RISK_LEVELS: Record<string, { label: string; color: string; bg: string }> = {
@@ -39,8 +39,7 @@ export default function RiskCenter() {
const { data: risks = [], isLoading } = useQuery<any[]>({
queryKey: ['risk-center'],
queryFn: async () => {
const res = await api.get('/dashboard/risks') as any
return res.data || []
return await dashboardApi.risks()
},
})
@@ -5,7 +5,7 @@ import { useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, BarChart, Bar, XAxis, YAxis, CartesianGrid, Legend } from 'recharts'
import { Award, TrendingUp } from 'lucide-react'
import api from '../../lib/api'
import { dashboardApi } from '../../lib/api-services'
const GRADE_COLORS: Record<string, string> = {
'A': '#237A57',
@@ -22,8 +22,7 @@ export default function PerformanceStats() {
const { data, isLoading } = useQuery({
queryKey: ['performance-stats', period],
queryFn: async () => {
const res = await api.get(`/dashboard/performance-stats?period=${period}`)
return res.data.data
return await dashboardApi.performanceStats(period)
},
})
+2 -3
View File
@@ -5,7 +5,7 @@
import { useQuery } from '@tanstack/react-query'
import { Link } from 'react-router-dom'
import { AlertCircle, Layers, FileText, Heart, ArrowRight, ListTodo } from 'lucide-react'
import api from '../../lib/api'
import { dashboardApi } from '../../lib/api-services'
import { InlineAlert } from '../../components/ui/InlineAlert'
interface NextAction {
@@ -44,8 +44,7 @@ export function TaskCenter() {
const { data, isLoading } = useQuery<{ actions: ActionGroup[]; totalCount: number }>({
queryKey: ['next-actions'],
queryFn: async () => {
const res = await api.get('/dashboard/workspace/next-actions') as any
return res.data
return await dashboardApi.nextActions()
},
staleTime: 60_000,
})
@@ -4,14 +4,13 @@
import { useQuery } from '@tanstack/react-query'
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts'
import { UserPlus, UserMinus, Users, TrendingDown } from 'lucide-react'
import api from '../../lib/api'
import { dashboardApi } from '../../lib/api-services'
export default function TurnoverStats() {
const { data, isLoading } = useQuery({
queryKey: ['turnover-stats'],
queryFn: async () => {
const res = await api.get('/dashboard/turnover-stats?months=12')
return res.data.data
return await dashboardApi.turnoverStats(12)
},
})
@@ -4,7 +4,7 @@
import { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import { Building2, Users, FileText, Calculator, TrendingUp } from 'lucide-react'
import api from '../../lib/api'
import { platformApi } from '../../lib/api-services'
interface DashboardData {
totalOrgs: number
@@ -29,7 +29,7 @@ export default function PlatformDashboard() {
const [loading, setLoading] = useState(true)
useEffect(() => {
api.get('/platform/dashboard').then((res: any) => setData(res.data)).finally(() => setLoading(false))
platformApi.dashboard().then((data: any) => setData(data)).finally(() => setLoading(false))
}, [])
if (loading) return <div className="text-center py-12 text-gray-400">...</div>
@@ -10,7 +10,7 @@ import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { useAuthStore } from '../../store/authStore'
import api from '../../lib/api'
import { authApi } from '../../lib/api-services'
import { Input, Label } from '../../components/ui/Input'
import Button from '../../components/ui/Button'
@@ -54,18 +54,18 @@ export default function PlatformLogin() {
}
}, [setValue])
const onSubmit = async (data: FormData) => {
const onSubmit = async (formData: FormData) => {
setError('')
setLoading(true)
try {
const res = await api.post('/auth/platform-login', data) as any
const res = await authApi.platformLogin(formData) as any
// 登录成功后保存/清除记住的账号密码
if (remember) {
localStorage.setItem(STORAGE_KEY, JSON.stringify({ phone: data.phone, password: data.password }))
localStorage.setItem(STORAGE_KEY, JSON.stringify({ phone: formData.phone, password: formData.password }))
} else {
localStorage.removeItem(STORAGE_KEY)
}
setAuth(res.data.user, res.data.accessToken, res.data.refreshToken)
setAuth(res.user, res.accessToken, res.refreshToken)
navigate('/platform/dashboard')
} catch (err: any) {
setError(err.response?.data?.error?.message || '登录失败,请稍后重试')
+10 -10
View File
@@ -3,7 +3,7 @@
*/
import { useEffect, useState } from 'react'
import { Search, Building2, Eye, Trash2, Edit2, Plus } from 'lucide-react'
import api from '../../lib/api'
import { platformApi } from '../../lib/api-services'
import { Input, Select, Label } from '../../components/ui/Input'
import Button from '../../components/ui/Button'
@@ -44,9 +44,9 @@ export default function PlatformOrgs() {
const params: any = { page, pageSize }
if (search) params.search = search
if (planFilter) params.plan = planFilter
const res = await api.get('/platform/orgs', { params }) as any
setOrgs(res.data.list)
setTotal(res.data.total)
const res = await platformApi.orgs(params) as any
setOrgs(res.list)
setTotal(res.total)
} finally {
setLoading(false)
}
@@ -62,8 +62,8 @@ export default function PlatformOrgs() {
setEditAdmin({ name: '', phone: '', password: '' })
// 加载企业管理员信息
try {
const res = await api.get(`/platform/orgs/${org.id}`) as any
const admin = res.data.users?.find((u: any) => u.role === 'ADMIN')
const res = await platformApi.orgDetail(org.id) as any
const admin = res.users?.find((u: any) => u.role === 'ADMIN')
if (admin) {
setEditAdmin({ name: admin.name || '', phone: admin.phone || '', password: '' })
}
@@ -75,7 +75,7 @@ export default function PlatformOrgs() {
const handleSaveEdit = async () => {
if (!editOrg) return
try {
await api.put(`/platform/orgs/${editOrg.id}`, {
await platformApi.updateOrg(editOrg.id, {
name: editOrg.name,
plan: editOrg.plan,
maxEmployees: editOrg.maxEmployees,
@@ -85,7 +85,7 @@ export default function PlatformOrgs() {
})
// 如果管理员信息有改动,同步保存
if (editAdmin.name || editAdmin.phone || editAdmin.password) {
await api.put(`/platform/orgs/${editOrg.id}/admin`, {
await platformApi.updateOrgAdmin(editOrg.id, {
adminName: editAdmin.name || undefined,
adminPhone: editAdmin.phone || undefined,
adminPassword: editAdmin.password || undefined,
@@ -105,7 +105,7 @@ export default function PlatformOrgs() {
}
setCreating(true)
try {
await api.post('/platform/orgs', createForm)
await platformApi.createOrg(createForm)
setCreateOpen(false)
setCreateForm({
name: '', plan: 'FREE', maxEmployees: 20, city: '',
@@ -123,7 +123,7 @@ export default function PlatformOrgs() {
const handleDelete = async () => {
if (!deleteOrg) return
try {
await api.delete(`/platform/orgs/${deleteOrg.id}`)
await platformApi.removeOrg(deleteOrg.id)
setDeleteOrg(null)
fetchOrgs()
} catch (err: any) {
@@ -3,7 +3,7 @@
*/
import { useEffect, useState } from 'react'
import { Search, Ban, CheckCircle } from 'lucide-react'
import api from '../../lib/api'
import { platformApi } from '../../lib/api-services'
import { Input, Select } from '../../components/ui/Input'
import Button from '../../components/ui/Button'
@@ -37,17 +37,17 @@ export default function PlatformUsers() {
const params: any = { page, pageSize }
if (search) params.search = search
if (orgFilter) params.orgId = orgFilter
const res = await api.get('/platform/users', { params }) as any
setUsers(res.data.list)
setTotal(res.data.total)
const res = await platformApi.users(params) as any
setUsers(res.list)
setTotal(res.total)
} finally {
setLoading(false)
}
}
useEffect(() => {
api.get('/platform/orgs', { params: { pageSize: 200 } }).then((res: any) => {
setOrgs(res.data.list.map((o: any) => ({ id: o.id, name: o.name })))
platformApi.orgs({ pageSize: 200 }).then((res: any) => {
setOrgs(res.list.map((o: any) => ({ id: o.id, name: o.name })))
})
}, [])
@@ -56,7 +56,7 @@ export default function PlatformUsers() {
const handleToggle = async (user: UserItem) => {
try {
await api.put(`/platform/users/${user.id}/toggle`)
await platformApi.toggleUser(user.id)
fetchUsers()
} catch (err: any) {
alert(err.response?.data?.error?.message || '操作失败')
+2 -3
View File
@@ -5,7 +5,7 @@
import { useEffect, useState } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { Loader2, CheckCircle, AlertCircle } from 'lucide-react'
import api from '../../lib/api'
import { portalApi } from '../../lib/api-services'
import Logo from '../../components/ui/Logo'
export default function AutoLogin() {
@@ -21,8 +21,7 @@ export default function AutoLogin() {
setErrorMsg('缺少登录凭证')
return
}
api.get('/portal/auto-login', { params: { token } }).then((res: any) => {
const data = res.data?.data || res.data
portalApi.autoLogin(token).then((data: any) => {
if (data?.token) {
localStorage.setItem('portalToken', data.token)
localStorage.setItem('portalEmployee', JSON.stringify(data.employee))
@@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'
import { useSearchParams } from 'react-router-dom'
import { PenTool, Check, AlertCircle } from 'lucide-react'
import api from '../../lib/api'
import { portalApi } from '../../lib/api-services'
import Card from '../../components/ui/Card'
import Button from '../../components/ui/Button'
@@ -24,8 +24,8 @@ export default function ContractConfirm() {
useEffect(() => {
if (token) {
api.get(`/portal/contract-confirm/${token}`).then((res: any) => {
setData(res.data)
portalApi.contractInfo(token).then((data: any) => {
setData(data)
}).catch((err: any) => {
setError(err.response?.data?.error?.message || '链接无效或已过期')
}).finally(() => setLoading(false))
@@ -39,9 +39,9 @@ export default function ContractConfirm() {
setSendingCode(true)
setError('')
try {
const res = await api.post('/portal/contract-confirm/send-code', { token }) as any
const data = await portalApi.contractConfirmSendCode(token) as any
setCodeSent(true)
setDevCode(res.data?.data?.code || '')
setDevCode(data?.code || '')
} catch (err: any) {
setError(err.response?.data?.error?.message || '验证码发送失败')
} finally {
@@ -52,7 +52,7 @@ export default function ContractConfirm() {
const handleConfirm = async () => {
setSubmitting(true)
try {
await api.post('/portal/contract-confirm', { token, agreed: true, verifyCode })
await portalApi.contractConfirm(token, verifyCode)
setConfirmed(true)
} catch (err: any) {
setError(err.response?.data?.error?.message || '确认失败')
+2 -11
View File
@@ -9,17 +9,9 @@ import {
DollarSign, FileText, CalendarCheck, ScrollText,
TrendingUp, Clock, AlertCircle, ChevronRight,
} from 'lucide-react'
import { portalApi } from '../../lib/api-services'
import Card from '../../components/ui/Card'
import { InlineAlert } from '../../components/ui/InlineAlert'
import axios from 'axios'
/** 员工端 API 实例(自动携带 portalToken */
const portalApi = axios.create({ baseURL: '/api/v1/portal' })
portalApi.interceptors.request.use((config: any) => {
const token = localStorage.getItem('portalToken')
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
/** 金额格式化 */
const fmt = (n: number) => (n || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
@@ -41,8 +33,7 @@ export default function EmployeeHome() {
const { data: overview, isLoading } = useQuery<any>({
queryKey: ['portal-home-overview'],
queryFn: async () => {
const res = await portalApi.get('/home/overview') as any
return res.data
return await portalApi.homeOverview()
},
})
+2 -3
View File
@@ -1,7 +1,7 @@
import { useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { Loader2, CalendarCheck } from 'lucide-react'
import api from '../../lib/api'
import { portalApi } from '../../lib/api-services'
export default function MyAttendance() {
const [month, setMonth] = useState(new Date().toISOString().slice(0, 7))
@@ -9,8 +9,7 @@ export default function MyAttendance() {
const { data, isLoading } = useQuery({
queryKey: ['portal-attendance', month],
queryFn: async () => {
const res = await api.get('/portal/attendance', { params: { month } }) as any
return res.data
return await portalApi.attendance(month)
},
})
+4 -12
View File
@@ -1,7 +1,7 @@
import { useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { FileText, AlertCircle, Check, RefreshCw, Calendar, Briefcase, Clock } from 'lucide-react'
import api from '../../lib/api'
import { portalApi } from '../../lib/api-services'
import Card from '../../components/ui/Card'
import Button from '../../components/ui/Button'
import EmptyState from '../../components/ui/EmptyState'
@@ -9,13 +9,6 @@ import EmptyState from '../../components/ui/EmptyState'
/** 金额格式化:保留两位小数 + 千分位 */
const fmt = (n: number) => (n || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
const portalApi = api.create({ baseURL: '/api/v1/portal' })
portalApi.interceptors.request.use((config: any) => {
const token = localStorage.getItem('portalToken')
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
export default function MyContract() {
const [resending, setResending] = useState(false)
const [resendMsg, setResendMsg] = useState('')
@@ -23,8 +16,7 @@ export default function MyContract() {
const { data, isLoading } = useQuery<any>({
queryKey: ['my-contract'],
queryFn: async () => {
const res = await portalApi.get('/contract') as any
return res.data?.data ?? null
return await portalApi.contract()
},
})
@@ -40,8 +32,8 @@ export default function MyContract() {
setResending(true)
setResendMsg('')
try {
const res = await portalApi.post('/contract-confirm/resend', { contractId: contract?.id }) as any
setResendMsg(res.data?.data?.message || '重发成功')
const data = await portalApi.resendContractConfirm(contract?.id) as any
setResendMsg(data?.message || '重发成功')
} catch (err: any) {
setResendMsg(err.response?.data?.error?.message || '重发失败')
} finally {
+4 -13
View File
@@ -6,18 +6,11 @@
import { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { FileText, CheckCircle, Clock, ArrowLeft, ChevronRight, ScrollText } from 'lucide-react'
import api from '../../lib/api'
import { portalApi } from '../../lib/api-services'
import Card from '../../components/ui/Card'
import Button from '../../components/ui/Button'
import EmptyState from '../../components/ui/EmptyState'
const portalApi = api.create({ baseURL: '/api/v1/portal' })
portalApi.interceptors.request.use((config: any) => {
const token = localStorage.getItem('portalToken')
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
export default function MyPolicies() {
const queryClient = useQueryClient()
const [selectedId, setSelectedId] = useState<string | null>(null)
@@ -26,8 +19,7 @@ export default function MyPolicies() {
const { data: list, isLoading } = useQuery<any>({
queryKey: ['portal-policies'],
queryFn: async () => {
const res = await portalApi.get('/policies') as any
return res.data?.data ?? res.data ?? []
return await portalApi.policies()
},
})
@@ -36,15 +28,14 @@ export default function MyPolicies() {
queryKey: ['portal-policy', selectedId],
queryFn: async () => {
if (!selectedId) return null
const res = await portalApi.get(`/policies/${selectedId}`) as any
return res.data?.data ?? res.data ?? null
return await portalApi.policyDetail(selectedId)
},
enabled: !!selectedId,
})
/** 阅读确认 */
const readMutation = useMutation({
mutationFn: (id: string) => portalApi.post(`/policies/${id}/read`),
mutationFn: (id: string) => portalApi.policyRead(id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['portal-policies'] })
queryClient.invalidateQueries({ queryKey: ['portal-policy', selectedId] })
+6 -8
View File
@@ -1,7 +1,7 @@
import { useState, useRef } from 'react'
import { useSearchParams } from 'react-router-dom'
import { ClipboardList, Check, FileText, X } from 'lucide-react'
import api from '../../lib/api'
import { portalApi } from '../../lib/api-services'
import Card from '../../components/ui/Card'
import Button from '../../components/ui/Button'
import { Input, Label } from '../../components/ui/Input'
@@ -46,8 +46,8 @@ export default function Onboarding() {
// 获取链接信息
useState(() => {
if (token) {
api.get(`/portal/onboarding/${token}`).then((res: any) => {
setOrgName(res.data.orgName)
portalApi.onboardingInfo(token).then((data: any) => {
setOrgName(data.orgName)
}).catch((err: any) => {
setError(err.response?.data?.error?.message || '链接无效')
})
@@ -63,10 +63,8 @@ export default function Onboarding() {
const formData = new FormData()
formData.append('file', file)
formData.append('fileType', currentFileType)
const res = await api.post(`/portal/onboarding/${token}/upload`, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
}) as any
setUploadedFiles([...uploadedFiles, res.data.data])
const data = await portalApi.onboardingUpload(token, formData) as any
setUploadedFiles([...uploadedFiles, data])
} catch (err: any) {
setError(err.response?.data?.error?.message || '文件上传失败')
} finally {
@@ -83,7 +81,7 @@ export default function Onboarding() {
setError('')
setLoading(true)
try {
await api.post('/portal/onboarding', { ...form, token, attachments: uploadedFiles })
await portalApi.onboardingSubmit({ ...form, token, attachments: uploadedFiles })
setSubmitted(true)
} catch (err: any) {
setError(err.response?.data?.error?.message || '提交失败')
@@ -4,18 +4,10 @@
*/
import { useQuery } from '@tanstack/react-query'
import { Check, Clock, AlertCircle, FileText, Upload, User, Phone, Banknote } from 'lucide-react'
import { portalApi } from '../../lib/api-services'
import Card from '../../components/ui/Card'
import { InlineAlert } from '../../components/ui/InlineAlert'
import { Stepper } from '../../components/ui/Stepper'
import axios from 'axios'
/** 员工端 API 实例 */
const portalApi = axios.create({ baseURL: '/api/v1/portal' })
portalApi.interceptors.request.use((config: any) => {
const token = localStorage.getItem('portalToken')
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
/** 入职步骤定义 */
const ONBOARDING_STEPS = [
@@ -31,8 +23,7 @@ export default function OnboardingProgress() {
const { data: progress, isLoading } = useQuery<any>({
queryKey: ['portal-onboarding-progress'],
queryFn: async () => {
const res = await portalApi.get('/onboarding/progress') as any
return res.data
return await portalApi.onboardingProgress()
},
})
+4 -14
View File
@@ -1,7 +1,7 @@
import { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { Check, TrendingUp, Download, Wallet, ChevronLeft, ChevronRight } from 'lucide-react'
import api from '../../lib/api'
import { portalApi } from '../../lib/api-services'
import Card from '../../components/ui/Card'
import Button from '../../components/ui/Button'
import EmptyState from '../../components/ui/EmptyState'
@@ -9,14 +9,6 @@ import EmptyState from '../../components/ui/EmptyState'
/** 金额格式化:保留两位小数 + 千分位 */
const fmt = (n: number) => (n || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
/** 员工端 API 实例(自动携带 portalToken */
const portalApi = api.create({ baseURL: '/api/v1/portal' })
portalApi.interceptors.request.use((config: any) => {
const token = localStorage.getItem('portalToken')
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
export default function Payslip() {
const queryClient = useQueryClient()
const [month, setMonth] = useState(new Date().toISOString().slice(0, 7))
@@ -25,21 +17,19 @@ export default function Payslip() {
const { data, isLoading } = useQuery<any>({
queryKey: ['payslip', month],
queryFn: async () => {
const res = await portalApi.get('/payslip', { params: { month } }) as any
return res.data?.data ?? null
return await portalApi.payslip(month)
},
})
const { data: history } = useQuery<any[]>({
queryKey: ['payslip-history'],
queryFn: async () => {
const res = await portalApi.get('/payslip/history') as any
return res.data?.data ?? []
return await portalApi.payslipHistory()
},
})
const confirmMutation = useMutation({
mutationFn: (id: string) => portalApi.post(`/payslip/${id}/confirm`),
mutationFn: (id: string) => portalApi.confirmPayslip(id),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['payslip'] }),
})
+9 -9
View File
@@ -1,7 +1,7 @@
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import Logo from '../../components/ui/Logo'
import api from '../../lib/api'
import { portalApi } from '../../lib/api-services'
import { Input, Label } from '../../components/ui/Input'
import Button from '../../components/ui/Button'
@@ -22,9 +22,9 @@ export default function PortalLogin() {
setError('')
setLoading(true)
try {
const res = await api.post('/portal/login', { phone, password }) as any
localStorage.setItem('portalToken', res.data.token)
localStorage.setItem('portalEmployee', JSON.stringify(res.data.employee))
const data = await portalApi.login(phone, password) as any
localStorage.setItem('portalToken', data.token)
localStorage.setItem('portalEmployee', JSON.stringify(data.employee))
navigate('/portal/payslip')
} catch (err: any) {
setError(err.response?.data?.error?.message || '登录失败')
@@ -36,9 +36,9 @@ export default function PortalLogin() {
const handleSendCode = async () => {
setError('')
try {
const res = await api.post('/portal/send-code', { phone }) as any
const data = await portalApi.sendCode(phone) as any
setCodeSent(true)
setDisplayedCode(res.data.code)
setDisplayedCode(data?.code || '')
} catch (err: any) {
setError(err.response?.data?.error?.message || '发送失败')
}
@@ -48,9 +48,9 @@ export default function PortalLogin() {
setError('')
setLoading(true)
try {
const res = await api.post('/portal/verify-code', { phone, code }) as any
localStorage.setItem('portalToken', res.data.token)
localStorage.setItem('portalEmployee', JSON.stringify(res.data.employee))
const data = await portalApi.verifyCode(phone, code) as any
localStorage.setItem('portalToken', data.token)
localStorage.setItem('portalEmployee', JSON.stringify(data.employee))
navigate('/portal/payslip')
} catch (err: any) {
setError(err.response?.data?.error?.message || '登录失败')
+4 -15
View File
@@ -5,19 +5,11 @@ import { useState } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { UserX, Clock, Check, X, FileText } from 'lucide-react'
import { portalApi } from '../../lib/api-services'
import Card from '../../components/ui/Card'
import Button from '../../components/ui/Button'
import { Input, Label, Select } from '../../components/ui/Input'
import { InlineAlert } from '../../components/ui/InlineAlert'
import axios from 'axios'
/** 员工端 API 实例 */
const portalApi = axios.create({ baseURL: '/api/v1/portal' })
portalApi.interceptors.request.use((config: any) => {
const token = localStorage.getItem('portalToken')
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
/** 离职原因选项 */
const RESIGN_REASONS = [
@@ -51,16 +43,14 @@ export default function ResignationApply() {
const { data: records = [], isLoading } = useQuery<any[]>({
queryKey: ['portal-resignation-status'],
queryFn: async () => {
const res = await portalApi.get('/resignation/status') as any
return Array.isArray(res?.data) ? res.data : []
return await portalApi.resignationStatus()
},
})
/** 提交离职申请 */
const submitMutation = useMutation({
mutationFn: async (data: { reason: string; expectedDate: string; remark: string }) => {
const res = await portalApi.post('/resignation/submit', data) as any
return res.data
return await portalApi.resignationSubmit(data)
},
onSuccess: () => {
toast.success('离职申请已提交,请等待HR审批')
@@ -75,8 +65,7 @@ export default function ResignationApply() {
/** 撤回离职申请 */
const withdrawMutation = useMutation({
mutationFn: async (id: string) => {
const res = await portalApi.post(`/resignation/${id}/withdraw`) as any
return res.data
return await portalApi.resignationWithdraw(id)
},
onSuccess: () => {
toast.success('离职申请已撤回')
+3 -3
View File
@@ -1,7 +1,7 @@
import { useState, useRef } from "react"
import { toast } from "sonner"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import api from "../../lib/api"
import { rosterApi, attachmentApi } from '../../lib/api-services'
import Card from "../../components/ui/Card"
import Button from "../../components/ui/Button"
import { Input, Label, Select } from "../../components/ui/Input"
@@ -15,12 +15,12 @@ export default function AttachmentInfo({ employeeId, attachments }: { employeeId
const [fileType, setFileType] = useState<'ID_CARD' | 'BANK_CARD' | 'EDUCATION' | 'OTHER'>('ID_CARD')
const addAttachmentMutation = useMutation({
mutationFn: (data: any) => api.post('/attachments', data),
mutationFn: (data: any) => attachmentApi.add(data),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['roster-profile', employeeId] }),
})
const deleteAttachmentMutation = useMutation({
mutationFn: (id: string) => api.delete(`/attachments/${id}`),
mutationFn: (id: string) => attachmentApi.remove(id),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['roster-profile', employeeId] }),
})
@@ -1,7 +1,7 @@
import { useState, useRef } from "react"
import { toast } from "sonner"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import api from "../../lib/api"
import { rosterApi, employeeApi } from '../../lib/api-services'
import Card from "../../components/ui/Card"
import Button from "../../components/ui/Button"
import { Input, Label, Select } from "../../components/ui/Input"
@@ -18,22 +18,22 @@ export default function AttendanceOvertimeInfo({ employeeId, attendanceRecords,
const [trainingForm, setTrainingForm] = useState({ trainingDate: '', topic: '', content: '', trainer: '', duration: 1, ackStatus: 'PENDING', ackDate: '', remark: '' })
const createMutation = useMutation({
mutationFn: (data: any) => api.post(`/roster/${employeeId}/attendance`, data),
mutationFn: (data: any) => rosterApi.attendance(employeeId, data),
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['roster-profile'] }); setShowForm(false) },
})
const deleteMutation = useMutation({
mutationFn: (id: string) => api.delete(`/roster/${employeeId}/attendance/${id}`),
mutationFn: (id: string) => rosterApi.removeAttendance(employeeId, id),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['roster-profile'] }),
})
const createTrainingMutation = useMutation({
mutationFn: (data: any) => api.post(`/roster/${employeeId}/training`, data),
mutationFn: (data: any) => rosterApi.training(employeeId, data),
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['roster-profile'] }); setShowForm(false) },
})
const deleteTrainingMutation = useMutation({
mutationFn: (id: string) => api.delete(`/roster/${employeeId}/training/${id}`),
mutationFn: (id: string) => rosterApi.removeTraining(employeeId, id),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['roster-profile'] }),
})
+4 -4
View File
@@ -3,7 +3,7 @@ import { useUnsavedChanges } from "../../hooks/useUnsavedChanges"
import { useState, useRef } from "react"
import { toast } from "sonner"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import api from "../../lib/api"
import { rosterApi, attachmentApi, employeeApi } from '../../lib/api-services'
import Card from "../../components/ui/Card"
import Button from "../../components/ui/Button"
import { Input, Label, Select } from "../../components/ui/Input"
@@ -18,12 +18,12 @@ export default function BasicInfo({ profile, employeeId, attachments }: { profil
const [fileType, setFileType] = useState<'ID_CARD' | 'BANK_CARD' | 'EDUCATION' | 'OTHER'>('ID_CARD')
const addAttachmentMutation = useMutation({
mutationFn: (data: any) => api.post('/attachments', data),
mutationFn: (data: any) => attachmentApi.add(data),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['roster-profile', employeeId] }),
})
const deleteAttachmentMutation = useMutation({
mutationFn: (id: string) => api.delete(`/attachments/${id}`),
mutationFn: (id: string) => attachmentApi.remove(id),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['roster-profile', employeeId] }),
})
@@ -81,7 +81,7 @@ export default function BasicInfo({ profile, employeeId, attachments }: { profil
})
const updateMutation = useMutation({
mutationFn: (data: any) => api.put(`/employees/${profile.id}`, data),
mutationFn: (data: any) => employeeApi.update(profile.id, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['roster-profile'] })
queryClient.invalidateQueries({ queryKey: ['roster'] })
@@ -1,7 +1,6 @@
import { useState, useRef } from "react"
import { toast } from "sonner"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import api from "../../lib/api"
import Card from "../../components/ui/Card"
import Button from "../../components/ui/Button"
import { Input, Label, Select } from "../../components/ui/Input"
+3 -3
View File
@@ -1,7 +1,7 @@
import { useState, useRef } from "react"
import { toast } from "sonner"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import api from "../../lib/api"
import { employeeApi } from '../../lib/api-services'
import Card from "../../components/ui/Card"
import Button from "../../components/ui/Button"
import { Input, Label, Select } from "../../components/ui/Input"
@@ -17,12 +17,12 @@ export default function ContractInfo({ employeeId, contracts, hireDate }: { empl
const [previewUrl, setPreviewUrl] = useState<string | null>(null)
const addContractMutation = useMutation({
mutationFn: (data: any) => api.post('/employees/contracts', { ...data, employeeId }),
mutationFn: (data: any) => employeeApi.addContract({ ...data, employeeId }),
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['roster-profile'] }); setShowForm(false) },
})
const deleteContractMutation = useMutation({
mutationFn: (contractId: string) => api.delete(`/employees/contracts/${contractId}`),
mutationFn: (contractId: string) => employeeApi.removeContract(contractId),
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['roster-profile'] }); toast.success('合同已删除') },
})
@@ -1,7 +1,7 @@
import { useState, useRef } from "react"
import { toast } from "sonner"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import api from "../../lib/api"
import { rosterApi } from '../../lib/api-services'
import Card from "../../components/ui/Card"
import Button from "../../components/ui/Button"
import { Input, Label, Select } from "../../components/ui/Input"
@@ -17,12 +17,12 @@ export default function DisciplinaryInfo({ employeeId, records }: { employeeId:
const [form, setForm] = useState({ violationDate: '', violationType: 'LATE', description: '', severity: 'WARNING', action: 'ORAL_WARNING', actionDetail: '', employeeAck: false, ackDate: '', ackMethod: 'SIGN', witness: '' })
const createMutation = useMutation({
mutationFn: (data: any) => api.post(`/roster/${employeeId}/disciplinary`, data),
mutationFn: (data: any) => rosterApi.createDisciplinary(employeeId, data),
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['roster-profile'] }); setShowForm(false) },
})
const deleteMutation = useMutation({
mutationFn: (id: string) => api.delete(`/roster/${employeeId}/disciplinary/${id}`),
mutationFn: (id: string) => rosterApi.removeDisciplinary(employeeId, id),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['roster-profile'] }),
})
@@ -1,7 +1,7 @@
/** EmployeeProfile 组件 - 员工详情页 */
import { useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import api from '../../lib/api'
import { rosterApi } from '../../lib/api-services'
import { DetailTab, TAB_COUNT_KEYS } from './shared'
import EmployeeProfileShell from './EmployeeProfileShell'
import BasicInfo from './BasicInfo'
@@ -23,8 +23,7 @@ export default function EmployeeProfile({ employeeId, onBack }: { employeeId: st
const { data: profile, isLoading } = useQuery<any>({
queryKey: ['roster-profile', employeeId],
queryFn: async () => {
const res = await api.get(`/roster/${employeeId}/profile`) as any
return res.data
return await rosterApi.profile(employeeId)
},
})
+2 -3
View File
@@ -1,7 +1,7 @@
import { useState, useRef } from "react"
import { toast } from "sonner"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import api from "../../lib/api"
import { rosterApi } from '../../lib/api-services'
import { useAuthStore } from "../../store/authStore"
import Card from "../../components/ui/Card"
import Button from "../../components/ui/Button"
@@ -16,8 +16,7 @@ export default function EvidenceChain({ employeeId }: { employeeId: string }) {
const { data, isLoading } = useQuery<any>({
queryKey: ['evidence-chain', employeeId],
queryFn: async () => {
const res = await api.get(`/roster/${employeeId}/evidence-chain`) as any
return res.data
return await rosterApi.evidenceChain(employeeId)
},
})
@@ -1,7 +1,7 @@
import { useState, useRef } from "react"
import { toast } from "sonner"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import api from "../../lib/api"
import { socialInsuranceApi } from '../../lib/api-services'
import Card from "../../components/ui/Card"
import Button from "../../components/ui/Button"
import { Input, Label, Select } from "../../components/ui/Input"
@@ -148,11 +148,8 @@ export function CityHistoryTab({ socialInsRecords, housingFundRecords, changeTyp
const correctMutation = useMutation({
mutationFn: async (data: any) => {
const cat = editing.cat
const url = cat === '社保'
? `/social/records/social/${editing.id}/correct`
: `/social/records/housing/${editing.id}/correct`
const res = await api.put(url, data) as any
return res.data
const type = cat === '社保' ? 'social' : 'housing'
return await socialInsuranceApi.correctRecord(type, editing.id, data)
},
onSuccess: () => {
toast.success('记录已修正')
@@ -1,7 +1,7 @@
import { useState, useRef } from "react"
import { toast } from "sonner"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import api from "../../lib/api"
import { rosterApi } from '../../lib/api-services'
import Card from "../../components/ui/Card"
import Button from "../../components/ui/Button"
import { Input, Label, Select } from "../../components/ui/Input"
@@ -17,12 +17,12 @@ export default function PerformanceInfo({ employeeId, records }: { employeeId: s
const [form, setForm] = useState({ period: '', score: 80, grade: 'B', result: 'QUALIFIED', summary: '', improvementPlan: '', employeeAck: false, ackDate: '', reviewer: '' })
const createMutation = useMutation({
mutationFn: (data: any) => api.post(`/roster/${employeeId}/performance`, data),
mutationFn: (data: any) => rosterApi.performance(employeeId, data),
onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['roster-profile'] }); setShowForm(false) },
})
const deleteMutation = useMutation({
mutationFn: (id: string) => api.delete(`/roster/${employeeId}/performance/${id}`),
mutationFn: (id: string) => rosterApi.removePerformance(employeeId, id),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['roster-profile'] }),
})
@@ -1,7 +1,7 @@
import { useState, useRef } from "react"
import { toast } from "sonner"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import api from "../../lib/api"
import { rosterApi } from '../../lib/api-services'
import Card from "../../components/ui/Card"
import Button from "../../components/ui/Button"
import { Input, Label, Select } from "../../components/ui/Input"
@@ -28,8 +28,7 @@ export default function TerminationInfo({ employeeId, profile, records }: { empl
const { data: evidenceChain, isLoading: evidenceLoading } = useQuery<any>({
queryKey: ['evidence-chain', employeeId],
queryFn: async () => {
const res = await api.get(`/roster/${employeeId}/evidence-chain`) as any
return res.data
return await rosterApi.evidenceChain(employeeId)
},
enabled: !!printRecord || showEvidence,
})
+5 -7
View File
@@ -1,7 +1,7 @@
import { useState, useRef } from "react"
import { toast } from "sonner"
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"
import api from "../../lib/api"
import { rosterApi, socialInsuranceApi } from '../../lib/api-services'
import Card from "../../components/ui/Card"
import Button from "../../components/ui/Button"
import { Input, Label, Select } from "../../components/ui/Input"
@@ -245,8 +245,7 @@ export function RehireModal({ employee, onClose, onSubmit, loading, error }: {
const { data: contractTypes = [] } = useQuery<Array<{ value: string; label: string; hasEndDate: boolean }>>({
queryKey: ['contract-types'],
queryFn: async () => {
const res = await api.get('/roster/contract-types') as any
return res.data || []
return await rosterApi.contractTypes()
},
staleTime: Infinity,
})
@@ -496,15 +495,14 @@ export function AddEmployeeModal({ onClose, onSubmit, loading, error }: {
const { data: cities = ['北京'] } = useQuery<string[]>({
queryKey: ['social-config-cities'],
queryFn: async () => {
const res = await api.get('/social/config/cities') as any
return res.data?.length ? res.data : ['北京']
const res = await socialInsuranceApi.cities() as any
return res?.length ? res : ['北京']
},
})
const { data: contractTypes = [] } = useQuery<Array<{ value: string; label: string; hasEndDate: boolean }>>({
queryKey: ['contract-types'],
queryFn: async () => {
const res = await api.get('/roster/contract-types') as any
return res.data || []
return await rosterApi.contractTypes()
},
staleTime: Infinity,
})
@@ -8,7 +8,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { TrendingUp, Save, History, Download, ShieldCheck, Clock, DollarSign, Sparkles, Users, FileText, Calculator, Award } from 'lucide-react'
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, Cell } from 'recharts'
import api from '../../lib/api'
import { dashboardApi } from '../../lib/api-services'
import Card from '../../components/ui/Card'
import Button from '../../components/ui/Button'
@@ -29,24 +29,21 @@ export default function AnnualValueReport() {
const { data: report, isLoading } = useQuery<any>({
queryKey: ['annual-value', year],
queryFn: async () => {
const res = await api.get(`/dashboard/annual-value?year=${year}`) as any
return res.data
return await dashboardApi.annualValue(year)
},
})
const { data: history } = useQuery<any>({
queryKey: ['annual-value-history'],
queryFn: async () => {
const res = await api.get('/dashboard/annual-value/history') as any
return res.data
return await dashboardApi.annualValueHistory()
},
enabled: showHistory,
})
const saveMutation = useMutation({
mutationFn: async () => {
const res = await api.post('/dashboard/annual-value/save', { year }) as any
return res.data
return await dashboardApi.annualValueSave(year)
},
onSuccess: () => {
toast.success('报告已保存')
+4 -7
View File
@@ -8,7 +8,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { Stethoscope, Save, History, CheckCircle2, AlertCircle, AlertTriangle, ChevronDown, ChevronUp } from 'lucide-react'
import { RadialBarChart, RadialBar, PolarAngleAxis, ResponsiveContainer } from 'recharts'
import api from '../../lib/api'
import { dashboardApi } from '../../lib/api-services'
import Card from '../../components/ui/Card'
import Button from '../../components/ui/Button'
@@ -23,24 +23,21 @@ export default function HealthCheck() {
const { data: healthCheck, isLoading } = useQuery<any>({
queryKey: ['health-check'],
queryFn: async () => {
const res = await api.get('/dashboard/health-check') as any
return res.data
return await dashboardApi.healthCheck()
},
})
const { data: history } = useQuery<any>({
queryKey: ['health-check-history'],
queryFn: async () => {
const res = await api.get('/dashboard/health-check/history') as any
return res.data
return await dashboardApi.healthCheckHistory()
},
enabled: showHistory,
})
const saveMutation = useMutation({
mutationFn: async () => {
const res = await api.post('/dashboard/health-check/save') as any
return res.data
return await dashboardApi.healthCheckSave()
},
onSuccess: () => {
toast.success('诊断报告已保存')