yichael 3 недель назад
Родитель
Сommit
944e004ccb

+ 24 - 1
bat-tool/adb-enable-port5555/enable-port5555.js

@@ -6,8 +6,31 @@ const path = require('path')
 const projectRoot = path.resolve(__dirname, '../..')
 const adbPath = path.join(projectRoot, 'exe', 'adb', 'adb.exe')
 
+// Get list of connected devices
+const devicesCommand = `"${adbPath}" devices`
+const devicesOutput = execSync(devicesCommand, { encoding: 'utf-8' })
+
+// Parse device list
+const deviceLines = devicesOutput.split('\n').filter(line => line.trim() && !line.startsWith('List'))
+const devices = deviceLines.map(line => {
+  const parts = line.trim().split('\t')
+  return parts[0]
+}).filter(id => id)
+
+if (devices.length === 0) {
+  console.error('No devices found. Please connect a device via USB.')
+  process.exit(1)
+}
+
+// Use first device if multiple devices are connected
+const deviceId = devices[0]
+if (devices.length > 1) {
+  console.log(`Multiple devices found. Using first device: ${deviceId}`)
+  console.log(`All devices: ${devices.join(', ')}`)
+}
+
 // Enable TCP/IP debugging on port 5555 via USB
 const port = 5555
-const command = `"${adbPath}" tcpip ${port}`
+const command = `"${adbPath}" -s ${deviceId} tcpip ${port}`
 const output = execSync(command, { encoding: 'utf-8' })
 console.log(output.trim())

+ 3 - 1
doc/CODING_STANDARDS.md

@@ -37,4 +37,6 @@ Do not add any `console.log` statements in production code.
 
 ## 8. Avoid Creating Script Files
 
-Do not create script files unnecessarily. If you can call PowerShell directly, use PowerShell instead of creating a separate script file.
+Do not create script files unnecessarily. If you can call PowerShell directly, use PowerShell instead of creating a separate script file.
+
+## 9. if need create any new files. must ask to me first

+ 79 - 0
doc/JSON_CRUD.md

@@ -0,0 +1,79 @@
+# JSON 增删改查简单指南
+
+## 基本说明
+
+所有 JSON 文件保存在 `static` 目录下,使用相对路径即可。
+
+## 增(Create)- 创建文件
+
+```javascript
+await window.electronAPI.runNodejsScript('json-parser', 'create', 'device_list.json', JSON.stringify({devices: []}))
+```
+
+## 查(Read)- 读取文件
+
+```javascript
+const result = await window.electronAPI.runNodejsScript('json-parser', 'read', 'device_list.json')
+const jsonData = JSON.parse(result.stdout).data
+```
+
+**文件不存在时:** `result.stdout` 为空字符串 `''`
+
+## 改(Update)- 更新文件
+
+```javascript
+await window.electronAPI.runNodejsScript('json-parser', 'update', 'device_list.json', JSON.stringify({devices: ['192.168.1.1', '192.168.1.2']}))
+```
+
+## 删(Delete)- 删除元素
+
+删除数组中的元素:
+
+```javascript
+// 1. 读取
+const result = await window.electronAPI.runNodejsScript('json-parser', 'read', 'device_list.json')
+const jsonData = JSON.parse(result.stdout).data
+
+// 2. 过滤删除
+const filtered = jsonData.devices.filter(ip => ip !== '192.168.1.1')
+
+// 3. 更新
+await window.electronAPI.runNodejsScript('json-parser', 'update', 'device_list.json', JSON.stringify({devices: filtered}))
+```
+
+## 完整示例
+
+### 添加设备到数组
+
+```javascript
+// 读取
+const result = await window.electronAPI.runNodejsScript('json-parser', 'read', 'device_list.json')
+const jsonData = result.stdout === '' ? {devices: []} : JSON.parse(result.stdout).data
+
+// 添加
+jsonData.devices.push('192.168.1.3')
+
+// 更新
+await window.electronAPI.runNodejsScript('json-parser', 'update', 'device_list.json', JSON.stringify({devices: jsonData.devices}))
+```
+
+### 删除设备从数组
+
+```javascript
+// 读取
+const result = await window.electronAPI.runNodejsScript('json-parser', 'read', 'device_list.json')
+const jsonData = JSON.parse(result.stdout).data
+
+// 删除
+const filtered = jsonData.devices.filter(ip => ip !== '192.168.1.3')
+
+// 更新
+await window.electronAPI.runNodejsScript('json-parser', 'update', 'device_list.json', JSON.stringify({devices: filtered}))
+```
+
+### 检查文件是否存在
+
+```javascript
+const result = await window.electronAPI.runNodejsScript('json-parser', 'check', 'device_list.json')
+const exists = JSON.parse(result.stdout).exists
+```

