当前位置: 首页 > news >正文

【Wit】pure-admin后台管理系统前端与FastAPI后端联调通信实例

在开始之前,先贴一下pure-admin的目录架构说明:

├── .github  # GitHub 配置文件
│   ├── ISSUE_TEMPLATE  # 问题提交参考模板
│   ├── workflows git  # 工作流
├── .husky  # 代码提交前校验配置文件
├── .vscode  # IDE 工具推荐配置文件
│   │   ├── extensions.json  # 一键安装平台推荐的 vscode 插件
│   │   ├── settings.json  # 设置扩展程序或 vscode 编辑器的一些属性
│   │   ├── vue3.0.code-snippets  # vue3.0 代码片段
│   │   └── vue3.2.code-snippets  # vue3.2 代码片段
│   │   └── vue3.3.code-snippets  # vue3.3 代码片段
├── build  # 构建工具
│   │   ├── cdn.ts  # 打包时采用 cdn 模式
│   │   ├── compress.ts  # 打包时启用 gzip 压缩或 brotli 压缩
│   │   ├── info.ts  # 输出打包信息(大小、用时)
│   │   ├── optimize.ts  # vite 依赖预构建配置项
│   │   ├── plugins.ts  # vite 相关插件存放处
│   │   ├── utils.ts  # 构建工具常用方法抽离
├── locales  # 国际化文件存放处
│   │   ├── en.yaml  # 英文配置
│   │   ├── zh-CN.yaml  # 中文配置
├── mock  # mock 模拟后台数据
│   │   ├── asyncRoutes.ts  # 模拟后台返回动态路由
│   │   ├── ...
├── node_modules  # 模块依赖
├── public  # 静态资源
│   │   ├── audio  # 音频文件
│   │   ├── html  # 静态 iframe 页面
│   │   ├── wasm  # wasm 文件以及胶水代码
│   │   ├── favicon.ico  # favicon
│   │   ├── logo.svg  # logo
│   │   ├── platform-config.json  # 全局配置文件(打包后修改也可生效)
├── src
│   ├── api  # 接口请求统一管理
│   ├── assets  # 字体、图片等静态资源
│   ├── components  # 自定义通用组件
│   │   ├── ReAnimateSelector  # [animate.css](https://animate.style/) 选择器组件
│   │   ├── ReAuth  # 按钮级别权限组件(根据路由meta中的auths字段进行判断)
│   │   ├── ReBarcode  # 条形码组件
│   │   ├── ReCol  # 封装 element-plus 的 el-col 组件
│   │   ├── ReCountTo  # 数字动画组件
│   │   ├── ReCropper  # 图片裁剪组件
│   │   ├── ReCropperPreview  # 图片裁剪预览组件
│   │   ├── ReDialog  # 基于 element-plus 中 el-dialog 组件开发的函数式弹框
│   │   ├── ReFlicker  # 圆点、方形闪烁动画组件
│   │   ├── ReFlop  # 时间翻牌组件
│   │   ├── ReFlowChart  # LogicFlow 流程图组件
│   │   ├── ReIcon  # 图标组件
│   │   ├── ReImageVerify  # 图形验证码组件
│   │   ├── ReMap  # 高德地图组件
│   │   ├── RePerms  # 按钮级别权限组件(根据登录接口返回的permissions字段进行判断)
│   │   ├── RePureTableBar  # 配合 `@pureadmin/table` 实现快速便捷的表格操作 https://github.com/pure-admin/pure-admin-table */
│   │   ├── ReQrcode  # 二维码组件
│   │   ├── ReSeamlessScroll  # 无缝滚动组件
│   │   ├── ReSegmented  # 分段控制器组件
│   │   ├── ReSelector  # 选择器组件
│   │   ├── ReSplitPane  # 切割面板组件
│   │   ├── ReText  # 支持 Tooltip 提示的文本省略组件
│   │   ├── ReTreeLine  # 树形连接线组件(基于element-plus)
│   │   ├── ReTypeit  # 打字机效果组件
│   │   ├── ReVxeTableBar  # 配合 vxe-table 实现快速便捷的表格操作
│   ├── config  # 获取平台动态全局配置
│   ├── directives  # 自定义指令
│   │   ├── auth  # 按钮级别权限指令(根据路由meta中的auths字段进行判断)
│   │   ├── copy  # 文本复制指令(默认双击复制)
│   │   ├── longpress  # 长按指令
│   │   ├── optimize  # 防抖、节流指令
│   │   ├── perms  # 按钮级别权限指令(根据登录接口返回的permissions字段进行判断)
│   │   ├── ripple  # 水波纹效果指令
│   ├── layout  # 主要页面布局
│   ├── plugins  # 处理一些库或插件,导出更方便的 api
│   ├── router  # 路由配置
│   ├── store  # pinia 状态管理
│   ├── style  # 全局样式
│   │   ├── dark.scss  # 暗黑模式样式适配文件
│   │   ├── element-plus.scss  # 全局覆盖 element-plus 样式文件
│   │   ├── reset.scss  # 全局重置样式文件
│   │   ├── sidebar.scss  # layout 布局样式文件
│   │   ├── tailwind.css  # tailwindcss 自定义样式配置文件
│   │   ├── ...
│   ├── utils  # 全局工具方法
│   │   ├── http  # 封装 axios 文件
│   │   ├── localforage  # 二次封装 localforage (https://localforage.docschina.org/) 支持设置过期时间,提供完整的类型提示
│   │   ├── progress  # 封装 nprogress
│   │   └── auth.ts  # 处理用户信息和 token 相关
│   │   └── chinaArea.ts  # 汉字转区域码
│   │   └── globalPolyfills.ts  # 解决项目可能因为安装某个依赖出现 `global is not defined` 报错
│   │   └── message.ts  # 消息提示函数
│   │   ├── mitt.ts  # 触发公共事件,类似 EventBus
│   │   ├── preventDefault.ts  # 阻止键盘F12、浏览器默认右键菜单、页面元素选中、图片默认可拖动的方法
│   │   ├── print.ts  # 打印函数
│   │   ├── propTypes.ts  # 二次封装 vue 的 propTypes
│   │   ├── responsive.ts  # 全局响应式 storage 配置
│   │   ├── sso.ts  # 前端单点登录逻辑处理
│   │   ├── tree.ts  # 树结构相关处理函数
│   ├── views  # 存放编写业务代码页面
│   ├── App.vue  # 入口页面
│   ├── main.ts  # 入口文件
├── types  # 全局 TS 类型配置
│   │   ├── directives.d.ts  # 全局自定义指令类型声明
│   │   ├── global-components.d.ts  # 自定义全局组件获得 Volar 提示(自定义的全局组件需要在这里声明下才能获得 Volar 类型提示哦)
│   │   ├── global.d.ts  # 全局类型声明,无需引入直接在 `.vue` 、`.ts` 、`.tsx` 文件使用即可获得类型提示
│   │   ├── index.d.ts  # 此文件跟同级目录的 global.d.ts 文件一样也是全局类型声明,只不过这里存放一些零散的全局类型,无需引入直接在 .vue 、.ts 、.tsx 文件使用即可获得类型提示
│   │   ├── router.d.ts  # 全局路由类型声明
│   │   ├── shims-tsx.d.ts  # 该文件是为了给 .tsx 文件提供类型支持,在编写时能正确识别语法
│   │   └── shims-vue.d.ts  # .vue、.scss 文件不是常规的文件类型,typescript 无法识别,所以我们需要通过下图的代码告诉 typescript 这些文件的类型,防止类型报错
├── .browserslistrc  # 配置目标浏览器的环境
├── .dockerignore  # 排除不需要上传到 docker 服务端的文件或目录
├── .editorconfig  # 编辑器读取文件格式及样式定义配置 https://editorconfig.org/
├── .env  # 全局环境变量配置(当 .env 文件与 .env.development、.env.production、.env.staging 这三个文件之一存在相同的配置 key 时,.env 优先级更低)
├── .env.development  # 开发环境变量配置
├── .env.production  # 生产环境变量配置
├── .env.staging  # 预发布环境变量配置
├── .gitattributes  # 自定义指定文件属性
├── .gitignore  # git 提交忽略文件
├── .gitpod.yml  # gitpod 部署配置
├── .lintstagedrc  # lint-staged 配置
├── .markdownlint.json  # markdown 格式检查配置
├── .npmrc  # npm 配置文件
├── .nvmrc  # 用于指定在使用 Node Version Manager(NVM)时要使用的特定 Node.js 版本
├── .prettierignore  # prettier 语法检查忽略文件
├── .prettierrc.js  # prettier 插件配置
├── .stylelintignore  # stylelint 语法检查忽略文件
├── CHANGELOG.en_US.md  # 版本更新日志(英文版)
├── CHANGELOG.md  # 版本更新日志(英文版)
├── CHANGELOG.zh_CN.md  # 版本更新日志(中文版)
├── Dockerfile  # 用来构建 docker 镜像
├── LICENSE  # 证书
├── README.en-US.md  # README(英文版)
├── README.md  # README
├── commitlint.config.js  # git 提交前检查配置
├── eslint.config.js  # eslint 语法检查配置
├── index.html  # html 主入口
├── package.json  # 依赖包管理以及命令配置
├── pnpm-lock.yaml  # 依赖包版本锁定文件
├── postcss.config.js  # postcss 插件配置
├── stylelint.config.js  # stylelint 配置
├── tailwind.config.ts  # tailwindcss 配置
├── tsconfig.json  # typescript 配置
└── vite.config.ts  # vite 配置

