From 718e24978990c186e983bac7dae507e5cfb7f84e Mon Sep 17 00:00:00 2001 From: qiuchenfan <2035024011@qq.com> Date: Fri, 14 Nov 2025 18:43:12 +0800 Subject: [PATCH] 11141843 --- .../components/weather/WeatherSearchForm.tsx | 111 ++++++++++++++++++ 1114/app/services/weatherApi.ts | 85 ++++++++++++++ 2 files changed, 196 insertions(+) diff --git a/1114/app/components/weather/WeatherSearchForm.tsx b/1114/app/components/weather/WeatherSearchForm.tsx index e69de29..94f035b 100644 --- a/1114/app/components/weather/WeatherSearchForm.tsx +++ b/1114/app/components/weather/WeatherSearchForm.tsx @@ -0,0 +1,111 @@ +//天气搜索表单组件 +import { cn } from "@/components/lib/utils"; +import React ,{useState,type FormEvent,type ChangeEvent, use} from "react"; +import { Button } from "../ui/button"; +import { Input} from "../ui/input"; +import {useWeatherStore} from "@/store/weatherStore"; +import { AlertCircle, Loader, RefreshCw, Search } from "lucide-react"; + +//城市输入验证搜索刷新 +export function WeatherSearchForm() { +const {isLoading,currentWeather,setCurrentWeather,refreshWeather} = useWeatherStore(); +const [city, setCity] = useState(''); +const [inputError, setInputError] = useState(''); +//输入框内容 +const handleInputChange = (event: ChangeEvent) => { + setCity(event.target.value); + setInputError(''); +}; +//表单提交验证 +const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + const trimmed = city.trim(); + + if (!trimmed) { + setInputError('请输入城市名称'); + return; + } + if (trimmed.length < 2) { + setInputError('城市名称至少需要2个字符'); + return; + } + if (!/^[\u4e00-\u9fa5a-zA-Z\s-]+$/.test(trimmed)) { + setInputError('城市名称包含无效字符'); + return; + } + + await searchWeather(trimmed); + }; + + +return ( +
+
+
+
+ +
+ +
+ //输入错误提示 + {inputError && ( +
+ +

+ {inputError} +

+
+ )} + //搜索刷新按钮 +
+ + {currentWeather && ( + + ) + } +
+
+
+); +} \ No newline at end of file diff --git a/1114/app/services/weatherApi.ts b/1114/app/services/weatherApi.ts index e69de29..e40a53a 100644 --- a/1114/app/services/weatherApi.ts +++ b/1114/app/services/weatherApi.ts @@ -0,0 +1,85 @@ +import axios, {type AxiosInstance,AxiosError} from 'axios'; +import {WeatherData} from "@/store/weatherStore"; + +//配置常量 +const API_KEY = '5097cc3212ea9c460b01e2be936c94d5'; +const BASE_URL = 'http://api.weatherstack.com'; +//创建axios实例 统一设置请求头 +const apiClient: AxiosInstance = axios.create({ + baseURL: BASE_URL, + timeout: 10000, + headers: { + 'Content-Type': 'application/json', + }, +}); +//请求拦截器 +apiClient.interceptors.request.use( + //添加api——key到每个请求 + (config) => { + config.params = { + ...config.params, + access_key: API_KEY, + }; + //打印请求信息 + if (process.env.NODE_ENV === 'development'){ + console.log('API Request:', config.method ?.toUpperCase(), config.url); + } + return config; + }, + (error) => { + console.error('Request Error:' ,error); + return Promise.reject(error); + } +); +//响应拦截器 +apiClient.interceptors.response.use( + (response) => { + if (response.data && response.data.error){ + const error = response.data.error; + console.error('API Error:', error); + throw new Error(error.info || '请求失效'); + } + //打印响应 + if (process.env.NODE_ENV === 'development'){ + console.log('API Response:', response.config.url,response.data); + } + return response; + }, + (error :AxiosError) =>{ + //统一错误处理 + console.error('Response Error:',error.message); + if (error.response) { + const status = error.response.status; + switch (status) { + case 404: + return Promise.reject(new Error('未找到该城市,请检查城市名称')); + case 401: + return Promise.reject(new Error('API认证失败,请检查API')); + case 403: + return Promise.reject(new Error('访问被拒绝,请检查API订阅计划')); + case 429: + return Promise.reject(new Error('请求过于频繁,请稍后再试')); + default: + return Promise.reject(new Error('服务器错误,请稍后再试')); + } + }else if (error.request ){ + return Promise.reject(new Error('网络连接失败,请检查网络')); + }else{ + return Promise.reject(new Error('请求配置错误')); + } + } +); +//weather API + export class WeatherAPI{ + static async getCurrentWeather(city: string): Promise { + try { + const response = await apiClient.get('/current',{ + params:{query :city} , + }); + return response.data; + }catch (error){ + throw error; + } + } + } + export default apiClient; \ No newline at end of file