+ 37 - 4
doc/JSON_PARSER.md

@@ -32,7 +32,7 @@ const response = JSON.parse((await window.electronAPI.runNodejsScript('json-pars
 
 ### 2. read - 读取 JSON 文件
 
-读取 JSON 文件内容。
+读取 JSON 文件内容。如果文件不存在,返回 `{ success: true, data: null }`。
 
 **参数:**
 - `operation`: `'read'`
@@ -42,7 +42,14 @@ const response = JSON.parse((await window.electronAPI.runNodejsScript('json-pars
 **示例:**
 ```javascript
 // 读取整个文件
-const data = JSON.parse((await window.electronAPI.runNodejsScript('json-parser', 'read', 'jason/testjson')).stdout).data
+const readResult = await window.electronAPI.runNodejsScript('json-parser', 'read', 'device_list.json')
+const readResponse = JSON.parse(readResult.stdout)
+const data = readResponse.data
+
+// 如果文件不存在(data 为 null),则创建
+if (data === null) {
+    const createResult = await window.electronAPI.runNodejsScript('json-parser', 'create', 'device_list.json', JSON.stringify({devices: []}))
+}
 
 // 读取特定路径
 const ip = JSON.parse((await window.electronAPI.runNodejsScript('json-parser', 'read', 'jason/testjson', JSON.stringify(['devices', 0, 'ip']))).stdout).data
@@ -50,10 +57,17 @@ const ip = JSON.parse((await window.electronAPI.runNodejsScript('json-parser', '
 
 **返回:**
 ```json
+// 文件存在时
 {
   "success": true,
   "data": {...}
 }
+
+// 文件不存在时
+{
+  "success": true,
+  "data": null
+}
 ```
 
 ### 3. update - 更新 JSON 文件
@@ -159,19 +173,38 @@ JSON.stringify(['users', 1, 'profile', 'name'])
 5. **返回值解析**:脚本返回的 `stdout` 是 JSON 字符串,需要 `JSON.parse()` 解析
 6. **错误处理**:检查返回的 `success` 字段判断操作是否成功
 7. **自动创建目录**:如果文件所在目录不存在,会自动创建
+8. **read 操作特性**:文件不存在时返回 `{ success: true, data: null }`,可通过 `data === null` 判断文件是否存在
+
+## 实际应用示例
+
+### 读取或创建文件
+
+```javascript
+const readResult = await window.electronAPI.runNodejsScript('json-parser', 'read', 'device_list.json')
+const readResponse = JSON.parse(readResult.stdout)
+let jsonData = readResponse.data
+
+if (jsonData === null) {
+    const createResult = await window.electronAPI.runNodejsScript('json-parser', 'create', 'device_list.json', JSON.stringify({devices: []}))
+    const createResponse = JSON.parse(createResult.stdout)
+    if (createResponse.success) {
+        jsonData = {devices: []}
+    }
+}
+```
 
 ## 错误示例
 
 ```json
 {
   "success": false,
-  "error": "JSON file does not exist"
+  "error": "Missing jsonString parameter for create operation"
 }
 ```
 
 ```json
 {
   "success": false,
-  "error": "Missing jsonString parameter for create operation"
+  "error": "Invalid file path"
 }
 ```

+ 74 - 0
doc/创建类的流程.md

@@ -0,0 +1,74 @@
+# 创建类的流程
+
+## 1. 在 JS 文件中创建类
+
+```javascript
+// 类名使用 PascalCase,描述类的用途
+class YourClass {
+    constructor(param1, param2) {
+        // 初始化属性
+        this.property1 = []
+        this.property2 = param1
+        this.property3 = param2
+
+        // 立即执行初始化逻辑
+        this.init()
+    }
+
+    async init() {
+        // 异步初始化逻辑
+        // 例如:读取数据、调用 API 等
+    }
+
+    async method1() {
+        // 通过 this. 访问属性
+        this.property1.push(item)
+    }
+
+    async method2() {
+        // 调用其他方法
+        await this.method1()
+    }
+}
+
+// 导出类
+export { YourClass }
+```
+
+## 2. 在 JSX 文件中使用类
+
+```jsx
+import { YourClass } from './your-class.js'
+
+function YourComponent({ show }) {
+  const yourRef = useRef(null)
+
+  useEffect(() => {
+    // 创建实例,生命周期与组件一致
+    yourRef.current = new YourClass(param1, param2)
+    
+    // 页面销毁时清理
+    return () => {
+      yourRef.current = null
+    }
+  }, [])
+
+  return (
+    <div onClick={() => yourRef.current?.method1()}>
+      {/* UI */}
+    </div>
+  )
+}
+```
+
+## 3. 关键要点
+
+- **构造函数**:接收参数,初始化属性,调用 `init()` 方法
+- **init() 方法**:执行异步初始化逻辑,不需要单独调用
+- **this. 访问**:所有方法中通过 `this.property` 访问属性
+- **生命周期**:实例在组件挂载时创建,卸载时销毁
+- **可选链**:调用方法时使用 `?.` 避免空引用错误
+
+## 4. 示例:DeviceClass
+
+参考 `src/page/device/device.js` 和 `src/page/device/device.jsx`

+ 2 - 1
nodejs/json-parser.js

@@ -73,7 +73,8 @@ if (operation === 'create') {
   }
 } else if (operation === 'read') {
   if (!fs.existsSync(filePath)) {
-    result = { success: false, error: 'JSON file does not exist' }
+    process.stdout.write('')
+    process.exit(0)
   } else {
     const jsonData = JSON.parse(fs.readFileSync(filePath, 'utf8'))
     result = {

+ 7 - 1
src/page/device/connect-item/connect-item.jsx

@@ -1,7 +1,7 @@
 import React from 'react'
 import './connect-item.scss'
 
-function ConnectItem({ ipAddress ,isConnected=false, isPreviewing=false}) {
+function ConnectItem({ ipAddress, isConnected=false, isPreviewing=false, onRemove }) {
   return (
     <div className="connect-item-container">
       <div className="ip-address">{ipAddress}</div>
@@ -11,6 +11,12 @@ function ConnectItem({ ipAddress ,isConnected=false, isPreviewing=false}) {
       <div className="preview-btn">
         {isPreviewing ? '停止预览' : '预览'}
       </div>
+      <div className="remove-btn" onClick={() => onRemove && onRemove(ipAddress)}>
+        <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+          <circle cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="2" fill="none"/>
+          <path d="M8 12H16" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
+        </svg>
+      </div>
     </div>
   )
 }

+ 14 - 0
src/page/device/connect-item/connect-item.scss

@@ -68,4 +68,18 @@ $font-size-scale: 1.5;  // 字体缩放系数,调整此值可改变字体大
 
     border-radius: 10px;
     @include highlight-btn(#00fb43);
+}
+
+.remove-btn {
+    width: 40%;
+    height: 40%;
+
+    margin: 1%;
+    @include div-btn-hover-effect;
+    @include div-btn-pressed-effect;
+
+    svg {
+        width: 100%;
+        height: 100%;
+    }
 }

+ 135 - 28
src/page/device/device.js

@@ -1,34 +1,141 @@
-export async function begin(){
-    // const result = await window.electronAPI.runNodejsScript('adb-connect', '192.168.2.5', '5555')
-    // alert(result.stdout) // 脚本输出
-    // const result = await window.electronAPI.runPythonScript('test', '1')
-    // alert(result.stdout) // 脚本输出
-
-
-    const is_exist = await window.electronAPI.runNodejsScript('json-parser', 'read', 'jason/testjson')
-    if (is_exist.success) {
-        alert('File exists')
-    } else {
-        alert('File does not exist')
+import comfirmView from '../public/comfirm-view/comfirm-view.js'
+import hintView from '../public/hint-view/hint-view.js'
+import alertView from '../public/alert-view/alert-view.js'
+
+// 设备管理类,所有方法都可以通过 this. 访问属性
+class DeviceClass {
+    constructor(setDeviceList) {
+        this.deviceArr = []
+        this.setDeviceList = setDeviceList
     }
-}
 
-export async function handleRefresh(e, self) {
-    // Start loading animation
-    self.startAnimation()
+    async init() {
+ 
+        let readResult = await window.electronAPI.runNodejsScript('json-parser', 'read', 'device_list.json')
 
-    // Simulate async operation
-    setTimeout(() => {
-        // Stop loading animation when done
-        self.stopAnimation()
-    }, 5000) 
-}
+        if (readResult.stdout === '') {
+            await window.electronAPI.runNodejsScript('json-parser', 'create', 'device_list.json', JSON.stringify({devices: []}))
+            if (this.setDeviceList) {
+                this.setDeviceList([])
+            }
+        } 
+        else
+        {
+            const jsonData = JSON.parse(readResult.stdout)
+            this.deviceArr = jsonData.data.devices
+            if (this.setDeviceList) {
+                this.setDeviceList(this.deviceArr)   
+            }
+        }
+    }
+
+    async handleRefresh(e, self) {
+        self.startAnimation()
+
+        this.scanDevice(() => {
+            self.stopAnimation()
+        });
+    }
+
+    async scanDevice(callback)
+    {
+        for (let i = 0; i <= 3; i++) {
+            for (let j = 0; j <= 255; j++) 
+            {
+                const ip = `192.168.${i}.${j}`
+                const result = await window.electronAPI.runNodejsScript('adb-connect', ip, '5555')
+                if (result.stdout == 'success') {
+                    await this.addDevice(ip)
+                }
+            }  
+        }
+        callback();
+    }
 
-export async function handleAdd() {
-    console.log('Add device clicked')
+    async handleAdd() {
+  
+        const ip = document.querySelector('.ip-input input').value
+        hintView.setContent(ip)
+        hintView.show()
+        const result = await window.electronAPI.runNodejsScript('adb-connect', ip, '5555')
+    
+        if(result.stdout == 'success') {
+            const newDevice = await this.addDevice(ip)
+            if (newDevice && this.setDeviceList) { 
+                this.setDeviceList(prev => [...prev, newDevice])
 
-    const ip = document.querySelector('.ip-input input').value
-    const result = await window.electronAPI.runNodejsScript('adb-connect', ip, '5555')
-    alert(result.stdout) // 脚本输出
+                hintView.setContent('设备添加成功')
+                hintView.show()
+            }
+            return
+        }
+
+        alertView.show()
+        alertView.setContent('无法检测到连接设备')
+    }
+
+    async addDevice(ip) {
+        const readResult = await window.electronAPI.runNodejsScript('json-parser', 'read', 'device_list.json')
+        
+        let jsonData
+        if (readResult.stdout === '') {
+            await window.electronAPI.runNodejsScript('json-parser', 'create', 'device_list.json', JSON.stringify({devices: []}))
+            jsonData = {devices: []}
+        } else {
+            const parsed = JSON.parse(readResult.stdout)
+            jsonData = parsed.data || {devices: []}
+        }
+        
+        // 数组中是字符串,直接 push IP 字符串
+        jsonData.devices.push(ip)
+        await window.electronAPI.runNodejsScript('json-parser', 'update', 'device_list.json', JSON.stringify({devices: jsonData.devices}))
+
+        // 更新 this.deviceArr
+        this.deviceArr = jsonData.devices
+        this.setDeviceList(jsonData.devices);
+        return ip
+    }
+
+    async removeDevice(ip) {
+        
+        comfirmView.show()
+        comfirmView.setContent('确定要删除设备吗?')
+        comfirmView.onConfirm = async () => {
+            comfirmView.hide()
+
+            const readResult = await window.electronAPI.runNodejsScript('json-parser', 'read', 'device_list.json')
+        
+            if (readResult.stdout === '') {
+                return
+            }
+            
+            const parsed = JSON.parse(readResult.stdout)
+            const jsonData = parsed.data || {devices: []}
+            
+            // 过滤掉要删除的设备(数组中是字符串,直接比较)
+            const filteredDevices = jsonData.devices.filter(deviceIp => deviceIp !== ip)
+            
+            // 更新 JSON 文件
+            await window.electronAPI.runNodejsScript('json-parser', 'update', 'device_list.json', JSON.stringify({devices: filteredDevices}))
+            
+            // 更新 this.deviceArr
+            this.deviceArr = filteredDevices
+            
+            // 更新 deviceArrRef
+            if (this.deviceArrRef) {
+                this.deviceArrRef.current = filteredDevices
+            }
+            
+            // 更新 React state
+            if (this.setDeviceList) {
+                this.setDeviceList(filteredDevices)
+            }
+        }
+        comfirmView.onCancel = () => {
+            comfirmView.hide()
+        }
+    }
+}
 
-}
+// 导出类,由组件创建实例并管理生命周期
+export { DeviceClass}

+ 78 - 33
src/page/device/device.jsx

@@ -2,56 +2,101 @@ import React, { useEffect, useRef, useState } from 'react'
 import './device.scss'
 import UpdateBtn from './update-btn/update-btn.jsx'
 import ConnectItem from './connect-item/connect-item.jsx'
-import { handleRefresh, begin, handleAdd } from './device.js'
+import HintView from '../public/hint-view/hint-view.jsx'
+import Alert from '../public/alert-view/alert-view.jsx'
+import ComfirmView from '../public/comfirm-view/comfirm-view.jsx'
+import { DeviceClass } from './device.js'
+import hintView from '../public/hint-view/hint-view.js'
+import alertView from '../public/alert-view/alert-view.js'
+import comfirmView from '../public/comfirm-view/comfirm-view.js'
+import { createHandleClose } from '../home.js'
 
 function Device({ show }) {
-  const hasRun = useRef(false)
   const [deviceList, setDeviceList] = useState([])
+  const [showHintView, setShowHintView] = useState(false)
+  const [showAlert, setShowAlert] = useState(false)
+  const [showComfirmView, setShowComfirmView] = useState(false)
+  const deviceClass = useRef(null)
 
   if (!show) {
     return null
   }
 
-  // 当页面第一次加载出来(使用 useRef 确保只执行一次,避免 StrictMode 双重调用)
+  // 初始化 deviceClass,确保在渲染时就有值
+  if (!deviceClass.current) {
+    deviceClass.current = new DeviceClass(setDeviceList)
+  }
+
+  // 设置 API 对象的回调函数并初始化 deviceClass
+  useEffect(() => {
+    hintView.setShowCallback(setShowHintView)
+    alertView.setShowCallback(setShowAlert)
+    comfirmView.setShowCallback(setShowComfirmView)
+    
+    // 回调设置完成后,初始化 deviceClass
+    if (deviceClass.current && !deviceClass.current._initialized) {
+      deviceClass.current.init()
+    }
+  }, [])
+
+  // 当 setDeviceList 更新时,同步更新 deviceClass 的引用
   useEffect(() => {
-    if (!hasRun.current) {
-      hasRun.current = true
-      begin()
+    if (deviceClass.current) {
+      deviceClass.current.setDeviceList = setDeviceList
     }
-  }, []) 
+  }, [setDeviceList])
 
   return (
-    <div className="device-container">
-      {/* 更新设备列表 */}
-      <div className="device-update">
-        <div className="device-update-title">设备列表</div>
-        <div className="device-update-btn">
-          <UpdateBtn
-            onClick={handleRefresh}
-            title="Refresh device list"
-          />
+    <>
+      <div className="device-container">
+        {/* 更新设备列表 */}
+        <div className="device-update">
+          <div className="device-update-title">设备列表</div>
+          <div className="device-update-btn">
+            <UpdateBtn
+              onClick={(e, self) => deviceClass.current?.handleRefresh(e, self)}
+              title="Refresh device list"
+            />
+          </div>
         </div>
-      </div>
-
-      {/* 设备列表 */}
-      <div className="device-list">
-        {deviceList.map((device, index) => (
-          <ConnectItem key={index} ipAddress={device.ipAddress} />
-        ))}
-      </div>
 
-      {/* 添加设备 */}
-      <div className="device-add">
-        <div className="ip-input">
-          <input type="text" placeholder="请输入设备IP" defaultValue="192.168." />
+        {/* 设备列表 */}
+        <div className="device-list">
+          {deviceList.map((device, index) => (
+            <ConnectItem 
+              key={index} 
+              ipAddress={deviceList[index]} 
+              onRemove={(ip) => deviceClass.current?.removeDevice(ip)}
+            />
+          ))}
         </div>
-        <div className="add-btn" onClick={handleAdd}>
-          <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
-            <path d="M12 5V19M5 12H19" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
-          </svg>
+
+        {/* 添加设备 */}
+        <div className="device-add">
+          <div className="ip-input">
+            <input type="text" placeholder="请输入设备IP" defaultValue="192.168." />
+          </div>
+          <div className="add-btn" onClick={() => deviceClass.current?.handleAdd()}>
+            <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+              <path d="M12 5V19M5 12H19" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
+            </svg>
+          </div>
         </div>
       </div>
-    </div>
+
+      {showHintView && <HintView show={showHintView} onClose={createHandleClose(setShowHintView)} title={hintView._title} content={hintView._content} />}
+      {showAlert && <Alert show={showAlert} onClose={createHandleClose(setShowAlert)} title={alertView._title} content={alertView._content} />}
+      {showComfirmView && <ComfirmView show={showComfirmView} onClose={() => {
+        comfirmView.hide()
+        if (comfirmView._onCancel) {
+          comfirmView._onCancel()
+        }
+      }} onConfirm={() => {
+        if (comfirmView._onConfirm) {
+          comfirmView._onConfirm()
+        }
+      }} title={comfirmView._title} content={comfirmView._content} />}
+    </>
   )
 }
 

+ 15 - 21
src/page/device/device.scss

@@ -46,7 +46,7 @@
       width: 90%;
       height: 8%;
       @include flex-row-between;
-      border: 1px solid #000000;
+      // border: 1px solid #000000;
     }
   }
 
@@ -56,41 +56,35 @@
     height: 10%;
 
     @include flex-row-between;
-    @include box-sizing-border-box;
 
     .ip-input {
-      width: 90%;
+      width: 80%;
       height: 50%;
-      
       @include flex-center;
-
+      
+      
       input {
-        width: 90%;
-        height: 80%;
-        border: 1px solid #000000;
         outline: none;
+        border: none;
         text-align: center;
-        
-        &::placeholder {
-          text-align: center;
-        }
-        
-        &:focus {
-          // outline: 2px solid #0769fb;
-          outline-offset: 0;
-          // border-color: #0769fb;
-        }
+        width: 90%;
+        height: 100%;
+
+        border: 1px solid #000000;
       }
     }
     .add-btn {
-      width: 10%;
+      width: 20%;
       height: 100%;
-      // border: 1px solid rgb(13, 255, 0);
-      @include flex-center;
+      cursor: pointer;
       
+      @include flex-center;
+      @include div-btn-hover-effect;
+      @include div-btn-pressed-effect;
       svg {
         width: 80%;
         height: 80%;
+        pointer-events: none;
       }
     }
   }

+ 9 - 0
src/page/home.jsx

@@ -1,6 +1,10 @@
 import React, { useState } from 'react'
 import './home.scss'
 import Alert from './public/alert-view/alert-view.jsx'
+import Hint from './public/hint-view/hint-view.jsx'
+import Comfirm from './public/comfirm-view/comfirm-view.jsx'
+
+
 import Device from './device/device.jsx'
 import ScreenShot from './screenshot/screenshot.jsx'
 import AiChat from './ai-chat/ai-chat.jsx'
@@ -9,6 +13,9 @@ import { createHandleClose } from './home.js'
 
 function Home() {
   const [showAlert, setShowAlert] = useState(false)
+  const [showHint, setShowHint] = useState(false)
+  const [showComfirm, setShowComfirm] = useState(false)
+
   const [showDevice, setShowDevice] = useState(true)
   const [showScreenShot, setShowScreenShot] = useState(true)
   const [showAiChat, setShowAiChat] = useState(true)
@@ -24,6 +31,8 @@ function Home() {
       </div>
      
       {showAlert && <Alert show={showAlert} onClose={createHandleClose(setShowAlert)} />}
+      {showHint && <Hint show={showHint} onClose={createHandleClose(setShowHint)} />}
+      {showComfirm && <Comfirm show={showComfirm} onClose={createHandleClose(setShowComfirm)} />}
     </div>
   )
 }

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

@@ -15,3 +15,43 @@ export const defaultTitle = '提示'
 
 // 默认内容
 export const defaultContent = '这是一个提示'
+
+// 全局 alertView API 对象
+const alertViewAPI = {
+  _show: false,
+  _content: defaultContent,
+  _title: defaultTitle,
+  _setShowCallback: null,
+
+  show() {
+    this._show = true
+    if (this._setShowCallback) {
+      this._setShowCallback(true)
+    }
+  },
+
+  hide() {
+    this._show = false
+    if (this._setShowCallback) {
+      this._setShowCallback(false)
+    }
+  },
+
+  setMessage(content) {
+    this._content = content
+  },
+
+  setContent(content) {
+    this._content = content
+  },
+
+  setTitle(title) {
+    this._title = title
+  },
+
+  setShowCallback(callback) {
+    this._setShowCallback = callback
+  }
+}
+
+export default alertViewAPI

+ 1 - 0
src/page/public/alert-view/alert-view.scss

@@ -12,6 +12,7 @@ $full-size: 100%;
   left: 0;
   width: $full-size;
   height: $full-size;
+  z-index: 9999;
   display: flex;
   flex-direction: column;
   justify-content: center;

+ 62 - 0
src/page/public/comfirm-view/comfirm-view.js

@@ -0,0 +1,62 @@
+// 点击确定隐藏alert
+export function confirm(onClose) {
+  if (onClose) {
+    onClose()
+  }
+}
+
+// 处理确定按钮点击
+export function handleConfirm(onClose) {
+  confirm(onClose)
+}
+
+// 处理取消按钮点击
+export function handleCancel(onClose) {
+  if (onClose) {
+    onClose()
+  }
+}
+
+// 默认标题
+export const defaultTitle = '提示'
+
+// 默认内容
+export const defaultContent = '这是一个提示'
+
+// 全局 comfirmView API 对象
+const comfirmViewAPI = {
+  _show: false,
+  _content: defaultContent,
+  _title: defaultTitle,
+  _onConfirm: null,
+  _onCancel: null,
+  _setShowCallback: null,
+
+  show() {
+    this._show = true
+    if (this._setShowCallback) {
+      this._setShowCallback(true)
+    }
+  },
+
+  hide() {
+    this._show = false
+    if (this._setShowCallback) {
+      this._setShowCallback(false)
+    }
+  },
+
+  setContent(content) {
+    this._content = content
+  },
+
+  setTitle(title) {
+    this._title = title
+  },
+
+  setShowCallback(callback) {
+    this._setShowCallback = callback
+  }
+}
+
+export default comfirmViewAPI

+ 28 - 0
src/page/public/comfirm-view/comfirm-view.jsx

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

+ 117 - 0
src/page/public/comfirm-view/comfirm-view.scss

@@ -0,0 +1,117 @@
+// ========== 颜色配置 ==========
+$white: #fff;
+$black: #000;
+
+// ========== 尺寸配置 ==========
+$full-size: 100%;
+
+// ========== 样式定义 ==========
+.comfirm-container {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: $full-size;
+  height: $full-size;
+  z-index: 9999;
+  background-color: rgba(0, 0, 0, 0.3);
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+
+  .alert-bg {
+    width: 50%;
+    height: 30%;
+    background-color: $white;
+    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: $full-size;
+      height: $full-size;
+      /* em单位精确相对于父元素.alert-bg的字体大小 */
+      font-size: 2.5em;
+      font-weight: bold;
+      color: $black;
+      display: flex;
+      flex-direction: row;
+      justify-content: center;
+      align-items: center;
+    }
+
+    .content {
+      width: $full-size;
+      height: $full-size;
+      display: flex;
+      flex-direction: row;
+      justify-content: center;
+      align-items: center;
+      /* em单位精确相对于父元素.alert-bg的字体大小 */
+      font-size: 1.5em;
+      color: $black;
+    }
+
+    .footer {
+      width: $full-size;
+      height: $full-size;
+      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;
+        cursor: pointer;
+        transition: all 0.3s;
+        
+        &:hover {
+          background-color: rgba(0, 0, 0, 0.05);
+          transform: scale(1.05);
+        }
+        
+        &:active {
+          transform: scale(0.95);
+        }
+      }
+      .cancel-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;
+        cursor: pointer;
+        transition: all 0.3s;
+
+        &:hover {
+          background-color: rgba(0, 0, 0, 0.05);
+          transform: scale(1.05);
+        }
+        
+        &:active {
+          transform: scale(0.95);
+        }
+      }
+    }
+  }
+}

+ 70 - 0
src/page/public/hint-view/hint-view.js

@@ -0,0 +1,70 @@
+// 点击确定隐藏alert
+export function confirm(onClose) {
+  if (onClose) {
+    onClose()
+  }
+}
+
+// 处理确定按钮点击
+export function handleConfirm(onClose) {
+  confirm(onClose)
+}
+
+// 默认标题
+export const defaultTitle = '提示'
+
+// 默认内容
+export const defaultContent = '这是一个提示'
+
+// 全局 hintView API 对象
+const hintViewAPI = {
+  _show: false,
+  _content: defaultContent,
+  _title: defaultTitle,
+  _setShowCallback: null,
+  _timeoutId: null,
+
+  show() {
+    this._show = true
+    if (this._setShowCallback) {
+      this._setShowCallback(true)
+    }
+    
+    // 清除之前的定时器
+    if (this._timeoutId) {
+      clearTimeout(this._timeoutId)
+    }
+    
+    // 1秒后自动关闭
+    this._timeoutId = setTimeout(() => {
+      this.hide()
+    }, 1000)
+  },
+
+  hide() {
+    this._show = false
+    if (this._setShowCallback) {
+      this._setShowCallback(false)
+    }
+    
+    // 清除定时器
+    if (this._timeoutId) {
+      clearTimeout(this._timeoutId)
+      this._timeoutId = null
+    }
+  },
+
+  setContent(content) {
+    this._content = content
+  },
+
+  setTitle(title) {
+    this._title = title
+  },
+
+  setShowCallback(callback) {
+    this._setShowCallback = callback
+  }
+}
+
+export default hintViewAPI

+ 24 - 0
src/page/public/hint-view/hint-view.jsx

@@ -0,0 +1,24 @@
+import React from 'react'
+import './hint-view.scss'
+import { handleConfirm, defaultTitle, defaultContent } from './hint-view.js'
+
+function HintView({ 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>
+    </div>
+  )
+}
+
+export default HintView

+ 94 - 0
src/page/public/hint-view/hint-view.scss

@@ -0,0 +1,94 @@
+// ========== 颜色配置 ==========
+$white: #fff;
+$black: #000;
+
+// ========== 尺寸配置 ==========
+$full-size: 100%;
+
+// ========== 样式定义 ==========
+.alert-container {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: $full-size;
+  height: $full-size;
+  z-index: 9999;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+
+  .alert-bg {
+    width: 50%;
+    height: 30%;
+    background-color: $white;
+    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: $full-size;
+      height: $full-size;
+      /* em单位精确相对于父元素.alert-bg的字体大小 */
+      font-size: 2.5em;
+      font-weight: bold;
+      color: $black;
+      display: flex;
+      flex-direction: row;
+      justify-content: center;
+      align-items: center;
+    }
+
+    .content {
+      width: $full-size;
+      height: $full-size;
+      display: flex;
+      flex-direction: row;
+      justify-content: center;
+      align-items: center;
+      /* em单位精确相对于父元素.alert-bg的字体大小 */
+      font-size: 1.5em;
+      color: $black;
+    }
+
+    .footer {
+      width: $full-size;
+      height: $full-size;
+      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;
+        cursor: pointer;
+        transition: all 0.3s;
+        
+        &:hover {
+          background-color: rgba(0, 0, 0, 0.05);
+          transform: scale(1.05);
+        }
+        
+        &:active {
+          transform: scale(0.95);
+        }
+      }
+    }
+  }
+}

+ 15 - 0
src/page/public/style/style.scss

@@ -79,4 +79,19 @@
     overflow: hidden;
     margin: 0;
     padding: 0;
+  }
+
+
+  @mixin div-btn-hover-effect {
+    transition: transform 0.2s ease;
+    &:hover {
+        transform: scale(1.05);
+    }
+  }
+
+  @mixin div-btn-pressed-effect {
+    transition: transform 0.1s ease;
+    &:active {
+        transform: scale(0.95);
+    }
   }