首先需要在vite.config.ts添加如下代码:

代码1

proxy: {"/api": {target: "http://127.0.0.1:8000",changeOrigin: true,rewrite: path => path.replace(/^\/api/, "")}},

代码1解析:

那么为什么要在vite.config.ts中添加api前缀,这是一个标准的前端开发代理配置方案。在Vite配置中添加 /api 前缀主要有以下几个作用:

开发环境代理 :在开发环境中,前端请求会添加 /api 前缀,然后通过Vite的代理配置转发到后端服务器( http://127.0.0.1:8000 )
解决跨域问题 :通过代理机制,前端可以在同一域名和端口下与后端通信,有效避免了浏览器的跨域限制
路径重写处理 :代理配置中的 rewrite 选项会将请求路径中的 /api 前缀移除,确保后端接收到的是正确的API路径
环境适配 :从 api/utils.ts 文件可以看到,项目使用了环境区分的URL策略:



在实际API调用中,项目使用这个前缀策略,例如在 routes.ts 中的API调用:

export const getAsyncRoutes = () => 
{return http.request<Result>("get", baseUrlApi("get-async-routes"));
};

其工作流程如下:
 开发环境下,前端请求URL会被设置为 /api/get-async-routes,Vite的代理配置捕获到 /api 开头的请求,代理将请求转发到 http://127.0.0.1:8000 ,并自动移除 /api 前缀,实际请求变为 http://127.0.0.1:8000/get-async-routes,这样既解决了跨域问题,又实现了前后端分离开发时的API请求转发。这种配置方式是前端工程化中的最佳实践,能够有效分离开发环境和生产环境的API请求处理策略。

接下来,在src/api目录下新建utils.ts文件,在其中写入一下代码:

代码2:

export const baseUrlApi = (url: string) =>process.env.NODE_ENV === "development"? `/api/${url}`: `http://127.0.0.1:8000/${url}`;

ps:"utils" 是 "utilities" 的缩写,中文通常翻译为工具函数、工具类或工具模块。在编程中,utils 的作用是封装通用的、可复用的功能代码,主要用途包括①:代码复用:将多个地方都会用到的功能(如日期格式化、数据验证、字符串处理等)抽离到 utils 中,避免重复编码。②简化主逻辑:把辅助性的复杂逻辑(如加密解密、数据转换)放到 utils 中,让业务代码更简洁清晰。③统一维护:工具函数集中管理,修改时只需改一处,所有引用的地方都会生效,便于维护。④分类组织:通常会按功能细分 utils(如 dateUtils、stringUtils),使项目结构更有序。

        代码2是一个用于处理 API 基础路径的工具函数,主要功能是根据当前环境(开发环境 / 生产环境)返回不同的 API 基础路径,以便前端请求能正确指向向后端服务。
代码解析:
首先定义与导出

export const baseUrlApi = (url: string) => { ... }


这是一个 TypeScript 箭头函数,命名为 baseUrlApi,接收一个字符串参数 url(表示 API 的具体路径部分),并通过 export 导出供其他模块使用。
接下来时环境判断

process.env.NODE_ENV === "development"


process.env.NODE_ENV 是 Node.js 环境中的一个变量,用于标识当前的运行环境:
development 表示开发环境(本地开发时)
production 表示生产环境(项目部署上线后)
路径拼接逻辑
若为开发环境:返回 /api/${url}
例如传入 url 为 /user/login,则前端显示接口结果为 /api/user/login
(通常配合本地开发服务器的代理配置,转发到后端开发服务)
若为生产环境:返回 https://www.wicms.cn/${url}
例如传入 url 为 /user/login,则结果为 https://www.wicms.cn/user/login
(直接指向生产环境的后端 API 域名)
作用与使用场景:
统一管理 API 路径:避免在每个接口请求中硬编码完整路径,便于维护。
环境隔离:开发时用本地 / 测试接口,上线后自动切换到生产接口,无需手动修改代码。

第三步,在具体的接口设置中引入代码2,以登录login为例,在src\api\user.ts文件通过以下代码引入代码2:

import { baseUrlApi } from "./utils";

在定义接口的地方调用baseUrlApi方法,如在定义的登录接口:

/** 登录 */
export const getLogin = (data?: object) => {return http.request<UserResult>("post", baseUrlApi("login"), { data });
};

至此,完成了前端的接口设置。接下来打开FastAPI后端,定义post路由返回对应结果,需要特别注意的是,前端显示的请求地址和真实的后端接口是不一样的,通过以上流程设置后,前端登录接口地址是:127.0.0.1:8848/api/login,然而实际请求的后端地址时:127.0.0.1:8000/login:

from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from fastapi.middleware.cors import CORSMiddleware
from routers import get_async_routesapp = FastAPI()# 配置CORS,允许前端跨域请求
app.add_middleware(CORSMiddleware,allow_origins=["http://localhost:8848"],  # 前端地址allow_credentials=True,allow_methods=["*"],allow_headers=["*"],
)# 定义请求数据模型(Pydantic)
class UserRequest(BaseModel):username: stremail: strage: int = None  # 可选字段@app.get("/")
async def read_root():return {"Hello": "World"}# 定义POST路由
@app.post("/api/user")
async def create_user(user: UserRequest):# 这里可以处理业务逻辑,比如存入数据库# 示例:返回处理后的用户数据return {"message": "用户创建成功","data": {"username": user.username,"email": user.email,"age": user.age if user.age else "未提供"}}# 定义POST路由
@app.post("/login")
async def login():# 使用JSONResponse返回格式化的JSON数据response_data = {"success": True,"data": {"avatar": "https://avatars.githubusercontent.com/u/44761321","username": "admin","nickname": "Fast","roles": ["admin"],"permissions": ["*:*:*"],"accessToken": "eyJhbGciOiJIUzUxMiJ9.admin","refreshToken": "eyJhbGciOiJIUzUxMiJ9.adminRefresh","expires": "2030/10/30 00:00:00"}}return JSONResponse(content=response_data, status_code=200)# 提供异步路由配置
@app.get("/get-async-routes")
async def async_routes():# 调用routers.py中的函数获取路由配置return JSONResponse(content=get_async_routes(), status_code=200)if __name__ == "__main__":import uvicornuvicorn.run(app, host="127.0.0.1", port=8000)

举一反三,只要明白了login的接口请求方法,其他的api请求是同样的道理,只是需要特别注意在需要使用的地方要引入src/api/ultis.ts配置。其他接口返回的数据类型参照mock里面定义的样式。

结合官方文档说明如下:

首先,要对接FastAPI后端,需要先将模拟数据接口Mock引用给注释掉,官方文档http请求 | Pure Admin 官方文档说明如下:

一、快速删除 mock

来到 build/plugins.ts (opens new window)文件,将 import { vitePluginFakeServer } from "vite-plugin-fake-server"; 注释,最后把 这里选中 (opens new window)55-60行的都注释即可

二、参考原来的Mock定义接口和数据及其使用方式

① 来到 mock (opens new window)文件存放目录,比如添加 login 接口,采用 post 请求,参考下面代码。

import { MockMethod } from "vite-plugin-mock";export default [{url: "/login",method: "post",response: ({ body }) => {return {success: true,data: {},};},},
] as MockMethod[];

② 上面我们添加了 login 接口,接着来到 api (opens new window)目录,比如添加 user.ts 文件,参考下面代码

import { http } from "@/utils/http";// 这里定义返回值类型,使接口拥有良好的类型推导
export type UserResult = {/** 是否请求成功 */success: boolean;data: {/** 用户名 */username: string;/** 当前登陆用户的角色 */roles: Array<string>;/** `token` */accessToken: string;/** 用于调用刷新`accessToken`的接口时所需的`token` */refreshToken: string;/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */expires: Date;};
};/** 登录接口 */
export const getLogin = (data?: object) => {return http.request<UserResult>("post", "/login", { data });
};

③ 上面导出了 getLogin 接口,接下来我们来到需要使用这个接口的地方导入,参考下面代码

点击查看

<script setup lang="ts">
import { getLogin } from "@/api/user";async function onLogin() {let result = await getLogin({ username: "admin", password: "admin123" });if (result.success) {console.log(result.data);}
}
</script><template><el-button @click="onLogin">点击登录</el-button>
</template>

您可以注意到我们在上面  步骤中定义了接口返回值类型 UserResult,在使用时就会获得良好的类型推导,如下图

img

三、与FastAPI后台联调通信

3.1 解决跨域问题

跨域本质是浏览器基于同源策略的一种安全手段。同源策略(Sameoriginpolicy),是一种约定,它是浏览器最核心也最基本的安全功能。当协议(protocol)、主机(host)、端口(port)其中一项不相同的时候就会产生跨域。跨域只产生在浏览器,这也是为什么您用 postman 请求接口时不会有跨域问题的原因

3.1.1解决办法

① 本地开发的话,我们来到 vite.config.ts (opens new window),参考下面代码配置本地跨域代理即可

proxy: {"/api": {// 这里填写后端地址target: "http://127.0.0.1:3000",changeOrigin: true,rewrite: path => path.replace(/^\/api/, "")}
}

② 配置好上面的跨域代理后,来到 src/api (opens new window)目录,这里我们先新建一个 utils.ts 文件用来配置环境,参考下面代码

注意:这里又分两种写法

第一种(也是最常用的一种,满足大多数场景,首选这种写法哦):比如后端使用 spring cloud 微服务架构,不同服务可能会部署在不同机器上,这时候前端必定产生跨域,推荐部署前端项目首选 nginx (opens new window),可以利用 nginx 的代理转发功能来解决跨域问题

utils.ts

export const baseUrlApi = (url: string) => `/api/${url}`;

第二种:前端打包直接丢给后端,放到后端项目里一起部署,协议、主机、端口都相同了,就没有跨域问题

utils.ts

export const baseUrlApi = (url: string) =>process.env.NODE_ENV === "development"? `/api/${url}`: `http://127.0.0.1:3000/${url}`;

③ 写完上面的 utils.ts 后,继续在 src/api (opens new window)目录,我们再建一个 user.ts 文件,导出这个接口,供页面调用,参考下面代码

user.ts

import { http } from "@/utils/http";
import { baseUrlApi } from "./utils";/** 登录 */
export const getLogin = (data?: object) => {return http.request<any>("post", baseUrlApi("login"), { data });
};

④ 上面都完成后,我们在需要调用该接口的地方调用即可,参考 src/store/modules/user.ts(opens new window)

多个后端地址如何联调

大致步骤和上面差不多,这里我们就简单写一下,具体对比上面的就行

① 在 vite.config.ts 写入下面代码

proxy: {// 第一个代理后端地址"/api": {target: "http://127.0.0.1:3000",changeOrigin: true,rewrite: path => path.replace(/^\/api/, "")},// 第二个代理后端地址"/otherApi": {target: "http://127.0.0.1:3290",changeOrigin: true,rewrite: path => path.replace(/^\/otherApi/, "")},// websocket地址(知识点:wss只能在https安全协议下使用)// "/wsApi": {//   target: "ws://localhost:3000",//   ws: true,// },// ...依此类推,有几个后端地址就写几个后端地址
}

② 在 utils.ts 里我们采用第一种写法,参考下面代码

utils.ts

// 第一个代理后端地址
export const baseUrlApi = (url: string) => `/api/${url}`;
// 第二个代理后端地址
export const baseUrlOtherApi = (url: string) => `/otherApi/${url}`;

③ 第一个代理后端地址上面建的是 user.ts 文件,那么第二个代理后端地址我们就建个 other.ts 文件吧,参考下面代码

other.ts

import { http } from "@/utils/http";
import { baseUrlOtherApi } from "./utils";/** 该接口采用 http://127.0.0.1:3290 后端地址 */
export const getOther = (data?: object) => {return http.request<any>("get", baseUrlOtherApi("other"), { data });
};

最后,需要在哪里调用,引入使用即可

如何声明接口返回值类型

可以看到上面的写法 http.request 后面跟的都是 any,这很不友好,参考下面友好类型写法即可

import { http } from "@/utils/http";
import { baseUrlApi } from "./utils";// 定义 login 接口返回值类型为 UserResult
export type UserResult = {/** 是否请求成功 */success: boolean;data: {/** 用户名 */username: string;/** 当前登陆用户的角色 */roles: Array<string>;/** `token` */accessToken: string;/** 用于调用刷新`accessToken`的接口时所需的`token` */refreshToken: string;/** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */expires: Date;};
};/** 登录 */
export const getLogin = (data?: object) => {return http.request<UserResult>("post", baseUrlApi("login"), { data });
};
平台封装的请求可以设置额外的 axios 配置吗

当然可以。点击查看这里,可以看到自定义 axios 配置中 axiosConfig 作为最后一个参数

import { http } from "@/utils/http";
import { baseUrlApi } from "./utils";export const testRequest = (data?: object) => {return http.request<any>("post",baseUrlApi("login"),{ data },// 自定义的axios配置在下面对象填写即可{headers: {"Content-Type": "application/x-www-form-urlencoded",},});
};
#JWT Token(内置无感刷新 token 解决方案)

实现原理:后端返回两个 token(一个用来请求,一个用来刷新)和过期时间,前端将其进行本地存储,每当接口请求时,把本地存储的过期时间与本地当前时间对比,如果 token 过期就把当前请求暂存,然后去请求刷新 token 接口,获取到新 token 后,再 触发 (opens new window)暂存的请求

#信息存储

具体看 src/utils/auth (opens new window)文件,里面有很详细的备注

#细节处理

① 不需要携带 token 的接口我们设置了 请求白名单(opens new window)

② 当 token 过期后,平台会暂存请求,直到拿到新 token 才会请求,避免了当页面有多个请求会重复刷新 token 的问题。核心代码有三处:一 (opens new window)二 (opens new window)三(opens new window)

生产环境 Mock 可以和真实接口共存吗?

当然可以共存,不过可能会遇到 Mock 干扰真实接口的问题。如果您在开发环境接口没问题,但是到生产环境出问题了,在排除后端问题前提下可以尝试 删除 Mock

除后端问题前提下可以尝试 删除 Mock

参考:Pure Admin 官方文档


文章转载自:

http://UwOz0Lf5.sgfpn.cn
http://jYvcRS0A.sgfpn.cn
http://f1ORg4TA.sgfpn.cn
http://ETqUsroB.sgfpn.cn
http://xEpuQQLp.sgfpn.cn
http://2zrQ2tpy.sgfpn.cn
http://36KS5Iex.sgfpn.cn
http://j3KuDDIE.sgfpn.cn
http://UvJYrVuu.sgfpn.cn
http://hQtbEpaz.sgfpn.cn
http://qamrcRXN.sgfpn.cn
http://g7gpJ2up.sgfpn.cn
http://B4KDEnHQ.sgfpn.cn
http://gKgn4NFy.sgfpn.cn
http://ReIAHhm3.sgfpn.cn
http://YFESKfAy.sgfpn.cn
http://v0WuH5Y8.sgfpn.cn
http://hSvksbRw.sgfpn.cn
http://qp2uto7c.sgfpn.cn
http://Ivj7M9XT.sgfpn.cn
http://A77vjd13.sgfpn.cn
http://7wmZ5por.sgfpn.cn
http://SsftZlzp.sgfpn.cn
http://fIz8BYHV.sgfpn.cn
http://k0U9PVsW.sgfpn.cn
http://6ZnPUtC3.sgfpn.cn
http://LbSOZnhJ.sgfpn.cn
http://n5D2FP00.sgfpn.cn
http://hTCkcXJT.sgfpn.cn
http://13fMvgG6.sgfpn.cn
http://www.dtcms.com/a/382746.html

相关文章:

  • godot+c#使用godot-sqlite连接数据库
  • 【pure-admin】pureadmin的登录对接后端
  • tcpump | 深入探索网络抓包工具
  • scikit-learn 分层聚类算法详解
  • Kafka面试精讲 Day 18:磁盘IO与网络优化
  • javaweb CSS
  • css`min()` 、`max()`、 `clamp()`
  • 超越平面交互:SLAM技术如何驱动MR迈向空间计算时代?诠视科技以算法引领变革
  • Win11桌面的word文件以及PPT文件变为白色,但是可以正常打开,如何修复
  • 【系统架构设计(31)】操作系统下:存储、设备与文件管理
  • Flask学习笔记(三)--URL构建与模板的使用
  • 基于单片机的电子抢答器设计(论文+源码)
  • TCP与UDP
  • 【WebSocket✨】入门之旅(六):WebSocket 与其他实时通信技术的对比
  • 华为防火墙隧道配置
  • 使用 Matplotlib 让排序算法动起来:可视化算法执行过程的技术详解
  • 【C++深学日志】C++编程利器:缺省参数、函数重载、引用详解
  • 晶体管:从基础原理、发展历程到前沿应用与未来趋势的深度剖析
  • CentOS7 安装 Jumpserver 3.10.15
  • jquery 文件上传 (CVE-2018-9207)漏洞复现
  • QML Charts组件之折线图的鼠标交互
  • 工程机械健康管理物联网系统:AIoT技术赋能装备全生命周期智能运维​
  • 第5课:上下文管理与状态持久化
  • SpringBootCodeGenerator使用JSqlParser解析DDL CREATE SQL 语句
  • 【WebSocket✨】入门之旅(五):WebSocket 的安全性
  • PHP使用echarts制作一个很漂亮的天气预报网站(曲线图+实况+未来一周预报)
  • 数据库造神计划第九天---增删改查(CRUD)(5)
  • 简单的折叠cell
  • 贪心算法在边缘计算卸载问题中的应用
  • pyAutoGUI 模块主要功能介绍-(2)键盘功能