tauri2+typescript+vue+vite+leaflet等的简单联合使用(一)
项目目标
主要的目的是学习tauri。
流程
1、搭建项目
2、简单的在项目使用leaflet
3、打包
准备项目
环境准备
废话不多说,直接开始
需要有准备能运行Rust的环境和Node,对于Rust可以参考下面这位大佬的文章,Node不必细说。
Rust 和 Cargo 安装指南-CSDN博客https://blog.csdn.net/qq_44154915/article/details/139365116
建立项目
进入tauri2
官网https://tauri.app/start/
笔者将使用pnpm建立项目,项目名称为ttvvl
如下
使用VSCode或者WebStorm打开项目
安装依赖
pnpm install
运行
pnpm tauri dev
然后就在compiling
这笔者感到疑惑,可能是第一次运行,搞了许久
运行结果
没有问题。
看一下文件夹的属性
6个G,有点大。
如果以
pnpm run dev
点击按钮,会出现错误
观察上面的代码,我们可以发现,这个是invoke好像是个方法。第一个参数是个字符串greet
第二个参数是个对象,属性是name。
同时找到src-tauri/src/lib.rs中的代码,如下
fn 是Rust的关键字,相当与定义了一个函数,函数名叫greet,参数name
format!是个格式化字符串的函数
其他不是很懂,但我们可以把这段代码给deepseek,问一问
解答如下
从这里我们可以得到关键信息——被这个宏标记的函数能被前端调用。
因为Rust函数的函数名叫greet,而invoke的第一个参数是字符串greet,我们可以很容易猜测
二者必然有关系,我们可以检验一下
修改函数名为greets,运行
发现报错了
我们可以看到下面还有一个函数
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {tauri::Builder::default().plugin(tauri_plugin_opener::init()).invoke_handler(tauri::generate_handler![greet]).run(tauri::generate_context!()).expect("error while running tauri application");
}
可以问一问deepseek
原来下面那个函数是入口函数,invoke_handler是注册器,因此,把中括号里面的greet改成greets
应该可以运行了,(每次都要编译,有点慢)。
如果点击按钮,下面应该不会出现字
事实确实如此。
那么推断很有可能是正确的,那么我们把字符串greet改成greets,就可以出现字了
事实确实如此,笔者明白了。
再次观察,Rust函数greets返回值是String,
而返回格式化后的字符串,对于App.vue中的TypeScript代码,打印出greetMsg.value的值
在运行后的的tauri中,可以打开开发者工具,打印结果如下。
既然如此,感觉明白了,invoke作为Rust与TypeScript交互的关键函数。
安装leaflet有关依赖
pnpm install leaflet leaflet-geoman-free
因为使用typescipt,还需要安装类型定义包
pnpm install --save @types/leaflet.pm @types/leaflet
显示地图
这也是很麻烦的事情。
安装vue-router
pnpm install vue-router
新建一些目录和文件,如下
在router目录的index.ts中
import {createRouter, createWebHashHistory, Router, RouteRecordRaw, RouterOptions} from "vue-router";
const routes:RouteRecordRaw[]=[{path:"/",redirect:"/map",children:[{path:"map",component:()=>import("../views/Map.vue")}]}
]
const options:RouterOptions={history:createWebHashHistory(),routes
}
const router:Router=createRouter(options)
export default router
暂时先这么写
main.ts的内容
import { createApp } from "vue";
import App from "@/App.vue";
import router from "@/router";
import * as L from "leaflet";
import "leaflet/dist/leaflet.css";
import "@geoman-io/leaflet-geoman-free";
import "@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css";
const app=createApp(App);
app.use(router);
app.config.globalProperties.$L = L;app.mount("#app");
将L作为Vue的全局属性,并且引入插件leaflet-geoman-free及相关样式
关于@符号,不多解释。
在constant/data.ts中
export const MAP_URL= "https://tile-a.openstreetmap.fr/hot/{z}/{x}/{y}.png";
这个是地图瓦片的URL。通过它就能访问地图。
在utils/Map.ts中
import {MAP_URL} from "@/constant/data.ts";
import L from 'leaflet';
const GetMap = (control: any=null, //控件language:any = 'zh' //语言
): L.Map => {const options: L.MapOptions = {center: [30.6667, 104.0667], //中心点minZoom: 0,maxZoom: 30,zoom: 10,zoomControl: true,doubleClickZoom: true,attributionControl: true,dragging: true,boxZoom: true,scrollWheelZoom: true,zoomSnap: 0.5,};const map = L.map('map', options);L.tileLayer(MAP_URL, {attribution: '',}).addTo(map);map.pm.addControls(control); map.pm.setLang(language);return map;
};
export default GetMap;
如果不写
import L from 'leaflet'
Vue: 'L' refers to a UMD global, but the current file is a module. Consider adding an import instead.
在views/Map.vue中
<script setup lang="ts">
import GetMap from "@/utils/Map.ts";
import { onMounted } from 'vue';
let map: any = null
onMounted(() => {map = GetMap();map.on('click', (e: any) => {console.log(e);});
})
</script><template>
<div id="map"></div>
</template><style scoped>
#map {height: 100vh;width: 200vh;
}
</style>
运行,结果如下
地图定位到成都市。
显示控件
可以查看leaflet-geoman-free文档
Introduction | Documentation for Leaflet-Geomanhttps://geoman.io/docs/leaflet在constant/data.ts中
export const MAP_CONTROL={
}
可以写一些设置,但有默认选项,就不改了。
在views/Map.vue中导入MAP_CONTROL,作为GetMap的参数
map = GetMap(MAP_CONTROL);
,使用控件,运行结果如下
搞点小操作1——计算两点之间的距离
操作过程
点两个点,通过invoke函数发送经纬度到rust函数,返回结果
计算距离函数
使用Haversine 公式
haversine公式计算两经纬度点距离-CSDN博客https://blog.csdn.net/spatial_coder/article/details/116605509使用Rust实现,src-tauri/src/lib.rs的代码如下
const EARTH_RADIUS_KM: f64 = 6371.0; // 地球半径,单位:公里
#[tauri::command]
// 计算两个经纬度点之间的距离(单位:公里)
fn haversine_distance(start:[f64;2],end:[f64;2]) -> f64 {// 将经纬度从度数转换为弧度let lat1_rad = start[0].to_radians();let lon1_rad = start[1].to_radians();let lat2_rad = end[0].to_radians();let lon2_rad = end[1].to_radians();// 经纬度差值let dlat = lat2_rad - lat1_rad;let dlon = lon2_rad - lon1_rad;// Haversine 公式let a = (dlat / 2.0).sin().powi(2) + lat1_rad.cos() * lat2_rad.cos() * (dlon / 2.0).sin().powi(2);let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());// 计算距离EARTH_RADIUS_KM * c
}#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {tauri::Builder::default().plugin(tauri_plugin_opener::init()).invoke_handler(tauri::generate_handler![haversine_distance]).run(tauri::generate_context!()).expect("error while running tauri application");
}
以公里作为单位
对两个点marker的思考
点了两个点marker,我们通过leaflet和有关插件的某些事件可以获得这两个点的属性,
我们可以修改这些属性。
那应该然后修改?
可以尝试使用tauri的多窗口。
marker位于父窗口,发送信号到子窗口,在子窗口进行修改,返回父窗口。
父子窗口以及发送信号
关于窗口的配置
src-tauri/capabilities/default.json
{"$schema": "../gen/schemas/desktop-schema.json","identifier": "default","description": "Capability for the main window","windows": ["main","marker-name"],"permissions": ["core:default","opener:default","core:window:allow-hide","core:window:allow-show"]
}
在子窗口添加一个路由
src/router/index.ts
{path:"/name",component:()=>import("../views/name.vue")}
src-tauri/tauri.conf.json
"windows": [{"title": "ttvvl","label": "main","width": 800,"height": 600},{"title": "点位窗口","width": 400,"height": 300,"label": "marker-name","resizable": true,"parent": "main","visible": false,"url":"#/name","decorations": false,"center": true}],
参数的具体含义,可以参考官网或者deekssek
{"windows": [{"label": "main", // 窗口的唯一标识符"title": "My Tauri App", // 窗口标题"width": 800, // 窗口宽度"height": 600, // 窗口高度"x": null, // 窗口初始水平位置(null 表示由系统决定)"y": null, // 窗口初始垂直位置(null 表示由系统决定)"minWidth": null, // 窗口最小宽度"minHeight": null, // 窗口最小高度"maxWidth": null, // 窗口最大宽度"maxHeight": null, // 窗口最大高度"resizable": true, // 是否允许调整窗口大小"fullscreen": false, // 是否全屏"focus": true, // 窗口是否在创建时获得焦点"visible": true, // 窗口是否可见"decorations": true, // 是否显示窗口装饰(标题栏、边框等)"alwaysOnTop": false, // 窗口是否始终置顶"maximized": false, // 窗口是否最大化"transparent": false, // 窗口背景是否透明"center": true, // 窗口是否居中"theme": "system", // 窗口主题("system"、"light"、"dark")"url": "index.html", // 窗口加载的页面路径"fileDropEnabled": true, // 是否启用文件拖放功能"skipTaskbar": false, // 是否在任务栏中显示窗口"shadow": true, // 是否显示窗口阴影"acceptFirstMouse": false, // 是否接受首次鼠标点击事件"tabbingIdentifier": null, // macOS 标签页标识符"titleBarStyle": "visible", // macOS 标题栏样式("visible"、"transparent"、"overlay")"hiddenTitle": false // 是否隐藏标题栏标题}]
}
父窗口
src/views/Map.vue
<script setup lang="ts">
//@ts-nocheck
import GetMap from "@/utils/Map.ts";
import {MAP_CONTROL} from "@/constant/data.ts";
import {invoke} from "@tauri-apps/api/core";
import {emit,listen} from "@tauri-apps/api/event";
import {onMounted, ref} from 'vue';
import WindowManager from '@/utils/window';const windowManager = new WindowManager();
let startName=ref<String>('起点');
let endName=ref<String>('终点');
let markerWindow,mainWindow,markerIndex=null;
let map: any = null
let markerList: L.Marker[] = [];
let distance=ref<number>(0);listen('message-to-main-window', (event)=>{ //监听消息let payload:any=event.payloadif(payload.close){windowManager.closeWindow('marker-name')mainWindow.show()}if(markerIndex===null){return}if(markerIndex===0){startName.value='起点'+payload.tooltipContent}else{endName.value='终点'+payload.tooltipContent}markerList[markerIndex].bindTooltip(payload.tooltipContent)
});
async function sendMsg(tooltipContent:String,nowClickMarkerId:Number) { //发送消息markerWindow=await windowManager.getWindowByLabel('marker-name') //子窗口mainWindow=await windowManager.getWindowByLabel('main') //父窗口if(markerWindow) {mainWindow.hide()markerWindow.show();markerIndex=markerList.findIndex((marker:L.Marker)=>marker._leaflet_id===nowClickMarkerId)emit('message-to-second-window', { //发送消息 参数lat: markerList[0].getLatLng().lat,lng: markerList[0].getLatLng().lng,markerIndex:markerIndex,tooltipContent:tooltipContent})}
}async function get_distance(){let start=markerList[0].getLatLng(); //开始点let end=markerList[1].getLatLng(); //终点let startArray=[start.lat,start.lng];let endArray=[end.lat,end.lng];distance.value = await invoke('haversine_distance', { //发送给rust tauristart: startArray,end: endArray});
}
onMounted(() => {// openWindow()map = GetMap(MAP_CONTROL);map.on('pm:create', (e: any) => { // map create事件let marker:L.Marker = e.marker;marker.on('click',(e)=>{ // marker的点击事件let nowClickMarkerId=e.target._leaflet_id // 获取marker的idsendMsg(e.target.getTooltip(),nowClickMarkerId) // 发送消息})markerList.push(marker);let length = markerList.length;if(length==2){ // 起始点 和终点get_distance() //获取距离}else if(length>2){markerList[0].remove(); //移除第一个点markerList.shift();get_distance() // 获取距离}})})
</script><template>
<div id="map"><div id="distance-text" v-if="markerList.length>=2">从{{startName}}到{{endName}}的距离为</div><div><button type="button" id="show" >{{distance}}</button></div><div id="unit">公里</div></div>
</template><style scoped>
#map {margin: 0 auto;width: 100%;height: 900px;
}
#show {max-width: 200px;max-height: 50px;padding: 10px;background-color: blue;color: white;text-align: center;position: absolute;top: 100px;right: 10px;z-index: 999;border-radius: 20px;
}
#distance-text {max-width: 200px;max-height: 50px;padding: 10px;background-color: #af8433;color: white;text-align: center;position: absolute;top: 10px;right: 10px;z-index: 999;border-radius: 20px;
}
#unit {max-width: 200px;max-height: 50px;padding: 10px;background-color: #af8433;color: white;text-align: center;position: absolute;top: 150px;right: 10px;z-index: 999;border-radius: 20px;
}</style>
子窗口
src/views/Name.vue
<script setup>
import {listen,emit} from "@tauri-apps/api/event";
import {onMounted, ref} from "vue";
let lat = ref(0);
let lng = ref(0);
let markerIndex = ref(0); // 0 替换成起点 1 替换成终点
let inputValue = ref('');
let oldTooltip=nullconst handleClicked = () => {emit('message-to-main-window', {tooltipContent:inputValue.value,close: true,});
}
const handleCancel = () => {emit('message-to-main-window', {tooltipContent:oldTooltip,close: true,});
}
onMounted(() => {listen('message-to-second-window', (event) => {let payload = event.payload;lat.value = payload.lat;lng.value = payload.lng;markerIndex.value = payload.markerIndex;inputValue.value=payload.tooltipContent;oldTooltip=payload.tooltipContent});}
)</script>
<template><div data-tauri-drag-region class="titlebar" ><h1>{{markerIndex===0?'起点':'终点'}}</h1><div v-if="lat>0">纬度:{{lat }}</div><div v-if="lng>0">经度:{{lng }}</div><div>名称<br/><input v-model="inputValue"></input></div><br/><button type="button" @click="handleClicked" class="sure">确定</button><button type="button" @click="handleCancel" class="cancel">取消</button></div>
</template>
<style >
.sure{padding: 10px ;margin-right: 10vw;background-color: #5174ff;
}
.cancel{padding: 10px ;margin-right: 10vw;background-color: #ffb43e;}</style>
窗口管理
src/utils/window.ts
import { getAllWebviewWindows } from '@tauri-apps/api/webviewWindow';export class WindowManager {async getWindowByLabel(label: string) {const windows = await getAllWebviewWindows();return windows.find((win) => win.label === label);}async closeWindow(label: string) {const window = await this.getWindowByLabel(label);if (window) {window.hide();}}
}export default WindowManager;
运行结果
运行结果倒是没问题,代码写得不行,算了。不管那些。
打包
本地打包
打包成exe命令
pnpm tauri build
因为笔者不是第一次打包,如果是第一次打包,需要下载一些东西,笔者就不展示了,可参考下面这位大佬的过程。
从零开始的 Tauri 开发 & 打包成 exe 【Windows 平台】_tauri 打包-CSDN博客https://blog.csdn.net/u010263423/article/details/136006546对于leaflet打包,会出现一个bug,解决过程可看这篇文章
解决Vue+Vite打包后Leaflet的marker图标不显示的问题_leaflet l.marker 没有-CSDN博客https://blog.csdn.net/qq_63401240/article/details/139972362?spm=1001.2014.3001.5502总之。打包结果
打包运行结果
可以。
上传到github并打包
在github上通过工作流打包
工作流代码参考下面这位大佬
tauri使用github的action自动发布release,让别人也可以看到下载链接_tauri github action-CSDN博客https://blog.csdn.net/weixin_44786530/article/details/140904091在大佬的基础上做出小的修改
修改了工件的版本,其他改动不大
运行结果如下
发现ubuntu出错了。笔者没有深究。
笔者再次打包,只选择了window系统,结果如下
项目地址
qe-present/ttvvlhttps://github.com/qe-present/ttvvl
总结
简单地使用了tauri,使用了信号通信,多窗口,打包。
相关文章:

tauri2+typescript+vue+vite+leaflet等的简单联合使用(一)
项目目标 主要的目的是学习tauri。 流程 1、搭建项目 2、简单的在项目使用leaflet 3、打包 准备项目 环境准备 废话不多说,直接开始 需要有准备能运行Rust的环境和Node,对于Rust可以参考下面这位大佬的文章,Node不必细说。 Rust 和…...
【流程图】在 .NET (WPF 或 WinForms) 中实现流程图中的连线算法
在 .NET (WPF 或 WinForms) 中实现流程图中的连线算法,通常涉及 图形绘制 和 路径计算。常见的连线方式包括 直线、折线 和 贝塞尔曲线。以下是几种方法的介绍和示例代码。 1. 直线连接(最简单) 适用场景: 两个节点之间没有障碍…...

IDEA集成DeepSeek,通过离线安装解决无法安装Proxy AI插件问题
文章目录 引言一、安装Proxy AI1.1 在线安装Proxy AI1.2 离线安装Proxy AI 二、Proxy AI中配置DeepSeek2.1 配置本地部署的DeepSeek(Ollama方式)2.2 通过第三方服务商提供的API进行配置 三、效果测试 引言 许多开发者尝试通过安装Proxy AI等插件将AI能力…...
【流行病学】Melodi-Presto因果关联工具
title: “[流行病学] Melodi Presto因果关联工具” date: 2022-12-08 lastmod: 2022-12-08 draft: false tags: [“流行病学”,“因果关联工具”] toc: true autoCollapseToc: true 阅读介绍 Melodi-Presto: A fast and agile tool to explore semantic triples derived from …...

详细分析KeepAlive的基本知识 并缓存路由(附Demo)
目录 前言1. 基本知识2. Demo2.1 基本2.2 拓展2.3 终极 3. 实战 前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 基本知识推荐阅读:KeepAlive知识点 从实战中学习,源自实战中vue路由的…...

【Go】Go viper 配置模块
1. 配置相关概念 在项目开发过程中,一旦涉及到与第三方中间件打交道就不可避免的需要填写一些配置信息,例如 MySQL 的连接信息、Redis 的连接信息。如果这些配置都采用硬编码的方式无疑是一种不优雅的做法,有以下缺陷: 不同环境…...

zabbix“专家坐诊”第277期问答
在线答疑:乐维社区 问题一 Q:这个怎么解决呢? A:缺少这个依赖。 Q:就一直装不上。 A:装 zabbix-agent2-7.0.0-releasel.el7.x86 64 需要前面提示的那个依赖才可以装。 问题二 Q:大佬,如果agen…...
大模型工程师学习日记(十一):FAISS 高效相似度搜索和密集向量聚类的库
Facebook AI Similarity Search (Faiss /Fez/) 是一个用于高效相似度搜索和密集向量聚类的库。它包含了在任意大小的向量集合中进行搜索的算法,甚至可以处理可能无法完全放入内存的向量集合。它还包含用于评估和参数调整的支持代码。 Faiss 官方文档:We…...
python学习第三天
条件判断 条件判断使用if、elif和else关键字。它们用于根据条件执行不同的代码块。 # 条件判断 age 18 if age < 18:print("你还是个孩子!") elif age 18:print("永远十八岁!") else:print("你还年轻!")…...
深入解析 Svelte:下一代前端框架的革命
深入解析 Svelte:下一代前端框架的革命 1. Svelte 简介 Svelte 是一款前端框架,与 React、Vue 等传统框架不同,它采用 编译时(Compile-time) 方式来优化前端应用。它不像 React 或 Vue 依赖虚拟 DOM,而是…...

C++20 中位移位运算符的统一行为:深入解析与实践指南
文章目录 1. 位移位运算符的基础1.1 左移运算符(<<)1.2 右移运算符(>>) 2. C20 对位移位运算符的统一2.1 移位数量超出操作数位宽2.2 负数移位 3. 实践中的注意事项4. 示例代码5. 总结 在 C 的发展历程中,…...

Linux——基本指令
我们今天学习Linux最基础的指令 ls 指令 语法: ls [选项] [⽬录或⽂件] 功能:对于⽬录,该命令列出该⽬录下的所有⼦⽬录与⽂件。对于⽂件,将列出⽂件名以及其他信 息。 命令中的选项,一次可以传递多个 ,…...
MySql面试总结(二)
WHERE 子句优化 截至2024年7月,MySQL最新稳定版本是8.2,并不存在MySQL 8.4 。下面从常见的几个方面为你介绍 MySQL 8.x 中 WHERE 子句的优化方法: 1. 确保使用索引 原理:索引可以加快数据的查找速度,当 WHERE 子句中的条件列有索引时,MySQL 可以直接定位到符合条件的数…...

Pytorch中的主要函数
目录 一、torch.manual_seed(seed)二、torch.cuda.manual_seed(seed)三、torch.rand(*size, outNone, dtypeNone, layouttorch.strided, deviceNone, requires_gradFalse)四、给大家写一个常用的自动选择电脑cuda 或者cpu 的小技巧五、torch.version.cuda;torch.bac…...
Java实现大数据量导出报表
一、实现方式 在Java中,导出数据到Excel有多种方式,每种方式都有其优缺点,适用于不同的场景。以下是常见的几种方式及其特点: 1.1 Apache POI Apache POI 是 Java 中最流行的库,支持读写 Excel 文件(包括…...
大语言模型 智能助手——既能生成自然语言回复,又能在必要时调用外部工具获取实时数据
示例代码: import json from langgraph.graph import Graph, END,StateGraph from langchain_core.utils.function_calling import convert_to_openai_function from langchain_community.tools.openweathermap import OpenWeatherMapQueryRun from langchain_core…...

PyTorch 系统教程:理解机器学习数据分割
数据分割是机器学习中的一个基本概念,它直接影响模型的性能和泛化。在本文中,我们将深入研究为什么数据分割在机器学习中很重要,并演示如何使用PyTorch有效地实现它。 理解数据分割 数据分割是将数据集划分为单独的组以进行训练、验证和测试…...
分水岭算法(Watershed Algorithm)教程:硬币分割实例
import cv2 import numpy as np# 1. 图像预处理 img cv2.imread("./water/water_coins.jpeg") gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, thresh cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU) kernel np.ones((3, 3), np.int8)…...

【STM32项目实战系列】基于STM32G474的FDCAN驱动配置
前言:本周工作中用到了CANFD的驱动,由于以前都是用到的CAN2.0,所以过程并不是特别的顺利,所以中间遇到几个比较小的问题导致自己卡住了一段时间,特此记录一下并完全奉上自己的配置的源码。 1,CANFD配置与简…...

shell文本处理
shell文本处理 一、grep 过滤来自一个文件或标准输入匹配模式内容。除了 grep 外,还有 egrep、fgrep。egrep 是 grep 的扩展,相当于 grep -E。fgrep 相当于 grep -f,用的比较少。 用法 grep [OPTION]... PATTERN [FILE]...支持的正则描述…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...

C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...