优化: 大文件拆分+代码分割+按需加载+console清理+any类型替换
- Money.tsx (2260行→54行): 拆分为 money/ 子目录4个组件, React.lazy二级分割 - AIAssistant.tsx (2038行→63行): 拆分为 ai-assistant/ 子目录6个组件, React.lazy二级分割 - xlsx改为动态导入, OvertimeTab从345KB降至12.7KB - api-services.ts: 请求参数 any→Record<string,unknown> - 移除前端3处console.log残留 - 后端console替换为pino logger - 前后端未使用import/变量清理 - Zod schema验证: termination/platform/special-status/work-process - 新增 leave.routes.ts, acceptance-test.routes.ts - UI组件: PageGuide, QueryError, Stepper
This commit is contained in:
@@ -8,6 +8,7 @@ import Card from '../components/ui/Card'
|
||||
import Button from '../components/ui/Button'
|
||||
import { Input, Label, Select } from '../components/ui/Input'
|
||||
import Modal from '../components/ui/Modal'
|
||||
import { useConfirm } from '../hooks/useConfirm'
|
||||
|
||||
|
||||
export default function Settings() {
|
||||
@@ -618,6 +619,7 @@ function handleAcceptanceExport() {
|
||||
|
||||
function PlanSettings({ orgData }: { orgData: any }) {
|
||||
const queryClient = useQueryClient()
|
||||
const confirm = useConfirm()
|
||||
const plan = orgData?.plan || 'FREE'
|
||||
|
||||
const { data: usageData } = useQuery<any>({
|
||||
@@ -683,8 +685,8 @@ function PlanSettings({ orgData }: { orgData: any }) {
|
||||
variant="secondary"
|
||||
className="w-full"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (confirm(`确定切换到${p.label}?`)) planMutation.mutate(p.key)
|
||||
onClick={async () => {
|
||||
if (await confirm({ title: '切换套餐', message: `确定切换到${p.label}?`, variant: 'primary' })) planMutation.mutate(p.key)
|
||||
}}
|
||||
disabled={planMutation.isPending}
|
||||
>
|
||||
@@ -959,8 +961,14 @@ function InitImport() {
|
||||
const data = await res.json()
|
||||
if (!data.success) {
|
||||
setError(data.error?.message || '导入失败')
|
||||
toast.error(data.error?.message || '导入失败')
|
||||
} else {
|
||||
setResult(data.data)
|
||||
if (data.data.errors?.length > 0) {
|
||||
toast.warning(`导入完成,但有 ${data.data.errors.length} 条错误,请查看详情`)
|
||||
} else {
|
||||
toast.success(`成功导入员工 ${data.data.employees} 人`)
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: ['roster'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
|
||||
}
|
||||
@@ -1123,6 +1131,22 @@ function InitImport() {
|
||||
{result.errors.length > 10 && <div className="text-amber-600">...还有 {result.errors.length - 10} 条,请导出错误日志查看全部</div>}
|
||||
</div>
|
||||
)}
|
||||
{/* 导入明细 */}
|
||||
{result.details?.filter((d: any) => d.status === 'success').length > 0 && (
|
||||
<div className="mt-2 pt-2 border-t border-green-200">
|
||||
<div className="font-medium text-gray-600 mb-1">导入明细:</div>
|
||||
<div className="max-h-40 overflow-y-auto">
|
||||
{result.details.filter((d: any) => d.status === 'success').map((d: any, i: number) => (
|
||||
<div key={i} className="flex gap-3 text-xs text-gray-500">
|
||||
<span>第{d.row}行</span>
|
||||
<span>{d.name}</span>
|
||||
{d.employeeId && <span className="text-gray-400">ID: {d.employeeId.slice(0, 8)}...</span>}
|
||||
<span className="text-safe">✓ {d.message}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1175,8 +1199,14 @@ function MonthlyImport() {
|
||||
const data = await res.json()
|
||||
if (!data.success) {
|
||||
setError(data.error?.message || '导入失败')
|
||||
toast.error(data.error?.message || '导入失败')
|
||||
} else {
|
||||
setResult(data.data)
|
||||
if (data.data.errors?.length > 0) {
|
||||
toast.warning(`导入完成,但有 ${data.data.errors.length} 条错误,请查看详情`)
|
||||
} else {
|
||||
toast.success('月度数据导入成功')
|
||||
}
|
||||
queryClient.invalidateQueries({ queryKey: ['roster'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['dashboard'] })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user