فهرست منبع

new page脚本完美

yichael 5 ماه پیش
والد
کامیت
3a4ea31fd3
4فایلهای تغییر یافته به همراه403 افزوده شده و 238 حذف شده
  1. 7 237
      main.js
  2. 0 1
      src/App.jsx
  3. 245 0
      src/pages/0-NEW-PAGE.BAT
  4. 151 0
      响应式设计教程.md

+ 7 - 237
main.js

@@ -3,7 +3,6 @@ import { fileURLToPath } from 'url';
 import path from 'path';
 import { exec } from 'child_process';
 import { promisify } from 'util';
-import os from 'os';
 
 const execAsync = promisify(exec);
 
@@ -11,23 +10,7 @@ const __filename = fileURLToPath(import.meta.url);
 const __dirname = path.dirname(__filename);
 const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged;
 
-/**
- * 设置内容安全策略 (Content Security Policy, CSP)
- * 
- * 功能说明:
- * - 为 Electron 应用设置 CSP 规则,防止 XSS 攻击
- * - 开发环境:允许 localhost 连接,便于开发调试
- * - 生产环境:严格限制资源加载,提高安全性
- * 
- * CSP 规则说明:
- * - default-src 'self': 默认只允许同源资源
- * - script-src: 控制脚本加载源
- * - style-src: 控制样式表加载源
- * - connect-src: 控制网络请求源(如 fetch, WebSocket)
- * - img-src: 控制图片加载源
- * - font-src: 控制字体加载源
- * - worker-src: 控制 Web Worker 加载源
- */
+// 设置内容安全策略(CSP),防止 XSS 攻击
 function setContentSecurityPolicy() {
   session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
     const csp = isDev
@@ -41,25 +24,7 @@ function setContentSecurityPolicy() {
   });
 }
 
