From 26860a0b4deee902f520fefd34085a44fde1d710 Mon Sep 17 00:00:00 2001 From: lanrtop Date: Sat, 11 Apr 2026 14:43:55 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20Tauri=20+=20React?= =?UTF-8?q?=20=E5=86=85=E7=BD=AE=E9=A1=B9=E7=9B=AE=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/commands/templates.rs | 199 ++++++++++++++++++++++++++-- 1 file changed, 188 insertions(+), 11 deletions(-) diff --git a/src-tauri/src/commands/templates.rs b/src-tauri/src/commands/templates.rs index 28a38ac..c558c5b 100644 --- a/src-tauri/src/commands/templates.rs +++ b/src-tauri/src/commands/templates.rs @@ -344,17 +344,8 @@ pub fn seed_builtin_templates() { Err(_) => return, }; - // 已有内置模板则跳过 - let count: i64 = conn - .query_row( - "SELECT COUNT(*) FROM project_templates WHERE is_builtin = 1", - [], - |row| row.get(0), - ) - .unwrap_or(0); - if count > 0 { - return; - } + // 不做全局跳过,直接用 INSERT OR IGNORE 保证幂等 + // 这样新增内置模板时,已有用户也能自动获得 struct Tpl { id: &'static str, @@ -450,6 +441,192 @@ edition = "2021" init_commands: "", files: vec![], }, + Tpl { + id: "builtin-tauri-react", + name: "Tauri + React", + description: "桌面应用,Tauri v2 + React 19 + TypeScript + Vite", + platform: "both", + tech_stack: "Rust,Node,pnpm,React,TypeScript,Tauri", + init_commands: "pnpm install", + files: vec![ + ("package.json", r#"{ + "name": "{{project_name_kebab}}", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "tauri dev", + "build": "tauri build", + "preview": "vite preview" + }, + "dependencies": { + "@tauri-apps/api": "^2", + "react": "^19", + "react-dom": "^19" + }, + "devDependencies": { + "@tauri-apps/cli": "^2", + "@types/react": "^19", + "@types/react-dom": "^19", + "@vitejs/plugin-react": "^4", + "typescript": "^5", + "vite": "^6" + } +} +"#), + ("vite.config.ts", r#"import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +export default defineConfig(async () => ({ + plugins: [react()], + clearScreen: false, + server: { + port: 1420, + strictPort: true, + watch: { + ignored: ["**/src-tauri/**"], + }, + }, +})); +"#), + ("tsconfig.json", r#"{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true + }, + "include": ["src"] +} +"#), + ("index.html", r#" + + + + + {{project_name}} + + +
+ + + +"#), + ("src/main.tsx", r#"import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; + +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( + + + +); +"#), + ("src/App.tsx", r#"import { useState } from "react"; + +function App() { + const [count, setCount] = useState(0); + + return ( +
+

{{project_name}}

+

Tauri v2 + React + TypeScript

+ +
+ ); +} + +export default App; +"#), + ("src-tauri/tauri.conf.json", r#"{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "{{project_name_kebab}}", + "version": "0.1.0", + "identifier": "com.example.{{project_name_snake}}", + "build": { + "beforeDevCommand": "pnpm vite", + "devUrl": "http://localhost:1420", + "beforeBuildCommand": "pnpm vite build", + "frontendDist": "../dist" + }, + "app": { + "windows": [ + { + "title": "{{project_name}}", + "width": 1200, + "height": 800 + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [] + } +} +"#), + ("src-tauri/Cargo.toml", r#"[package] +name = "{{project_name_snake}}" +version = "0.1.0" +description = "A Tauri App" +authors = [] +edition = "2021" + +[lib] +name = "{{project_name_snake}}_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +tauri = { version = "2", features = [] } +tauri-plugin-opener = "2" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +"#), + ("src-tauri/build.rs", "fn main() {\n tauri_build::build()\n}\n"), + ("src-tauri/src/main.rs", r#"// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + {{project_name_snake}}_lib::run() +} +"#), + ("src-tauri/src/lib.rs", r#"#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_opener::init()) + .invoke_handler(tauri::generate_handler![]) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); +} +"#), + ("src-tauri/capabilities/default.json", r#"{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "Capability for the main window", + "windows": ["main"], + "permissions": [ + "core:default", + "opener:default" + ] +} +"#), + (".gitignore", "node_modules/\ndist/\n.env\n.env.*\n!.env.example\nsrc-tauri/target/\nsrc-tauri/gen/\n"), + ], + }, ]; for t in &templates {