From 0ee9737136b71eb71ad44bf27dd6bcee8bb96bd8 Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 14 May 2025 20:25:43 +0800 Subject: [PATCH 1/2] #3 --- package.json | 3 +- src/utils/http.ts | 192 +++++++++++++++++++++++++++++++++++++++++++++ src/utils/index.ts | 2 + src/utils/sleep.ts | 7 ++ 4 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 src/utils/http.ts create mode 100644 src/utils/sleep.ts diff --git a/package.json b/package.json index 2cb532d..6c4fb3b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@codingapi/ui-framework", - "version": "0.0.25", + "version": "0.0.26", "description": "A UI Framework built with React and Typescript", "keywords": [ "ui", @@ -27,6 +27,7 @@ ], "dependencies": { "@types/node": "^22.15.2", + "axios": "^1.9.0", "base64-js": "^1.5.1", "jszip": "^3.10.1", "lodash": "^4.17.21", diff --git a/src/utils/http.ts b/src/utils/http.ts new file mode 100644 index 0000000..2478e2b --- /dev/null +++ b/src/utils/http.ts @@ -0,0 +1,192 @@ +import axios, {AxiosInstance} from "axios"; +import {sleep} from "./sleep"; +import {Base64Utils} from "./base64"; + +export interface MessageBox { + success: (msg: string) => void; + error: (msg: string) => void; +} + +export class HttpClient { + private readonly api: AxiosInstance; + private readonly messageBox: MessageBox; + + constructor(timeout: number, messageBox: MessageBox) { + this.messageBox = messageBox; + this.api = axios.create({ + timeout: timeout, + headers: { + "Content-Type": "application/json", + }, + }); + + this.addRequestInterceptors(); + this.addResponseInterceptors(); + } + + private addRequestInterceptors() { + this.api.interceptors.request.use((config: any) => { + const token = localStorage.getItem("token"); + if (token) { + config.headers = { + Authorization: `${token}`, + } as any; + } + return config; + }, (error: any) => { + return Promise.reject(error); + }); + } + + private addResponseInterceptors() { + this.api.interceptors.response.use(async (response: any) => { + const headers = response.headers; + const token = headers['authorization']; + + const state = response.status; + if (state === 200) { + if (token) { + console.log('reset token', token); + localStorage.setItem("token", token) + } + + if (response.data) { + const success = response.data.success; + if (!success) { + const errMessage = response.data.errMessage; + const errCode = response.data.errCode; + if ("token.expire" === errCode || "token.error" === errCode) { + this.messageBox.error('登录已过期,请退出再重新打开'); + await sleep(1500); + localStorage.clear(); + window.location.href = '/#login'; + } else { + if ("login.error" === errCode) { + return response; + } + this.messageBox.error(errMessage) + } + } + } else { + this.messageBox.error('抱歉,该账户无权限访问'); + } + } + return response; + }, + (error: any) => { + const response = error.response; + const state = response.data.status; + + if (state === 403) { + this.messageBox.error('抱歉,该账户无权限访问'); + return { + data: { + success: false, + } + } + } + return Promise.reject(error); + } + ) + } + + public get = async (url: string, params?: any) => { + try { + const response = await this.api.get(url, { + params + }); + return response.data; + } catch (e) { + return { + success: false, + } + } + } + + public post = async (url: string, data: any) => { + try { + const response = await this.api.post(url, data); + return response.data; + } catch (e) { + return { + success: false, + } + } + } + + + public page = async (url: string, params: any, sort: any, filter: any, match: { + key: string, + type: string + }[]) => { + const base64Match = Base64Utils.stringToBase64(JSON.stringify(match)); + const base64Sort = Base64Utils.stringToBase64(JSON.stringify(sort)); + const base64Filter = Base64Utils.stringToBase64(JSON.stringify(filter)); + + const response = await this.get(url, { + ...params, + sort: base64Sort, + filter: base64Filter, + params: base64Match, + }); + + if (response.success) { + const list = response.data.total > 0 ? response.data.list : []; + return { + data: list, + success: response.success, + total: response.data.total + }; + } else { + return { + data: [], + success: response.success, + total: 0 + } + } + } + + + public download = async (url: string, filename?: string) => { + try { + const token = localStorage.getItem("token"); + const response = await axios.get(url, { + responseType: 'blob', + headers: { + 'Authorization': token, + } + }); + const bytes = await response.data; + const blob = new Blob([bytes]); + const downloadUrl = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = downloadUrl; + a.download = filename || 'result.csv'; + a.click(); + } catch (e) { + console.log(e); + } + } + + public postDownload = async (url: string, data: any, filename?: string) => { + try { + const token = localStorage.getItem("token"); + const response = await axios.post(url, data, { + responseType: 'blob', + headers: { + 'Authorization': token, + } + }); + const bytes = await response.data; + const blob = new Blob([bytes]); + const downloadUrl = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = downloadUrl; + a.download = filename || 'result.csv'; + a.click(); + } catch (e) { + console.log(e); + } + } + +} diff --git a/src/utils/index.ts b/src/utils/index.ts index ffb2559..4524928 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,3 +1,5 @@ export * from './base64'; export * from './dynamicComponent'; export * from './role'; +export * from './sleep'; +export * from './http'; diff --git a/src/utils/sleep.ts b/src/utils/sleep.ts new file mode 100644 index 0000000..1bde575 --- /dev/null +++ b/src/utils/sleep.ts @@ -0,0 +1,7 @@ +export const sleep = async (time: number) => { + return new Promise((resolve:any) => { + setTimeout(() => { + resolve(); + }, time); + }) +} From 07fa01b84fcf4b171a9d6dba2d7a2a0ca8ba215b Mon Sep 17 00:00:00 2001 From: lorne <1991wangliang@gmail.com> Date: Wed, 14 May 2025 20:44:19 +0800 Subject: [PATCH 2/2] fix #3 --- README.md | 56 ++++++++++++++++++++++++++ playground/src/App.tsx | 2 + playground/src/components/HttpTest.tsx | 49 ++++++++++++++++++++++ src/utils/http.ts | 42 ++++++++++++++++--- 4 files changed, 144 insertions(+), 5 deletions(-) create mode 100644 playground/src/components/HttpTest.tsx diff --git a/README.md b/README.md index 33c339e..0a08991 100644 --- a/README.md +++ b/README.md @@ -306,6 +306,61 @@ const MicroComponentTest = () => { export default MicroComponentTest; ``` + +### 网络请求 +``` +import React from "react"; +import Space from "@/components/Space"; +//@ts-ignore +import {HttpClient,Response} from "@codingapi/ui-framework"; + +const httpClient = new HttpClient(10000,{ + success:(msg:string)=>{ + console.log('success',msg); + }, + error:(msg:string)=>{ + console.log('error',msg); + }, +}); + +const HttpTest = ()=>{ + + const [url, setUrl] = React.useState('/api/products'); + + const handlerGet = ()=>{ + httpClient.get(url).then((res:Response)=>{ + const json = JSON.stringify(res); + console.log(json); + alert(json); + }) + } + + return ( + <> +
+