-/**
- * 创建主窗口
- * 
- * 功能说明:
- * - 创建 Electron 应用的主窗口
- * - 配置窗口大小和 Web 安全设置
- * - 根据环境加载不同的内容源
- * 
- * 窗口配置:
- * - 宽度: 1200px
- * - 高度: 800px
- * - preload: 预加载脚本路径(用于安全地暴露 Node.js API)
- * - nodeIntegration: false(禁用 Node.js 集成,提高安全性)
- * - contextIsolation: true(启用上下文隔离,防止 XSS 攻击)
- * 
- * 加载逻辑:
- * - 开发环境: 加载 Vite 开发服务器 (http://localhost:5173) 并打开开发者工具
- * - 生产环境: 加载构建后的静态文件 (dist/index.html)
- */
+// 创建主窗口,根据环境加载不同内容源
 function createWindow() {
   const mainWindow = new BrowserWindow({
     width: 1200,
@@ -79,126 +44,7 @@ function createWindow() {
   }
 }
 
-/**
- * 获取本机局域网 IP 地址
- * 
- * 功能说明:
- * - 遍历所有网络接口,查找第一个非内部的 IPv4 地址
- * - 用于确定局域网网段,以便扫描同网段的设备
- * 
- * 查找逻辑:
- * - 遍历所有网络接口(如以太网、Wi-Fi 等)
- * - 过滤条件:IPv4 地址 && 非内部地址(非 127.0.0.1)
- * - 返回找到的第一个符合条件的 IP 地址
- * 
- * @returns {string} 本机局域网 IP 地址,如果未找到则返回默认值 '192.168.1.1'
- * 
- * @example
- * // 如果本机 IP 是 192.168.2.35,则返回 '192.168.2.35'
- * const localIP = getLocalIP(); // '192.168.2.35'
- */
-function getLocalIP() {
-  const interfaces = os.networkInterfaces();
-  for (const name of Object.keys(interfaces)) {
-    for (const iface of interfaces[name]) {
-      if (iface.family === 'IPv4' && !iface.internal) {
-        return iface.address;
-      }
-    }
-  }
-  return '192.168.1.1';
-}
-
-/**
- * 扫描局域网内端口 5555 的 ADB 设备
- * 
- * 功能说明:
- * - 获取本机 IP 地址,确定局域网网段(如 192.168.2.x)
- * - 扫描该网段内所有可能的 IP 地址(1-254)
- * - 对每个 IP 尝试连接 ADB 端口 5555
- * - 使用并行扫描提高效率,每批同时扫描 50 个 IP
- * 
- * 扫描策略:
- * - 根据本机 IP 确定网段(如 192.168.2.35 → 网段 192.168.2.x)
- * - 扫描范围:192.168.2.1 到 192.168.2.254
- * - 每批处理 50 个 IP,避免同时发起过多连接
- * - 每个连接超时时间:300ms(快速失败,提高扫描速度)
- * - 忽略连接失败的错误(大部分 IP 不会有设备)
- * 
- * 性能优化:
- * - 使用 Promise.all 并行执行,大幅提升扫描速度
- * - 分批处理避免系统资源耗尽
- * - 扫描完成后等待 500ms,确保所有连接已建立
- * 
- * @returns {Promise<void>} 扫描完成(不返回结果,结果通过 getADBDevices 获取)
- * 
- * @example
- * // 如果本机 IP 是 192.168.2.35
- * // 会扫描 192.168.2.1:5555 到 192.168.2.254:5555
- * await scanADBDevices();
- */
-async function scanADBDevices() {
-  const localIP = getLocalIP();
-  const ipParts = localIP.split('.');
-  const baseIP = `${ipParts[0]}.${ipParts[1]}.${ipParts[2]}`;
-
-  // 并行扫描,每批 50 个 IP
-  const batchSize = 100;
-  const promises = [];
-
-  for (let i = 1; i <= 254; i += batchSize) {
-    const batch = [];
-    for (let j = i; j < i + batchSize && j <= 254; j++) {
-      const ip = `${baseIP}.${j}`;
-      batch.push(
-        execAsync(`adb connect ${ip}:5555`, { timeout: 300 })
-          .catch(() => {}) // 忽略连接失败
-      );
-    }
-    promises.push(Promise.all(batch));
-  }
-
-  await Promise.all(promises);
-  // 等待连接建立
-  await new Promise(resolve => setTimeout(resolve, 500));
-}
-
-/**
- * 获取已连接的 ADB 设备列表
- * 
- * 功能说明:
- * - 执行 `adb devices` 命令获取当前所有已连接的设备
- * - 解析命令输出,提取设备 ID 和状态
- * - 只返回状态为 'device' 的设备(已授权且可用的设备)
- * 
- * 命令输出格式:
- * ```
- * List of devices attached
- * 192.168.2.5:5555    device
- * 3764756281ZZZZZ     device
- * ```
- * 
- * 解析逻辑:
- * - 跳过第一行标题("List of devices attached")
- * - 每行按空白字符分割,提取设备 ID 和状态
- * - 只保留状态为 'device' 的设备(排除 'offline', 'unauthorized' 等)
- * 
- * 返回数据格式:
- * ```javascript
- * [
- *   { id: '192.168.2.5:5555', status: 'device' },
- *   { id: '3764756281ZZZZZ', status: 'device' }
- * ]
- * ```
- * 
- * @returns {Promise<Array<{id: string, status: string}>>} 设备列表数组
- * - id: 设备标识符(IP:端口 或 USB 序列号)
- * - status: 设备状态(通常为 'device')
- * 
- * @example
- * const devices = await getADBDevices();
- * // [{ id: '192.168.2.5:5555', status: 'device' }]
- */
+// 获取已连接的 ADB 设备列表
 async function getADBDevices() {
   try {
     const { stdout } = await execAsync('adb devices');
@@ -221,72 +67,12 @@ async function getADBDevices() {
   }
 }
 
-/**
- * IPC 处理程序:扫描 ADB 设备
- * 
- * 功能说明:
- * - 接收渲染进程的扫描请求
- * - 先执行扫描操作(scanADBDevices)
- * - 然后获取设备列表(getADBDevices)
- * - 返回扫描后的设备列表
- * 
- * 调用方式(从渲染进程):
- * ```javascript
- * const devices = await window.electronAPI.scanADBDevices();
- * ```
- * 
- * @returns {Promise<Array<{id: string, status: string}>>} 设备列表数组
- */
-ipcMain.handle('scan-adb-devices', async () => {
-  try {
-    await scanADBDevices();
-    return await getADBDevices();
-  } catch (error) {
-    console.error('扫描设备失败:', error);
-    return [];
-  }
-});
-
-/**
- * IPC 处理程序:获取 ADB 设备列表
- * 
- * 功能说明:
- * - 接收渲染进程的获取设备列表请求
- * - 直接返回当前已连接的设备列表(不执行扫描)
- * - 用于刷新设备列表,查看当前连接状态
- * 
- * 调用方式(从渲染进程):
- * ```javascript
- * const devices = await window.electronAPI.getADBDevices();
- * ```
- * 
- * @returns {Promise<Array<{id: string, status: string}>>} 设备列表数组
- */
+// IPC 处理程序:获取 ADB 设备列表
 ipcMain.handle('get-adb-devices', async () => {
   return await getADBDevices();
 });
 
-/**
- * IPC 处理程序:连接 ADB 设备
- * 
- * 功能说明:
- * - 接收渲染进程的连接设备请求
- * - 执行 `adb connect` 命令连接指定设备
- * - 返回连接结果(成功或失败)
- * 
- * 调用方式(从渲染进程):
- * ```javascript
- * const result = await window.electronAPI.connectADBDevice('192.168.2.5:5555');
- * // { success: true } 或 { success: false, error: '错误信息' }
- * ```
- * 
- * @param {Electron.IpcMainInvokeEvent} event - IPC 事件对象
- * @param {string} ipPort - 设备 IP 地址和端口(格式:'192.168.2.5:5555')
- * 
- * @returns {Promise<{success: boolean, error?: string}>} 连接结果
- * - success: true 表示连接成功,false 表示连接失败
- * - error: 连接失败时的错误信息(仅在失败时存在)
- */
+// IPC 处理程序:连接 ADB 设备
 ipcMain.handle('connect-adb-device', async (event, ipPort) => {
   try {
     await execAsync(`adb connect ${ipPort}`);
@@ -297,15 +83,7 @@ ipcMain.handle('connect-adb-device', async (event, ipPort) => {
   }
 });
 
-/**
- * 应用启动逻辑
- * 
- * 当 Electron 应用准备就绪时:
- * 1. 设置内容安全策略(CSP)
- * 2. 创建主窗口
- * 3. 监听 'activate' 事件(macOS 特有,当应用被激活时)
- *    - 如果没有窗口,则创建新窗口
- */
+// 应用启动逻辑:设置 CSP、创建窗口、监听激活事件
 app.whenReady().then(() => {
   setContentSecurityPolicy();
   createWindow();
@@ -317,15 +95,7 @@ app.whenReady().then(() => {
   });
 });
 
-/**
- * 应用关闭逻辑
- * 
- * 当所有窗口都关闭时:
- * - macOS: 不退出应用(保持应用在 Dock 中运行)
- * - Windows/Linux: 退出应用
- * 
- * 这是 Electron 的标准行为,符合各平台的应用生命周期管理
- */
+// 应用关闭逻辑:macOS 保持运行,其他平台退出
 app.on('window-all-closed', () => {
   if (process.platform !== 'darwin') {
     app.quit();

+ 0 - 1
src/App.jsx

@@ -1,6 +1,5 @@
 import './App.css';
 import Home from './pages/Home.jsx';
-
 function App() {
   return (
     <div className="app">

+ 245 - 0
src/pages/0-NEW-PAGE.BAT

@@ -0,0 +1,245 @@
+@echo off
+setlocal EnableDelayedExpansion
+
+rem Change to project root
+cd /d "%~dp0\..\.." 2>nul
+if errorlevel 1 (
+    echo ERROR: Cannot change to project root directory
+    echo Script location: %~dp0
+    pause
+    exit /b 1
+)
+
+:GetPageName
+rem Get page name and strip spaces/tabs
+set /p PAGE_NAME="Enter Page Name: "
+if "!PAGE_NAME!"=="" (
+    echo Page name cannot be empty!
+    echo.
+    goto GetPageName
+)
+set "PAGE_NAME=!PAGE_NAME: =!"
+set "PAGE_NAME=!PAGE_NAME:	=!"
+if "!PAGE_NAME!"=="" (
+    echo Page name cannot be empty!
+    echo.
+    goto GetPageName
+)
+
+rem Select creation mode
+echo ========================================
+echo   Create New Page
+echo ========================================
+echo   1. Create Page With Folder
+echo   2. Create Page
+echo ========================================
+set /p MODE="Select mode (1 or 2) [Default: 1]: "
+if "%MODE%"=="" set "MODE=1"
+if not "%MODE%"=="1" if not "%MODE%"=="2" (
+    echo Invalid selection!
+    pause
+    goto EndScript
+)
+
+rem Set file paths based on mode
+if "%MODE%"=="1" (
+    set "PAGE_FOLDER=src\pages\!PAGE_NAME!"
+    set "JSX_FILE=src\pages\!PAGE_NAME!\!PAGE_NAME!.jsx"
+    set "JS_FILE=src\pages\!PAGE_NAME!\!PAGE_NAME!.js"
+    set "CSS_FILE=src\pages\!PAGE_NAME!\!PAGE_NAME!.css"
+) else (
+    set "PAGE_FOLDER="
+    set "JSX_FILE=src\pages\!PAGE_NAME!.jsx"
+    set "JS_FILE=src\pages\!PAGE_NAME!.js"
+    set "CSS_FILE=src\pages\!PAGE_NAME!.css"
+)
+
+rem Check if the page already exists
+if "%MODE%"=="1" (
+    if exist "!PAGE_FOLDER!" (
+        echo Page !PAGE_NAME! already exists!
+        pause
+        goto EndScript
+    )
+) else (
+    if exist "!JSX_FILE!" (
+        echo Page !PAGE_NAME! already exists!
+        pause
+        goto EndScript
+    )
+)
+
+rem Create folder if needed
+if "%MODE%"=="1" if not exist "!PAGE_FOLDER!" mkdir "!PAGE_FOLDER!"
+
+rem Create JSX file
+(
+echo import './!PAGE_NAME!.css';
+echo import { !PAGE_NAME!Logic } from './!PAGE_NAME!.js';
+echo.
+echo function !PAGE_NAME!^(^) {
+echo   const logic = !PAGE_NAME!Logic^(^);
+echo.
+echo   return ^(
+echo     ^<div className="!PAGE_NAME!-container"^>
+echo       ^<h1^>!PAGE_NAME!^</h1^>
+echo     ^</div^>
+echo   ^);
+echo }
+echo.
+echo export default !PAGE_NAME!;
+) > "!JSX_FILE!"
+
+rem Create JS file
+(
+echo export function !PAGE_NAME!Logic^(^) {
+echo   // business logic placeholder
+echo.
+echo   return {
+echo     // expose data or methods here
+echo   };
+echo }
+) > "!JS_FILE!"
+
+rem Create CSS file
+(
+echo .!PAGE_NAME!-container {
+echo   padding: 20px;
+echo }
+) > "!CSS_FILE!"
+
+echo Created: !JSX_FILE!
+echo Created: !JS_FILE!
+echo Created: !CSS_FILE!
+
+rem Add the new page into App.jsx
+set "APP_FILE=src\App.jsx"
+if exist "!APP_FILE!" (
+    call :AddToApp "%MODE%" "!PAGE_NAME!" "!APP_FILE!"
+    echo Added !PAGE_NAME! to App.jsx
+) else (
+    echo Warning: App.jsx not found, skipping...
+)
+
+rem Show summary
+echo.
+echo ========================================
+echo Page !PAGE_NAME! created successfully!
+echo ========================================
+echo.
+echo Summary:
+if "%MODE%"=="1" (
+    echo   1. Created folder: !PAGE_FOLDER!
+    echo   2. Created files:
+    echo      - !JSX_FILE!
+    echo      - !JS_FILE!
+    echo      - !CSS_FILE!
+    echo   3. Modified file: src\App.jsx
+    echo      - Added import: import !PAGE_NAME! from './pages/!PAGE_NAME!/!PAGE_NAME!.jsx';
+    echo      - Added component: ^<!PAGE_NAME! /^>
+) else (
+    echo   1. Created files in src\pages directory:
+    echo      - !JSX_FILE!
+    echo      - !JS_FILE!
+    echo      - !CSS_FILE!
+    echo   2. Modified file: src\App.jsx
+    echo      - Added import: import !PAGE_NAME! from './pages/!PAGE_NAME!.jsx';
+    echo      - Added component: ^<!PAGE_NAME! /^>
+)
+
+pause
+goto EndScript
+
+rem Add new page into App.jsx (subroutine)
+:AddToApp
+setlocal EnableDelayedExpansion
+set "MODE_VAL=%~1"
+set "PAGE_VAL=%~2"
+set "APP_PATH=%~3"
+
+if not exist "%APP_PATH%" (
+    echo ERROR: File not found: %APP_PATH%
+    exit /b 1
+)
+
+rem Generate import line based on mode
+if "%MODE_VAL%"=="1" (
+    set "IMPORT_LINE=import %PAGE_VAL% from './pages/%PAGE_VAL%/%PAGE_VAL%.jsx';"
+) else (
+    set "IMPORT_LINE=import %PAGE_VAL% from './pages/%PAGE_VAL%.jsx';"
+)
+
+rem Find last import line number
+set "LAST_IMPORT_LINE="
+for /f "tokens=1* delims=:" %%n in ('findstr /n /b /c:"import " "%APP_PATH%"') do set "LAST_IMPORT_LINE=%%n"
+
+rem Find end of return statement (best effort)
+set "RETURN_LINE="
+for /f "tokens=1 delims=:" %%n in ('findstr /n /c:"  );" "%APP_PATH%"') do set "RETURN_LINE=%%n"
+if not defined RETURN_LINE (
+    for /f "tokens=1 delims=:" %%n in ('findstr /n /c:");" "%APP_PATH%"') do set "RETURN_LINE=%%n"
+)
+
+rem Find last </div> before return ends
+set "LAST_DIV_LINE="
+for /f "tokens=1* delims=:" %%n in ('findstr /n /c:"</div>" "%APP_PATH%"') do (
+    if defined RETURN_LINE (
+        if %%n lss !RETURN_LINE! set "LAST_DIV_LINE=%%n"
+    ) else (
+        set "LAST_DIV_LINE=%%n"
+    )
+)
+
+rem Check if import/component already exist
+set "NEED_IMPORT=1"
+set "NEED_COMPONENT=1"
+findstr /c:"import %PAGE_VAL% from" "%APP_PATH%" >nul 2>&1
+if not errorlevel 1 set "NEED_IMPORT=0"
+findstr /c:"<%PAGE_VAL% />" "%APP_PATH%" >nul 2>&1
+if not errorlevel 1 set "NEED_COMPONENT=0"
+
+rem Rewrite App.jsx with additions
+set "TEMP_FILE=%TEMP%\app_temp_%RANDOM%.jsx"
+if exist "%TEMP_FILE%" del "%TEMP_FILE%"
+
+set /a "LINE_NO=0"
+for /f "usebackq delims=" %%a in ("%APP_PATH%") do (
+    set "line=%%a"
+    set /a "LINE_NO+=1"
+    set "isImport=0"
+    if /i "!line:~0,7!"=="import " set "isImport=1"
+
+    rem Insert component before closing div inside return
+    if "!LINE_NO!"=="!LAST_DIV_LINE!" if !NEED_COMPONENT! equ 1 (
+        set "indent="
+        set "line_copy=!line!"
+        for /f "tokens=1 delims=<" %%b in ("!line_copy!") do set "indent=%%b"
+        if "!indent!"=="" set "indent=    "
+        if "!indent!"=="!line_copy!" set "indent=    "
+        rem add 2 spaces for component indent under div
+        set "indent=!indent!  "
+        >>"%TEMP_FILE%" echo !indent!^<%PAGE_VAL% /^>
+        set "NEED_COMPONENT=0"
+    )
+
+    >>"%TEMP_FILE%" echo(!line!
+
+    rem Insert new import after the last import line
+    if !isImport! equ 1 if !NEED_IMPORT! equ 1 if "!LINE_NO!"=="!LAST_IMPORT_LINE!" (
+        >>"%TEMP_FILE%" echo !IMPORT_LINE!
+        set "NEED_IMPORT=0"
+    )
+)
+
+rem Append missing import/component at end if needed
+if !NEED_IMPORT! equ 1 >>"%TEMP_FILE%" echo !IMPORT_LINE!
+if !NEED_COMPONENT! equ 1 >>"%TEMP_FILE%" echo       ^<%PAGE_VAL% /^>
+
+move /y "%TEMP_FILE%" "%APP_PATH%" >nul
+endlocal
+exit /b
+
+:EndScript
+endlocal
+echo 按任意键退出窗口...
+pause >nul

+ 151 - 0
响应式设计教程.md

@@ -0,0 +1,151 @@
+# 响应式设计快速教程
+
+## 一、@media 媒体查询
+
+### 基本语法
+```css
+@media (条件) {
+  /* 当条件满足时应用的样式 */
+}
+```
+
+### 常用断点(屏幕宽度)
+```css
+/* 移动端 */
+@media (max-width: 767px) { }  /* 小于等于 767px */
+
+/* 平板 */
+@media (min-width: 768px) and (max-width: 1023px) { }  /* 768px 到 1023px */
+
+/* 桌面 */
+@media (min-width: 1024px) { }  /* 大于等于 1024px */
+```
+
+### 如何选择断点值?
+- **767px**:常见手机最大宽度
+- **768px**:常见平板最小宽度
+- **1024px**:常见桌面最小宽度
+
+**技巧**:打开浏览器开发者工具,调整窗口大小,观察你的设计在哪个宽度开始变形,那就是你的断点。
+
+---
+
+## 二、clamp() 函数
+
+### 基本语法
+```css
+clamp(最小值, 理想值, 最大值)
+```
+
+### 工作原理
+浏览器会:
+1. 如果 `理想值` < `最小值` → 使用 `最小值`
+2. 如果 `理想值` > `最大值` → 使用 `最大值`
+3. 否则 → 使用 `理想值`
+
+### 如何计算数值?
+
+#### 示例 1:字体大小
+```css
+font-size: clamp(0.875rem, 1.5vw, 1rem);
+```
+- **最小值 0.875rem**:小屏幕时字体不会太小(约 14px)
+- **理想值 1.5vw**:随视口宽度变化(视口宽度 × 1.5%)
+- **最大值 1rem**:大屏幕时字体不会太大(约 16px)
+
+**计算思路**:
+- 先确定最小和最大值(固定值)
+- 中间用 `vw`(视口宽度百分比)让它在中间自适应
+
+#### 示例 2:内边距
+```css
+padding: clamp(0.5rem, 1.5vw, 1rem);
+```
+- **最小值 0.5rem**:小屏幕时内边距 8px
+- **理想值 1.5vw**:随屏幕宽度变化
+- **最大值 1rem**:大屏幕时内边距 16px
+
+#### 示例 3:宽度
+```css
+width: clamp(250px, 30%, 400px);
+```
+- **最小值 250px**:最小宽度 250px
+- **理想值 30%**:占父容器 30%
+- **最大值 400px**:最大宽度 400px
+
+### 快速计算法
+
+**步骤 1:确定最小值和最大值**
+- 打开浏览器,调整到最小窗口,看元素是否还能正常显示 → 这是最小值
+- 调整到最大窗口,看元素是否过大 → 这是最大值
+
+**步骤 2:确定中间值**
+- 使用 `vw`(视口宽度百分比)
+- 公式:`理想值 = (最大值 - 最小值) / 2` 对应的 vw
+- 或者直接用 `2vw`、`1.5vw` 等常见值测试
+
+**步骤 3:测试调整**
+- 在浏览器中调整窗口大小,观察效果
+- 如果小屏幕时太小 → 增大最小值
+- 如果大屏幕时太大 → 减小最大值
+- 如果中间过渡不自然 → 调整中间 vw 值
+
+---
+
+## 三、实际应用示例
+
+### 完整示例
+```css
+.container {
+  /* 容器内边距:小屏 12px,大屏 24px,中间自适应 */
+  padding: clamp(0.75rem, 2vw, 1.5rem);
+  
+  /* 标题字体:小屏 16px,大屏 28px,中间自适应 */
+  font-size: clamp(1rem, 2.5vw, 1.75rem);
+}
+
+/* 移动端特殊处理 */
+@media (max-width: 767px) {
+  .container {
+    padding: 1rem;  /* 移动端固定值 */
+  }
+}
+```
+
+### 常用数值参考
+
+| 属性 | 最小值 | 中间值 | 最大值 | 说明 |
+|------|--------|--------|--------|------|
+| 字体 | `0.875rem` | `1.5vw` | `1rem` | 正文 |
+| 字体 | `1rem` | `2.5vw` | `1.8rem` | 标题 |
+| 内边距 | `0.5rem` | `1.5vw` | `1rem` | 小元素 |
+| 内边距 | `0.75rem` | `2vw` | `1.5rem` | 容器 |
+| 圆角 | `4px` | `1vw` | `8px` | 按钮 |
+| 间距 | `0.5rem` | `1.5vw` | `1rem` | gap/margin |
+
+---
+
+## 四、调试技巧
+
+1. **打开浏览器开发者工具**(F12)
+2. **切换到响应式设计模式**(Ctrl+Shift+M)
+3. **调整窗口大小**,观察元素变化
+4. **修改 CSS 值**,实时查看效果
+5. **测试不同设备尺寸**(手机、平板、桌面)
+
+---
+
+## 五、总结
+
+1. **@media**:用于不同屏幕尺寸的样式切换
+   - `max-width`:小于等于某值
+   - `min-width`:大于等于某值
+
+2. **clamp()**:让数值在最小和最大之间自适应
+   - 格式:`clamp(最小, 理想, 最大)`
+   - 理想值用 `vw` 实现自适应
+
+3. **工作流程**:
+   - 先用 `clamp()` 实现基础自适应
+   - 再用 `@media` 处理特殊情况(如移动端布局变化)
+