yichael 4 tygodni temu
commit
8a96f10b57

+ 31 - 0
.gitignore

@@ -0,0 +1,31 @@
+# Dependencies
+node_modules/
+package-lock.json
+yarn.lock
+
+# Build outputs
+dist/
+dist-electron/
+build/
+out/
+
+# Logs
+*.log
+npm-debug.log*
+
+# OS
+.DS_Store
+Thumbs.db
+
+# IDE
+.vscode/
+.idea/
+*.swp
+*.swo
+
+# Environment
+.env
+.env.local
+
+# Electron
+*.asar

+ 27 - 0
README.md

@@ -0,0 +1,27 @@
+**一、框架:Electron + React + Vite
+
+**二、测试连接:adb connect 192.168.0.15:5555
+
+**三、开发命令
+- `npm run dev`:只启动 Vite 前端开发服务器。
+- `npm run electron`:直接启动 Electron(加载本地文件)。
+- `npm run electron:dev`:同时启动 Vite 开发服务器并在准备好后打开 Electron(推荐开发时使用)。
+- `npm run build`:构建生产版本。
+- `npm run preview`:预览构建后的应用。
+
+**四、将页面设置为子页面(条件渲染)
+
+**步骤:**
+
+**1. 修改 `src/pages/Home.jsx`**:
+   - 添加:`import Devices from './Devices/Devices';`
+   - 在 `HomeLogic()` 中获取:`const { showDevices, setShowDevices } = HomeLogic();`
+   - 在 return 中添加:`{showDevices && <Devices />}` 和按钮控制显示/隐藏
+
+**2. 修改 `src/pages/Home.js`**:
+   - 添加:`import { useState } from 'react';`
+   - 在 `HomeLogic` 中添加:`const [showDevices, setShowDevices] = useState(false);`
+   - return 中暴露:`showDevices, setShowDevices`
+
+**3. 修改 `src/App.jsx`**:
+   - 删除 Devices 的 import 和组件使用

+ 76 - 0
README_REACT.md