Http Test

+
+ + url: + { + setUrl(e.target.value); + }}/> + + + + ) +} + +export default HttpTest; + +``` + 更多实例参考: https://github.com/codingapi/ui-framework/tree/main/playground ## 主要特性 @@ -314,6 +369,7 @@ export default MicroComponentTest; - 事件总线:用于组件间通信 - 访问控制:用于权限管理 - 微前端动态组件:支持动态加载和卸载组件 +- 网络请求:封装了 HttpClient,支持 GET、POST、PUT、DELETE 等请求方式 ## 开发 diff --git a/playground/src/App.tsx b/playground/src/App.tsx index e6c84a9..e54307b 100644 --- a/playground/src/App.tsx +++ b/playground/src/App.tsx @@ -4,6 +4,7 @@ import ComponentBusTest from "@/components/ComponentBusTest"; import EventBusTest from "@/components/EventBusTest"; import MicroComponentTest from "@/components/MicroComponentTest"; import Base64Test from "@/components/Base64Test"; +import HttpTest from "@/components/HttpTest"; const App = () => { @@ -18,6 +19,7 @@ const App = () => { + ); } diff --git a/playground/src/components/HttpTest.tsx b/playground/src/components/HttpTest.tsx new file mode 100644 index 0000000..4f3b429 --- /dev/null +++ b/playground/src/components/HttpTest.tsx @@ -0,0 +1,49 @@ +import React from "react"; +import Space from "@/components/Space"; +//@ts-ignore +import {HttpClient,Response} from "@codingapi/ui-framework"; + +const httpClient = new HttpClient(10000,{ + success:(msg:string)=>{ + console.log('success',msg); + }, + error:(msg:string)=>{ + console.log('error',msg); + }, +}); + +const HttpTest = ()=>{ + + const [url, setUrl] = React.useState('/api/products'); + + const handlerGet = ()=>{ + httpClient.get(url).then((res:Response)=>{ + const json = JSON.stringify(res); + console.log(json); + alert(json); + }) + } + + return ( + <> +
+

Http Test

+
+ + url: + { + setUrl(e.target.value); + }}/> + + + + ) +} + +export default HttpTest; diff --git a/src/utils/http.ts b/src/utils/http.ts index 2478e2b..1265a99 100644 --- a/src/utils/http.ts +++ b/src/utils/http.ts @@ -7,6 +7,14 @@ export interface MessageBox { error: (msg: string) => void; } +export type Response = { + success: boolean; + errCode?: string; + errMessage?: string; + data?: any; + total?: number; +} + export class HttpClient { private readonly api: AxiosInstance; private readonly messageBox: MessageBox; @@ -90,12 +98,36 @@ export class HttpClient { ) } - public get = async (url: string, params?: any) => { + public get = async (url: string, params?: any): Promise => { try { const response = await this.api.get(url, { params }); - return response.data; + return response.data as Response; + } catch (e) { + return { + success: false, + } + } + } + + public put = async (url: string, data: any): Promise => { + try { + const response = await this.api.put(url, data); + return response.data as Response; + } catch (e) { + return { + success: false, + } + } + } + + public delete = async (url: string, params?: any): Promise => { + try { + const response = await this.api.delete(url, { + params + }); + return response.data as Response; } catch (e) { return { success: false, @@ -103,10 +135,10 @@ export class HttpClient { } } - public post = async (url: string, data: any) => { + public post = async (url: string, data: any): Promise => { try { const response = await this.api.post(url, data); - return response.data; + return response.data as Response; } catch (e) { return { success: false, @@ -118,7 +150,7 @@ export class HttpClient { public page = async (url: string, params: any, sort: any, filter: any, match: { key: string, type: string - }[]) => { + }[]): Promise => { const base64Match = Base64Utils.stringToBase64(JSON.stringify(match)); const base64Sort = Base64Utils.stringToBase64(JSON.stringify(sort)); const base64Filter = Base64Utils.stringToBase64(JSON.stringify(filter));