From 0256f0cf22a9eaffacd2732c8e2d8a3eccf9354c Mon Sep 17 00:00:00 2001 From: mamamiyear Date: Mon, 24 Nov 2025 09:39:30 +0800 Subject: [PATCH] fix: npm lint errors --- src/apis/request.ts | 32 +++--- src/apis/types.ts | 8 +- src/apis/upload.ts | 4 +- src/components/BatchRegister.tsx | 27 +++-- src/components/InputDrawer.tsx | 4 +- src/components/InputPanel.tsx | 48 ++++---- src/components/KeyValueList.tsx | 7 +- src/components/LayoutWrapper.tsx | 2 +- src/components/LoginModal.tsx | 7 +- src/components/MainContent.tsx | 9 +- src/components/PeopleForm.tsx | 40 +++---- src/components/RegisterModal.tsx | 6 +- src/components/ResourceList.tsx | 186 +++++++++++-------------------- src/components/SiderMenu.tsx | 2 +- src/components/UserProfile.tsx | 2 +- src/contexts/AuthContext.tsx | 21 ++-- src/contexts/useAuth.ts | 12 ++ 17 files changed, 195 insertions(+), 222 deletions(-) create mode 100644 src/contexts/useAuth.ts diff --git a/src/apis/request.ts b/src/apis/request.ts index 697978d..77f6d62 100644 --- a/src/apis/request.ts +++ b/src/apis/request.ts @@ -6,16 +6,16 @@ import { API_CONFIG } from './config'; export interface RequestOptions { method?: 'GET' | 'POST' | 'PUT' | 'DELETE'; headers?: Record; - body?: any; + body?: unknown; timeout?: number; } // 自定义错误类 export class ApiError extends Error { status?: number; - data?: any; + data?: unknown; - constructor(message: string, status?: number, data?: any) { + constructor(message: string, status?: number, data?: unknown) { super(message); this.name = 'ApiError'; this.status = status; @@ -24,7 +24,7 @@ export class ApiError extends Error { } // 基础请求函数 -export async function request( +export async function request( url: string, options: RequestOptions = {} ): Promise { @@ -67,18 +67,19 @@ export async function request( clearTimeout(timeoutId); if (!response.ok) { - let errorData: any; + let errorData: unknown; try { errorData = await response.json(); } catch { errorData = { message: response.statusText }; } - throw new ApiError( - errorData.message || `HTTP ${response.status}: ${response.statusText}`, - response.status, - errorData - ); + let messageText = `HTTP ${response.status}: ${response.statusText}`; + if (errorData && typeof errorData === 'object') { + const maybe = errorData as { message?: string }; + messageText = maybe.message ?? messageText; + } + throw new ApiError(messageText, response.status, errorData); } // 检查响应是否有内容 @@ -108,7 +109,8 @@ export async function request( } // GET 请求 -export function get(url: string, params?: Record): Promise { +type QueryParamValue = string | number | boolean | null | undefined; +export function get(url: string, params?: Record): Promise { let fullUrl = url; if (params) { @@ -129,7 +131,7 @@ export function get(url: string, params?: Record): Promise } // POST 请求 -export function post(url: string, data?: any, options?: Partial): Promise { +export function post(url: string, data?: unknown, options?: Partial): Promise { return request(url, { method: 'POST', body: data, @@ -138,7 +140,7 @@ export function post(url: string, data?: any, options?: Partial(url: string, data?: any): Promise { +export function put(url: string, data?: unknown): Promise { return request(url, { method: 'PUT', body: data, @@ -146,12 +148,12 @@ export function put(url: string, data?: any): Promise { } // DELETE 请求 -export function del(url: string): Promise { +export function del(url: string): Promise { return request(url, { method: 'DELETE' }); } // 文件上传请求 -export function upload(url: string, file: File, fieldName = 'file', options?: Partial): Promise { +export function upload(url: string, file: File, fieldName = 'file', options?: Partial): Promise { const formData = new FormData(); formData.append(fieldName, file); diff --git a/src/apis/types.ts b/src/apis/types.ts index 3885f3c..eed361d 100644 --- a/src/apis/types.ts +++ b/src/apis/types.ts @@ -1,7 +1,7 @@ // API 请求和响应类型定义 // 基础响应类型 -export interface ApiResponse { +export interface ApiResponse { data?: T; error_code: number; error_info?: string; @@ -25,7 +25,7 @@ export interface PostInputRequest { // 人员信息请求类型 export interface PostPeopleRequest { - people: Record; + people: People; } // 人员查询参数类型 @@ -39,6 +39,7 @@ export interface GetPeoplesParams { offset?: number; search?: string; top_k?: number; + [key: string]: string | number | boolean | null | undefined; } // 人员信息类型 @@ -52,8 +53,9 @@ export interface People { marital_status?: string; created_at?: number; match_requirement?: string; - [key: string]: any; cover?: string; + introduction?: Record; + comments?: { remark?: { content: string; updated_at: number } }; } // 分页响应类型 diff --git a/src/apis/upload.ts b/src/apis/upload.ts index 2d61f49..185d8d8 100644 --- a/src/apis/upload.ts +++ b/src/apis/upload.ts @@ -56,8 +56,8 @@ export async function postInputImageWithProgress( try { const response = xhr.responseText ? JSON.parse(xhr.responseText) : {}; resolve(response); - } catch (error) { - resolve({error_code: 1, error_info: '解析响应失败'}); + } catch { + resolve({ error_code: 1, error_info: '解析响应失败' }); } } else { reject(new Error(`HTTP ${xhr.status}: ${xhr.statusText}`)); diff --git a/src/components/BatchRegister.tsx b/src/components/BatchRegister.tsx index 785aa95..ae889f4 100644 --- a/src/components/BatchRegister.tsx +++ b/src/components/BatchRegister.tsx @@ -5,10 +5,10 @@ import PeopleForm from './PeopleForm.tsx' import InputDrawer from './InputDrawer.tsx' import { createPeoplesBatch, type People } from '../apis' -const { Panel } = Collapse as any +const Panel = Collapse.Panel const { Content } = Layout -type FormItem = { id: string; initialData?: any } +type FormItem = { id: string; initialData?: Partial } type Props = { inputOpen?: boolean; onCloseInput?: () => void; containerEl?: HTMLElement | null } @@ -29,7 +29,18 @@ const BatchRegister: React.FC = ({ inputOpen = false, onCloseInput, conta delete instancesRef.current[id] } - const buildPeople = (values: any, common: any): People => { + type FormValues = { + name: string; + contact?: string; + gender: string; + age: number; + height?: number; + marital_status?: string; + introduction?: Record; + match_requirement?: string; + cover?: string; + } + const buildPeople = (values: FormValues, common: { contact?: string }): People => { return { name: values.name, contact: values.contact || common.contact || undefined, @@ -43,9 +54,9 @@ const BatchRegister: React.FC = ({ inputOpen = false, onCloseInput, conta } } - const handleInputResult = (list: any) => { + const handleInputResult = (list: unknown) => { const arr = Array.isArray(list) ? list : [list] - const next: FormItem[] = arr.map((data: any) => ({ id: `${Date.now()}-${Math.random()}`, initialData: data })) + const next: FormItem[] = arr.map((data) => ({ id: `${Date.now()}-${Math.random()}`, initialData: data as Partial })) setItems((prev) => [...prev, ...next]) } @@ -61,12 +72,12 @@ const BatchRegister: React.FC = ({ inputOpen = false, onCloseInput, conta message.error('表单未就绪') return } - const allValues: any[] = [] + const allValues: FormValues[] = [] for (const f of forms) { try { const v = await f.validateFields() allValues.push(v) - } catch (err: any) { + } catch { setLoading(false) message.error('请完善全部表单后再提交') return @@ -87,7 +98,7 @@ const BatchRegister: React.FC = ({ inputOpen = false, onCloseInput, conta setItems([{ id: `${Date.now()}-${Math.random()}` }]) commonForm.resetFields() } - } catch (e: any) { + } catch { message.error('提交失败') } finally { setLoading(false) diff --git a/src/components/InputDrawer.tsx b/src/components/InputDrawer.tsx index d478f2d..913db06 100644 --- a/src/components/InputDrawer.tsx +++ b/src/components/InputDrawer.tsx @@ -7,7 +7,7 @@ import './InputDrawer.css'; type Props = { open: boolean; onClose: () => void; - onResult?: (data: any) => void; + onResult?: (data: unknown) => void; containerEl?: HTMLElement | null; // 抽屉挂载容器(用于放在标题栏下方) showUpload?: boolean; // 透传到输入面板,控制图片上传按钮 mode?: 'input' | 'search' | 'batch-image'; // 透传到输入面板,控制工作模式 @@ -31,7 +31,7 @@ const InputDrawer: React.FC = ({ open, onClose, onResult, containerEl, sh // 在输入处理成功(onResult 被调用)后,自动关闭抽屉 const handleResult = React.useCallback( - (data: any) => { + (data: unknown) => { onResult?.(data); onClose(); }, diff --git a/src/components/InputPanel.tsx b/src/components/InputPanel.tsx index 9c151a9..61bdde4 100644 --- a/src/components/InputPanel.tsx +++ b/src/components/InputPanel.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { Input, Upload, message, Button, Spin, Tag } from 'antd'; +import type { UploadFile, RcFile } from 'antd/es/upload/interface'; import { PictureOutlined, SendOutlined, LoadingOutlined, SearchOutlined } from '@ant-design/icons'; import { postInput, postInputImage, getPeoples } from '../apis'; import './InputPanel.css'; @@ -7,14 +8,14 @@ import './InputPanel.css'; const { TextArea } = Input; interface InputPanelProps { - onResult?: (data: any) => void; + onResult?: (data: unknown) => void; showUpload?: boolean; // 是否显示图片上传按钮,默认显示 mode?: 'input' | 'search' | 'batch-image'; // 输入面板工作模式,新增批量图片模式 } const InputPanel: React.FC = ({ onResult, showUpload = true, mode = 'input' }) => { const [value, setValue] = React.useState(''); - const [fileList, setFileList] = React.useState([]); + const [fileList, setFileList] = React.useState([]); const [loading, setLoading] = React.useState(false); // 批量模式不保留文本内容 @@ -63,9 +64,9 @@ const InputPanel: React.FC = ({ onResult, showUpload = true, mo } setLoading(true); try { - const results: any[] = []; + const results: unknown[] = []; for (let i = 0; i < fileList.length; i++) { - const f = fileList[i].originFileObj || fileList[i]; + const f = fileList[i].originFileObj as RcFile | undefined; if (!f) continue; const resp = await postInputImage(f); if (resp && resp.error_code === 0 && resp.data) { @@ -95,7 +96,7 @@ const InputPanel: React.FC = ({ onResult, showUpload = true, mo // 如果有图片,优先处理图片上传 if (hasImage) { - const file = fileList[0].originFileObj || fileList[0]; + const file = fileList[0].originFileObj as RcFile | undefined; if (!file) { message.error('图片文件无效,请重新选择'); return; @@ -152,18 +153,18 @@ const InputPanel: React.FC = ({ onResult, showUpload = true, mo if (!items || items.length === 0) return; if (mode === 'batch-image') { - const newEntries: any[] = []; + const newEntries: UploadFile[] = []; for (let i = 0; i < items.length; i++) { const item = items[i]; if (item.kind === 'file') { const file = item.getAsFile(); if (file && file.type.startsWith('image/')) { - const entry = { + const entry: UploadFile = { uid: `${Date.now()}-${Math.random()}`, name: 'image', status: 'done', - originFileObj: file, - } as any; + originFileObj: file as unknown as RcFile, + }; newEntries.push(entry); } } @@ -190,14 +191,13 @@ const InputPanel: React.FC = ({ onResult, showUpload = true, mo if (firstImage) { e.preventDefault(); setValue(''); - setFileList([ - { - uid: `${Date.now()}-${Math.random()}`, - name: 'image', - status: 'done', - originFileObj: firstImage, - } as any, - ]); + const item: UploadFile = { + uid: `${Date.now()}-${Math.random()}`, + name: 'image', + status: 'done', + originFileObj: firstImage as unknown as RcFile, + }; + setFileList([item]); message.success('已添加剪贴板图片'); } } @@ -278,9 +278,9 @@ const InputPanel: React.FC = ({ onResult, showUpload = true, mo fileList={fileList} onChange={({ fileList: nextFileList }) => { if (mode === 'batch-image') { - const normalized = nextFileList.map((entry: any) => { - const raw = entry.originFileObj || entry; - return { ...entry, name: 'image', originFileObj: raw }; + const normalized: UploadFile[] = nextFileList.map((entry) => { + const raw = (entry as UploadFile).originFileObj; + return { ...(entry as UploadFile), name: 'image', originFileObj: raw }; }); setValue(''); setFileList(normalized); @@ -290,15 +290,15 @@ const InputPanel: React.FC = ({ onResult, showUpload = true, mo return; } // 仅添加第一张 - const first = nextFileList[0] as any; - const raw = first.originFileObj || first; - const renamed = { ...first, name: 'image', originFileObj: raw }; + const first = nextFileList[0] as UploadFile; + const raw = first.originFileObj; + const renamed: UploadFile = { ...first, name: 'image', originFileObj: raw }; setValue(''); setFileList([renamed]); } }} onRemove={(file) => { - setFileList((prev) => prev.filter((x) => x.uid !== (file as any).uid)); + setFileList((prev) => prev.filter((x) => x.uid !== (file as UploadFile).uid)); return true; }} showUploadList={false} diff --git a/src/components/KeyValueList.tsx b/src/components/KeyValueList.tsx index 976746b..5ec99dc 100644 --- a/src/components/KeyValueList.tsx +++ b/src/components/KeyValueList.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { Row, Col, Input, Button } from 'antd'; import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'; import './KeyValueList.css'; @@ -15,11 +15,8 @@ type Props = { const KeyValueList: React.FC = ({ value, onChange }) => { const [rows, setRows] = useState([]); + const initializedRef = useRef(false); useEffect(() => { - // 初始化时提供一行空输入;之后只合并父值,不再自动新增空行 - const initializedRef = (KeyValueList as any)._initializedRef || { current: false }; - (KeyValueList as any)._initializedRef = initializedRef; - setRows((prev) => { const existingIdByKey = new Map(prev.filter((r) => r.k).map((r) => [r.k, r.id])); const valuePairs: KeyValuePair[] = value diff --git a/src/components/LayoutWrapper.tsx b/src/components/LayoutWrapper.tsx index 10b6bee..56ec30f 100644 --- a/src/components/LayoutWrapper.tsx +++ b/src/components/LayoutWrapper.tsx @@ -66,7 +66,7 @@ const LayoutWrapper: React.FC = () => { showInput={isHome || isList || isBatch} /> {/* 下方为主布局:左侧菜单 + 右侧内容 */} - + void; - onOk: (values: any) => Promise; + onOk: (values: LoginRequest) => Promise; title: string; username?: string; usernameReadOnly?: boolean; @@ -30,12 +31,12 @@ const LoginModal: React.FC = ({ open, onCancel, onOk, title, username, us const payload = isEmail ? { email: username, password } : { phone: username, password }; - await onOk(payload as any); + await onOk(payload as LoginRequest); if (!hideSuccessMessage) { message.success('登录成功'); } onCancel(); - } catch (error) { + } catch { message.error('登录失败'); } }; diff --git a/src/components/MainContent.tsx b/src/components/MainContent.tsx index d8bb10b..3bc9d19 100644 --- a/src/components/MainContent.tsx +++ b/src/components/MainContent.tsx @@ -3,15 +3,16 @@ import { Layout, Typography } from 'antd'; import PeopleForm from './PeopleForm.tsx'; import InputDrawer from './InputDrawer.tsx'; import './MainContent.css'; +import type { People } from '../apis'; const { Content } = Layout; type Props = { inputOpen?: boolean; onCloseInput?: () => void; containerEl?: HTMLElement | null }; const MainContent: React.FC = ({ inputOpen = false, onCloseInput, containerEl }) => { - const [formData, setFormData] = React.useState(null); + const [formData, setFormData] = React.useState | null>(null); - const handleInputResult = (data: any) => { - setFormData(data); + const handleInputResult = (data: unknown) => { + setFormData(data as Partial); }; return ( @@ -24,7 +25,7 @@ const MainContent: React.FC = ({ inputOpen = false, onCloseInput, contain 点击右上角可以直接输入描述或上传图片 - + {/* 首页右侧输入抽屉,仅在顶栏点击后弹出;挂载到标题栏下方容器 */} diff --git a/src/components/PeopleForm.tsx b/src/components/PeopleForm.tsx index 383d645..33b2e1d 100644 --- a/src/components/PeopleForm.tsx +++ b/src/components/PeopleForm.tsx @@ -8,7 +8,7 @@ import { createPeople, type People } from '../apis'; const { TextArea } = Input; interface PeopleFormProps { - initialData?: any; + initialData?: Partial; // 编辑模式下由父组件控制提交,隐藏内部提交按钮 hideSubmitButton?: boolean; // 暴露 AntD Form 实例给父组件,用于在外部触发校验与取值 @@ -22,11 +22,7 @@ const PeopleForm: React.FC = ({ initialData, hideSubmitButton = // 当 initialData 变化时,自动填充表单 useEffect(() => { if (initialData) { - console.log('收到API返回数据,自动填充表单:', initialData); - - // 处理返回的数据,将其转换为表单需要的格式 - const formData: any = {}; - + const formData: Partial = {}; if (initialData.name) formData.name = initialData.name; if (initialData.contact) formData.contact = initialData.contact; if (initialData.cover) formData.cover = initialData.cover; @@ -35,13 +31,8 @@ const PeopleForm: React.FC = ({ initialData, hideSubmitButton = if (initialData.height) formData.height = initialData.height; if (initialData.marital_status) formData.marital_status = initialData.marital_status; if (initialData.match_requirement) formData.match_requirement = initialData.match_requirement; - if (initialData.introduction) formData.introduction = initialData.introduction; - - // 设置表单字段值 + if (initialData.introduction) formData.introduction = initialData.introduction as Record; form.setFieldsValue(formData); - - // 显示成功消息 - // message.success('已自动填充表单,请检查并确认信息'); } }, [initialData, form]); @@ -52,7 +43,18 @@ const PeopleForm: React.FC = ({ initialData, hideSubmitButton = // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const onFinish = async (values: any) => { + type FormValues = { + name: string; + contact?: string; + gender: string; + age: number; + height?: number; + marital_status?: string; + introduction?: Record; + match_requirement?: string; + cover?: string; + }; + const onFinish = async (values: FormValues) => { setLoading(true); try { @@ -81,16 +83,14 @@ const PeopleForm: React.FC = ({ initialData, hideSubmitButton = message.error(response.error_info || '提交失败,请重试'); } - } catch (error: any) { - console.error('提交失败:', error); - - // 根据错误类型显示不同的错误信息 - if (error.status === 422) { + } catch (e) { + const err = e as { status?: number; message?: string }; + if (err.status === 422) { message.error('表单数据格式有误,请检查输入内容'); - } else if (error.status >= 500) { + } else if ((err.status ?? 0) >= 500) { message.error('服务器错误,请稍后重试'); } else { - message.error(error.message || '提交失败,请重试'); + message.error(err.message || '提交失败,请重试'); } } finally { setLoading(false); diff --git a/src/components/RegisterModal.tsx b/src/components/RegisterModal.tsx index 1414436..81d8cc9 100644 --- a/src/components/RegisterModal.tsx +++ b/src/components/RegisterModal.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import { Modal, Form, Input, Button, message } from 'antd'; -import { useAuth } from '../contexts/AuthContext'; +import { useAuth } from '../contexts/useAuth'; import type { RegisterRequest } from '../apis/types'; interface Props { @@ -29,7 +29,7 @@ const RegisterModal: React.FC = ({ open, onCancel }) => { await sendCode({ target_type, target, scene: 'register' }); message.success('验证码已发送'); setStep('verify'); - } catch (error) { + } catch { message.error('发送验证码失败'); } }; @@ -40,7 +40,7 @@ const RegisterModal: React.FC = ({ open, onCancel }) => { await register({ ...registerPayload, ...values } as RegisterRequest); message.success('注册成功'); onCancel(); - } catch (error) { + } catch { message.error('注册失败'); } }; diff --git a/src/components/ResourceList.tsx b/src/components/ResourceList.tsx index 33a0b86..86113ea 100644 --- a/src/components/ResourceList.tsx +++ b/src/components/ResourceList.tsx @@ -22,7 +22,7 @@ export type Resource = Omit & { id: string }; // 统一转换 API 返回的人员列表为表格需要的结构 function transformPeoples(list: People[] = []): Resource[] { - return (list || []).map((person: any) => ({ + return (list || []).map((person: Partial) => ({ id: person.id || `person-${Date.now()}-${Math.random()}`, name: person.name || '未知', gender: person.gender || '其他/保密', @@ -72,7 +72,7 @@ async function fetchResources(): Promise { return transformed; - } catch (error: any) { + } catch (error: unknown) { console.error('获取人员列表失败:', error); message.error('获取人员列表失败,使用模拟数据'); @@ -463,124 +463,70 @@ async function fetchResources(): Promise { } // 数字范围筛选下拉 +function NumberRangeFilterDropdown({ setSelectedKeys, selectedKeys, confirm, clearFilters }: FilterDropdownProps) { + const [min, max] = String(selectedKeys?.[0] ?? ':').split(':'); + const [localMin, setLocalMin] = React.useState(min ? Number(min) : undefined); + const [localMax, setLocalMax] = React.useState(max ? Number(max) : undefined); + return ( +
+ + setLocalMin(v ?? undefined)} style={{ width: '100%' }} /> + setLocalMax(v ?? undefined)} style={{ width: '100%' }} /> + + + + + +
+ ); +} + function buildNumberRangeFilter(dataIndex: keyof Resource, label: string): ColumnType { return { title: label, dataIndex, - sorter: (a: Resource, b: Resource) => Number((a as any)[dataIndex] ?? 0) - Number((b as any)[dataIndex] ?? 0), - filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }: FilterDropdownProps) => { - const [min, max] = String(selectedKeys?.[0] ?? ':').split(':'); - const [localMin, setLocalMin] = React.useState(min ? Number(min) : undefined); - const [localMax, setLocalMax] = React.useState(max ? Number(max) : undefined); - return ( -
- - setLocalMin(v ?? undefined)} - style={{ width: '100%' }} - /> - setLocalMax(v ?? undefined)} - style={{ width: '100%' }} - /> - - - - - -
- ); + sorter: (a: Resource, b: Resource) => { + const av = a[dataIndex] as number | undefined; + const bv = b[dataIndex] as number | undefined; + return Number(av ?? 0) - Number(bv ?? 0); }, + filterDropdown: (props) => , onFilter: (filterValue: React.Key | boolean, record: Resource) => { const [minStr, maxStr] = String(filterValue).split(':'); const min = minStr ? Number(minStr) : undefined; const max = maxStr ? Number(maxStr) : undefined; - const val = Number((record as any)[dataIndex] ?? NaN); - if (Number.isNaN(val)) return false; - if (min !== undefined && val < min) return false; - if (max !== undefined && val > max) return false; + const val = record[dataIndex] as number | undefined; + if (val === undefined || Number.isNaN(Number(val))) return false; + if (min !== undefined && Number(val) < min) return false; + if (max !== undefined && Number(val) > max) return false; return true; }, } as ColumnType; } // 枚举筛选下拉(用于性别等枚举类列) +function EnumFilterDropdown({ setSelectedKeys, selectedKeys, confirm, clearFilters, options, label }: FilterDropdownProps & { options: Array<{ label: string; value: string }>; label: string }) { + const [val, setVal] = React.useState(selectedKeys && selectedKeys[0] ? String(selectedKeys[0]) : undefined); + return ( +
+ + setVal(v)} - options={options} - style={{ width: '100%' }} - /> - - - - - -
- ); - }, - onFilter: (filterValue: React.Key | boolean, record: Resource) => - String((record as any)[dataIndex]) === String(filterValue), + filterDropdown: (props) => , + onFilter: (filterValue: React.Key | boolean, record: Resource) => String(record[dataIndex] ?? '') === String(filterValue), render: (g: string) => { const color = g === '男' ? 'blue' : g === '女' ? 'magenta' : 'default'; return {g}; @@ -654,7 +600,7 @@ const ResourceList: React.FC = ({ inputOpen = false, onCloseInput, contai } else { message.error(res.error_info || '删除失败'); } - } catch (err: any) { + } catch { message.error('删除失败'); } finally { await reloadResources(); @@ -688,7 +634,7 @@ const ResourceList: React.FC = ({ inputOpen = false, onCloseInput, contai onFilter: (filterValue: React.Key | boolean, record: Resource) => String(record.name).includes(String(filterValue)), render: (text: string, record: Resource) => { // 图片图标逻辑 - const hasCover = record.cover && record.cover.trim() !== ''; + const hasCover = typeof record.cover === 'string' && record.cover.trim() !== ''; const pictureIcon = ( = ({ inputOpen = false, onCloseInput, contai cursor: hasCover ? 'pointer' : 'default' }} onClick={hasCover ? () => { - setCurrentImageUrl(record.cover); + setCurrentImageUrl(record.cover as string); setImageModalVisible(true); } : undefined} /> @@ -834,7 +780,7 @@ const ResourceList: React.FC = ({ inputOpen = false, onCloseInput, contai title: '操作', key: 'actions', width: 80, - render: (_: any, record: Resource) => ( + render: (_: unknown, record: Resource) => ( = ({ inputOpen = false, onCloseInput, contai } else { message.error(res.error_info || '更新失败'); } - } catch (err: any) { - if (err?.errorFields) { + } catch (err) { + const hasErrorFields = typeof err === 'object' && err !== null && 'errorFields' in err; + if (hasErrorFields) { message.error('请完善表单后再保存'); } else { message.error('更新失败'); @@ -1003,7 +950,7 @@ const ResourceList: React.FC = ({ inputOpen = false, onCloseInput, contai } }, } - : ({} as any) + : {} } pagination={{ ...pagination, @@ -1055,7 +1002,7 @@ const ResourceList: React.FC = ({ inputOpen = false, onCloseInput, contai )} {record.created_at && (
- {record.created_at ? '录入于: ' + formatDate(record.created_at) : ''} + {record.created_at ? '录入于: ' + formatDate(Number(record.created_at)) : ''}
)} @@ -1064,7 +1011,7 @@ const ResourceList: React.FC = ({ inputOpen = false, onCloseInput, contai 择偶要求 {record.match_requirement ? ( -
{record.match_requirement}
+
{String(record.match_requirement)}
) : (
暂无匹配要求
)} @@ -1078,7 +1025,7 @@ const ResourceList: React.FC = ({ inputOpen = false, onCloseInput, contai {record.comments && record.comments.remark ? ( @@ -1121,7 +1068,7 @@ const ResourceList: React.FC = ({ inputOpen = false, onCloseInput, contai {})} - onResult={(list: any) => { + onResult={(list: unknown) => { // setInputResult(list); const mapped = transformPeoples(Array.isArray(list) ? list : []); setData(mapped); @@ -1186,13 +1133,14 @@ const ResourceList: React.FC = ({ inputOpen = false, onCloseInput, contai } else { message.error(res.error_info || '更新失败'); } - } catch (err: any) { - if (err?.errorFields) { - message.error('请完善表单后再确认'); - } else { - message.error('更新失败'); + } catch (err) { + const hasErrorFields = typeof err === 'object' && err !== null && 'errorFields' in err; + if (hasErrorFields) { + message.error('请完善表单后再确认'); + } else { + message.error('更新失败'); + } } - } }} destroyOnHidden okText="确认" @@ -1225,7 +1173,7 @@ const ResourceList: React.FC = ({ inputOpen = false, onCloseInput, contai } else { message.error(res.error_info || '操作失败'); } - } catch (err) { + } catch { message.error('操作失败'); } }} diff --git a/src/components/SiderMenu.tsx b/src/components/SiderMenu.tsx index 6372716..f0fc434 100644 --- a/src/components/SiderMenu.tsx +++ b/src/components/SiderMenu.tsx @@ -1,6 +1,6 @@ import LoginModal from './LoginModal'; import RegisterModal from './RegisterModal'; -import { useAuth } from '../contexts/AuthContext'; +import { useAuth } from '../contexts/useAuth'; import React from 'react'; import { Layout, Menu, Grid, Drawer, Button } from 'antd'; import { FormOutlined, UnorderedListOutlined, MenuOutlined, CopyOutlined, UserOutlined, SettingOutlined } from '@ant-design/icons'; diff --git a/src/components/UserProfile.tsx b/src/components/UserProfile.tsx index 7efcf46..be734f5 100644 --- a/src/components/UserProfile.tsx +++ b/src/components/UserProfile.tsx @@ -2,7 +2,7 @@ import React from 'react' import { Form, Input, Button, message, Card, Space, Upload, Modal } from 'antd' import 'react-image-crop/dist/ReactCrop.css' import ReactCrop, { centerCrop, makeAspectCrop, type Crop } from 'react-image-crop' -import { useAuth } from '../contexts/AuthContext' +import { useAuth } from '../contexts/useAuth' import { updateMe, deleteUser, uploadAvatar, updatePhone, updateEmail } from '../apis' import { UserOutlined, EditOutlined } from '@ant-design/icons' import LoginModal from './LoginModal'; diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index 697e37f..94d9544 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -1,4 +1,4 @@ -import { createContext, useContext, useState, useEffect } from 'react'; +import { createContext, useState, useEffect } from 'react'; import type { ReactNode } from 'react'; import { login as apiLogin, register as apiRegister, sendCode as apiSendCode, getMe as apiGetMe, logout as apiLogout } from '../apis'; import type { LoginRequest, RegisterRequest, User, SendCodeRequest } from '../apis/types'; @@ -28,9 +28,10 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { if (response.data) { setUser(response.data); } - } catch (error) { + } catch { setToken(null); localStorage.removeItem('token'); + void 0; } }; validateSession(); @@ -48,7 +49,9 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { if (me.data) { setUser(me.data); } - } catch (e) { void e; } + } catch { + void 0; + } }; const logout = async () => { @@ -79,7 +82,9 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { if (me.data) { setUser(me.data); } - } catch (e) { void e; } + } catch { + void 0; + } }; const value = { @@ -97,10 +102,4 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => { return {children}; }; -export const useAuth = () => { - const context = useContext(AuthContext); - if (context === undefined) { - throw new Error('useAuth must be used within an AuthProvider'); - } - return context; -}; \ No newline at end of file +export default AuthContext; \ No newline at end of file diff --git a/src/contexts/useAuth.ts b/src/contexts/useAuth.ts new file mode 100644 index 0000000..56cbaf6 --- /dev/null +++ b/src/contexts/useAuth.ts @@ -0,0 +1,12 @@ +import { useContext } from 'react'; +import AuthContext from './AuthContext'; + +export const useAuth = () => { + const context = useContext(AuthContext); + if (context === undefined) { + throw new Error('useAuth must be used within an AuthProvider'); + } + return context; +}; + +export default useAuth; \ No newline at end of file