@@ -0,0 +1,76 @@
+# Electron + React + Vite 项目
+
+最基础的 Electron + React + Vite 桌面应用项目。
+
+## 项目结构
+
+```
+.
+├── src/
+│   ├── index.jsx          # 入口文件(只做路由跳转)
+│   └── home/
+│       ├── home.jsx       # Home 页面组件
+│       ├── home.js        # Home 逻辑
+│       └── home.css       # Home 样式
+├── electron/
+│   └── main.js            # Electron 主进程
+├── vite.config.js         # Vite 配置
+└── package.json
+```
+
+## 安装依赖
+
+```bash
+npm install
+```
+
+## 运行应用
+
+### 开发模式(推荐)
+```bash
+npm run electron:dev
+```
+
+这会同时启动:
+1. Vite 开发服务器(http://localhost:5173)
+2. Electron 应用
+
+### 分别启动
+```bash
+# 终端1:启动 Vite
+npm run dev
+
+# 终端2:启动 Electron
+npm run electron
+```
+
+## 打包应用
+
+```bash
+npm run electron:build
+```
+
+## 页面说明
+
+### index.jsx
+- 只做路由跳转逻辑
+- 访问 `/` 自动跳转到 `/home`
+
+### home.jsx
+- Home 页面组件
+- 包含一个 label 和一个 button
+
+### home.js
+- Home 页面的业务逻辑
+- `handleClick` 函数处理按钮点击
+
+### home.css
+- Home 页面的样式
+- 最基础的样式
+
+## 技术栈
+
+- **Electron** - 桌面应用框架
+- **React** - UI 框架
+- **Vite** - 构建工具
+- **React Router** - 路由

+ 19 - 0
config.js

@@ -0,0 +1,19 @@
+// Electron 应用配置
+module.exports = {
+  // 窗口配置
+  window: {
+    width: 800,
+    height: 600,
+    autoHideMenuBar: true, // 隐藏菜单栏(File、Edit、View、Window、Help)
+  },
+  
+  // 开发工具配置
+  devTools: {
+    enabled: false, // 是否显示调试侧边栏(DevTools)
+  },
+
+  // Python 路径配置
+  pythonPath: {
+    path: 'D:\\Program\\Python312' // Python 安装路径
+  }
+}

+ 42 - 0
electron/main.js

@@ -0,0 +1,42 @@
+const { app, BrowserWindow } = require('electron')
+const path = require('path')
+const config = require('../config.js')
+const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged
+
+function createWindow() {
+  const mainWindow = new BrowserWindow({
+    width: config.window.width,
+    height: config.window.height,
+    autoHideMenuBar: config.window.autoHideMenuBar, // 从配置文件读取
+    webPreferences: {
+      nodeIntegration: false,
+      contextIsolation: true
+    }
+  })
+
+  if (isDev) {
+    mainWindow.loadURL('http://localhost:5173')
+    // 根据配置文件决定是否打开调试侧边栏
+    if (config.devTools.enabled) {
+      mainWindow.webContents.openDevTools()
+    }
+  } else {
+    mainWindow.loadFile(path.join(__dirname, '../dist/index.html'))
+  }
+}
+
+app.whenReady().then(() => {
+  createWindow()
+
+  app.on('activate', () => {
+    if (BrowserWindow.getAllWindows().length === 0) {
+      createWindow()
+    }
+  })
+})
+
+app.on('window-all-closed', () => {
+  if (process.platform !== 'darwin') {
+    app.quit()
+  }
+})

+ 73 - 0
enviroment-check.ps1

@@ -0,0 +1,73 @@
+# Environment check script
+Write-Host "Checking development environment..." -ForegroundColor Cyan
+Write-Host "================================" -ForegroundColor Cyan
+
+# Check Node.js
+Write-Host "`nChecking Node.js..." -ForegroundColor Yellow
+$nodeVersion = node --version 2>$null
+if ($nodeVersion) {
+    Write-Host "✓ Node.js: $nodeVersion" -ForegroundColor Green
+} else {
+    Write-Host "✗ Node.js is not installed" -ForegroundColor Red
+    # Download nodejs
+    Write-Host "Downloading Node.js..." -ForegroundColor Yellow
+    npm install -g node
+    if ($LASTEXITCODE -ne 0) {
+        Write-Host "✗ Node.js download failed" -ForegroundColor Red
+        exit 1
+    }
+    Write-Host "✓ Node.js downloaded successfully" -ForegroundColor Green
+    node --version
+}
+
+# Check npm
+Write-Host "`nChecking npm..." -ForegroundColor Yellow
+$npmVersion = npm --version 2>$null
+if ($npmVersion) {
+    Write-Host "✓ npm: $npmVersion" -ForegroundColor Green
+} else {
+    Write-Host "✗ npm is not installed" -ForegroundColor Red
+    # Download npm
+    Write-Host "Downloading npm..." -ForegroundColor Yellow
+    npm install -g npm
+    if ($LASTEXITCODE -ne 0) {
+        Write-Host "✗ npm download failed" -ForegroundColor Red
+        exit 1
+    }
+    Write-Host "✓ npm downloaded successfully" -ForegroundColor Green
+    npm --version
+}
+
+# Check if dependencies are installed
+Write-Host "`nChecking project dependencies..." -ForegroundColor Yellow
+if (Test-Path "node_modules") {
+    Write-Host "✓ node_modules exists" -ForegroundColor Green
+} else {
+    Write-Host "✗ node_modules does not exist, installing dependencies..." -ForegroundColor Yellow
+    npm install
+    if ($LASTEXITCODE -ne 0) {
+        Write-Host "✗ Dependency installation failed" -ForegroundColor Red
+        exit 1
+    }
+}
+
+# Check if python is installed
+Write-Host "`nChecking if python is installed..." -ForegroundColor Yellow
+$pythonVersion = python --version 2>$null
+if ($pythonVersion) {
+    Write-Host "✓ python: $pythonVersion" -ForegroundColor Green
+} else {
+    Write-Host "✗ python is not installed" -ForegroundColor Red
+    # Download python
+    Write-Host "Downloading python..." -ForegroundColor Yellow
+    npm install -g python
+    if ($LASTEXITCODE -ne 0) {
+        Write-Host "✗ python download failed" -ForegroundColor Red
+        exit 1
+    }
+    Write-Host "✓ python downloaded successfully" -ForegroundColor Green
+    python --version
+}
+
+Write-Host "`n================================" -ForegroundColor Cyan
+Write-Host "Environment check completed!" -ForegroundColor Green

+ 28 - 0
index.html

@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Electron React Vite App</title>
+    <style>
+      * {
+        margin: 0;
+        padding: 0;
+        box-sizing: border-box;
+      }
+      html, body {
+        width: 100%;
+        height: 100%;
+        overflow: hidden;
+      }
+      #root {
+        width: 100%;
+        height: 100%;
+      }
+    </style>
+  </head>
+  <body>
+    <div id="root"></div>
+    <script type="module" src="/src/index.jsx"></script>
+  </body>
+</html>

+ 27 - 0
package.json

@@ -0,0 +1,27 @@
+{
+  "name": "electron-react-vite-app",
+  "version": "1.0.0",
+  "description": "A basic Electron + React + Vite application",
+  "main": "electron/main.js",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview",
+    "electron": "electron .",
+    "electron:dev": "concurrently \"npm run dev\" \"wait-on http://localhost:5173 && electron .\"",
+    "electron:build": "npm run build && electron-builder"
+  },
+  "dependencies": {
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0",
+    "react-router-dom": "^6.20.0"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-react": "^4.2.1",
+    "concurrently": "^8.2.2",
+    "electron": "^28.0.0",
+    "electron-builder": "^24.9.1",
+    "vite": "^5.0.8",
+    "wait-on": "^7.2.0"
+  }
+}

+ 5 - 0
run_react.bat

@@ -0,0 +1,5 @@
+@echo off
+powershell -ExecutionPolicy Bypass -File enviroment-check.ps1
+if %ERRORLEVEL% EQU 0 (
+    npm run electron:dev
+)

+ 21 - 0
src/index.jsx

@@ -0,0 +1,21 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
+import Home from './page/home.jsx'
+
+function App() {
+  return (
+    <BrowserRouter>
+      <Routes>
+        <Route path="/" element={<Navigate to="/page" replace />} />
+        <Route path="/page" element={<Home />} />
+      </Routes>
+    </BrowserRouter>
+  )
+}
+
+ReactDOM.createRoot(document.getElementById('root')).render(
+  <React.StrictMode>
+    <App />
+  </React.StrictMode>
+)

+ 12 - 0
src/page/device/device.css

@@ -0,0 +1,12 @@
+.device-container {
+    width: 100%;
+    height: 100%;
+
+    background-color: #0769fb;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+ 
+    margin: 0;
+    padding: 0;
+}

+ 0 - 0
src/page/device/device.js


+ 16 - 0
src/page/device/device.jsx

@@ -0,0 +1,16 @@
+import React from 'react'
+import './device.css'
+
+function Device({ show }) {
+  if (!show) {
+    return null
+  }
+
+  return (
+    <div className="device-container">
+        Device (20%)
+    </div>
+  )
+}
+
+export default Device

+ 21 - 0
src/page/home.css

@@ -0,0 +1,21 @@
+/* Home页面的容器 */
+.home-container {
+  width: 100vw;
+  height: 100vh;
+  margin: 0;
+  padding: 0;
+ 
+  box-sizing: border-box;
+}
+
+.home-bg {
+  display: grid !important;
+  grid-template-columns: 20% 30% 50% !important;
+
+  width: 100%;
+  height: 100%;
+  gap: 0;
+  margin: 0;
+  padding: 0;
+  box-sizing: border-box;
+}

+ 23 - 0
src/page/home.js

@@ -0,0 +1,23 @@
+// 切换 Alert 显示/隐藏
+export function toggleAlert(showAlert, setShowAlert) {
+  setShowAlert(!showAlert)
+}
+
+// 关闭 Alert
+export function closeAlert(setShowAlert) {
+  setShowAlert(false)
+}
+
+// 创建切换处理函数
+export function createHandleToggle(showAlert, setShowAlert) {
+  return () => {
+    toggleAlert(showAlert, setShowAlert)
+  }
+}
+
+// 创建关闭处理函数
+export function createHandleClose(setShowAlert) {
+  return () => {
+    closeAlert(setShowAlert)
+  }
+}

+ 28 - 0
src/page/home.jsx

@@ -0,0 +1,28 @@
+import React, { useState } from 'react'
+import './home.css'
+import Alert from './public/alert-view/alert-view.jsx'
+import Device from './device/device.jsx'
+import ScreenShot from './screenshot/screenshot.jsx'
+import Process from './process/process.jsx'
+import { createHandleToggle, createHandleClose } from './home.js'
+
+function Home() {
+  const [showAlert, setShowAlert] = useState(false)
+  const [showDevice, setShowDevice] = useState(true)
+  const [showScreenShot, setShowScreenShot] = useState(true)
+  const [showProcess, setShowProcess] = useState(true)
+  
+  return (
+    <div className="home-container">
+      <div className='home-bg'>
+        {showDevice && <Device show={showDevice}/>}
+        {showScreenShot && <ScreenShot show={showScreenShot}/>}
+        {showProcess && <Process show={showProcess}/>}
+      </div>
+     
+      {showAlert && <Alert show={showAlert} onClose={createHandleClose(setShowAlert)} />}
+    </div>  
+  )
+}
+
+export default Home

+ 13 - 0
src/page/process/process.css

@@ -0,0 +1,13 @@
+.process-container {
+    width: 100%;
+    height: 100%;
+ 
+    background-color: #07fb40;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+
+    margin: 0;
+    padding: 0;
+}

+ 0 - 0
src/page/process/process.js


+ 16 - 0
src/page/process/process.jsx

@@ -0,0 +1,16 @@
+import React from 'react'
+import './process.css'
+
+function Process({ show }) {
+  if (!show) {
+    return null
+  }
+
+  return (
+    <div className="process-container">
+        Process (20%)
+    </div>
+  )
+}
+
+export default Process

+ 78 - 0
src/page/public/alert-view/alert-view.css

@@ -0,0 +1,78 @@
+.alert-container {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+}
+
+.alert-bg {
+    width: 50%;
+    height: 30%;
+    background-color: #fff;
+    border-radius: 10px;
+    box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
+    display: flex;
+    flex-direction: column;
+
+    /* 文字不能选择 */
+    user-select: none;
+    
+    /* 根据屏幕缩放而缩放,同时响应宽度和高度变化 */
+    /* 取宽度和高度计算结果的较小值,确保字体大小同时响应宽高变化 */
+    /* 父div宽度 = 50vw,高度 = 30vh */
+    /* 宽度比例:50vw * 0.04 = 父div宽度的4% */
+    /* 高度比例:30vh * 0.08 = 父div高度的8% */
+    font-size: min(calc(50vw * 0.04), calc(30vh * 0.08));
+}
+
+.title {
+    width: 100%;
+    height: 100%;
+    /* em单位精确相对于父元素.alert-bg的字体大小 */
+    font-size: 2.5em;
+    font-weight: bold;
+    color: #000;
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    align-items: center;
+}
+
+.content {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    align-items: center;
+    /* em单位精确相对于父元素.alert-bg的字体大小 */
+    font-size: 1.5em;
+    color: #000;
+}
+
+.footer {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    align-items: center;
+}
+
+.confirm-button {
+    width:30%;
+    height: 80%;
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    align-items: center;
+
+    box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
+    border-radius: 10px;
+    font-size: 1.5em;
+}

+ 17 - 0
src/page/public/alert-view/alert-view.js

@@ -0,0 +1,17 @@
+// 点击确定隐藏alert
+export function confirm(onClose) {
+  if (onClose) {
+    onClose()
+  }
+}
+
+// 处理确定按钮点击
+export function handleConfirm(onClose) {
+  confirm(onClose)
+}
+
+// 默认标题
+export const defaultTitle = '提示'
+
+// 默认内容
+export const defaultContent = '这是一个提示'

+ 27 - 0
src/page/public/alert-view/alert-view.jsx

@@ -0,0 +1,27 @@
+import React from 'react'
+import './alert-view.css'
+import { handleConfirm, defaultTitle, defaultContent } from './alert-view.js'
+
+function AlertView({ show, onClose, title = defaultTitle, content = defaultContent }) {
+  if (!show) {
+    return null
+  }
+
+  return (
+    <div className="alert-container">
+        <div className="alert-bg">
+            <div className="title">
+              <label>{title}</label>
+            </div>
+            <div className="content">
+              <label>{content}</label>
+            </div>
+            <div className="footer">
+               <div className="confirm-button" onClick={() => handleConfirm(onClose)}>确定</div>
+            </div>
+        </div>
+    </div>
+  )
+}
+
+export default AlertView

+ 12 - 0
src/page/screenshot/screenshot.css

@@ -0,0 +1,12 @@
+.screenshot-container {
+    width: 100%;
+    height: 100%;
+
+    background-color: #fb0707e6;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+ 
+    margin: 0;
+    padding: 0;
+}

+ 0 - 0
src/page/screenshot/screenshot.js


+ 16 - 0
src/page/screenshot/screenshot.jsx

@@ -0,0 +1,16 @@
+import React from 'react'
+import './screenshot.css'
+
+function ScreenShot({ show }) {
+  if (!show) {
+    return null
+  }
+
+  return (
+    <div className="screenshot-container">
+        ScreenShot (60%)
+    </div>
+  )
+}
+
+export default ScreenShot

+ 9 - 0
vite.config.js

@@ -0,0 +1,9 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+export default defineConfig({
+  plugins: [react()],
+  server: {
+    port: 5173
+  }
+})