news 2026/6/23 19:39:00

鸿蒙 Electron 深度整合:从桌面应用到鸿蒙全场景的进阶实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙 Electron 深度整合:从桌面应用到鸿蒙全场景的进阶实践

开发者还需要面对鸿蒙分布式能力的深度调用Electron 与鸿蒙的数据双向同步跨端权限管理等进阶问题。本文将聚焦这些核心痛点,通过实战代码案例,展示鸿蒙 Electron 整合的进阶玩法,帮助开发者打造真正的全场景跨端应用。

一、进阶整合的核心场景与技术选型

1. 核心场景定位

相较于基础的消息交互,进阶整合主要面向以下场景:

  • 分布式数据共享:Electron 桌面应用与鸿蒙设备共享同一套业务数据(如用户配置、任务列表),实现数据一处修改,多端实时同步。
  • 鸿蒙原生能力调用:在 Electron 中直接调用鸿蒙设备的原生能力(如相机、定位、设备信息获取)。
  • 跨端事件联动:Electron 触发鸿蒙设备的事件(如鸿蒙智慧屏弹窗、穿戴设备震动),反之亦然。

2. 技术选型升级

在基础案例的 WebSocket 之上,我们引入更成熟的技术栈解决进阶问题:

  • 鸿蒙侧:使用鸿蒙分布式数据服务(DDS)实现数据同步,鸿蒙 RPC 远程调用实现跨设备能力调用。
  • Electron 侧:使用MQTT 协议替代 WebSocket 实现更稳定的消息推送,结合鸿蒙 SDK for Node.js直接调用鸿蒙设备接口。
  • 数据同步:使用JSON Schema规范数据格式,结合增量更新减少数据传输开销。

二、环境准备:新增依赖与配置

在基础环境的基础上,我们需要安装以下新增依赖:

1. Electron 侧新增依赖

bash

运行

# MQTT客户端(替代WebSocket,更适合物联网场景) npm install mqtt --save # 鸿蒙Node.js SDK(模拟调用鸿蒙设备能力,实际项目需替换为官方SDK) npm install @harmonyos/node-sdk --save-dev # 数据序列化与增量更新 npm install jsondiffpatch --save

2. 鸿蒙侧新增配置

entry/src/main/module.json5中添加分布式数据服务权限:

json5

{ "module": { // 原有配置... "requestPermissions": [ { "name": "ohos.permission.INTERNET" }, { "name": "ohos.permission.DISTRIBUTED_DATASYNC" // 分布式数据同步权限 }, { "name": "ohos.permission.GET_NETWORK_INFO" // 网络信息权限 } ], "distributedConfiguration": { // 开启分布式能力 "deviceCommunication": true, "dataSync": true } } }

三、代码案例:分布式数据同步与跨端能力调用

本案例实现两个核心功能:

  1. 分布式任务列表同步:Electron 与鸿蒙设备的任务列表实时增量同步。
  2. 跨端能力调用:Electron 调用鸿蒙设备的相机能力,鸿蒙设备触发 Electron 的弹窗事件。

场景 1:分布式任务列表同步(增量更新)

1.1 鸿蒙侧:分布式数据服务(DDS)实现数据存储与同步

鸿蒙侧使用分布式 KV 存储存储任务列表,并通过 MQTT 将增量数据推送给 Electron。

typescript

运行

// entry/src/main/ets/utils/DistributedData.ts import distributedKVStore from '@ohos.data.distributedKVStore'; import { BusinessError } from '@ohos.base'; import mqtt from '@ohos/mqtt'; // 鸿蒙MQTT客户端(需安装对应依赖) // 初始化分布式KV存储 let kvStore: distributedKVStore.KVStore | null = null; // MQTT客户端实例 let mqttClient: mqtt.MqttClient | null = null; // 任务数据结构 export interface Task { id: string; content: string; completed: boolean; updateTime: number; } // 初始化分布式存储 export async function initKVStore() { try { const kvManagerConfig = { context: getContext(), bundleName: 'com.example.harmonyelectron' // 替换为你的应用包名 }; const kvManager = distributedKVStore.createKVManager(kvManagerConfig); const kvStoreConfig = { storeId: 'task_list_store', securityLevel: distributedKVStore.SecurityLevel.S1, encrypt: false }; kvStore = await kvManager.getKVStore(kvStoreConfig); console.log('分布式KV存储初始化成功'); // 监听数据变化 subscribeKVStoreChange(); // 连接MQTT服务器(Electron端启动的MQTT broker) connectMqtt(); } catch (error) { console.error('初始化KV存储失败:', error as BusinessError); } } // 监听KV存储数据变化 function subscribeKVStoreChange() { if (!kvStore) return; kvStore.on('dataChange', (data) => { console.log('数据变化:', data); // 读取变化后的任务列表 getTaskList().then((taskList) => { // 推送增量数据到Electron publishMqttMessage('task/list/change', JSON.stringify(taskList)); }); }); } // 连接MQTT服务器 function connectMqtt() { const options = { url: 'mqtt://192.168.1.100:1883', // Electron的MQTT broker地址 clientId: `harmony-device-${Math.random().toString(36).substr(2, 9)}`, username: 'harmony', password: '123456', keepalive: 60 }; mqttClient = mqtt.connect(options); mqttClient.on('connect', () => { console.log('MQTT连接成功'); // 订阅Electron的任务更新主题 mqttClient?.subscribe('task/list/update'); }); // 接收Electron的任务更新消息 mqttClient?.on('message', (topic, payload) => { if (topic === 'task/list/update') { const newTask: Task = JSON.parse(payload.toString()); // 更新分布式存储 updateTask(newTask); } }); } // 发布MQTT消息 function publishMqttMessage(topic: string, payload: string) { if (mqttClient && mqttClient.connected) { mqttClient.publish(topic, payload, { qos: 1 }); } } // 读取任务列表 export async function getTaskList(): Promise<Task[]> { if (!kvStore) return []; try { const entries = await kvStore.getEntries('task_'); const taskList: Task[] = []; entries.forEach((entry) => { taskList.push(JSON.parse(entry.value as string)); }); // 按更新时间排序 return taskList.sort((a, b) => b.updateTime - a.updateTime); } catch (error) { console.error('读取任务列表失败:', error as BusinessError); return []; } } // 更新任务(新增/修改) export async function updateTask(task: Task) { if (!kvStore) return; try { await kvStore.put(`task_${task.id}`, JSON.stringify(task)); console.log('任务更新成功:', task.id); } catch (error) { console.error('更新任务失败:', error as BusinessError); } } // 删除任务 export async function deleteTask(taskId: string) { if (!kvStore) return; try { await kvStore.delete(`task_${taskId}`); console.log('任务删除成功:', taskId); } catch (error) { console.error('删除任务失败:', error as BusinessError); } }
1.2 鸿蒙侧页面:任务列表展示与操作

typescript

运行

// entry/src/main/ets/pages/TaskPage.ets import { initKVStore, getTaskList, updateTask, deleteTask, Task } from '../utils/DistributedData'; import { uuid } from '../utils/UUID'; // 自定义UUID工具类 @Entry @Component struct TaskPage { @State taskList: Task[] = []; @State newTaskContent: string = ''; aboutToAppear() { // 初始化分布式存储 initKVStore().then(() => { // 加载任务列表 this.loadTaskList(); }); } // 加载任务列表 async loadTaskList() { this.taskList = await getTaskList(); } // 添加新任务 async addTask() { if (!this.newTaskContent.trim()) return; const newTask: Task = { id: uuid(), content: this.newTaskContent.trim(), completed: false, updateTime: Date.now() }; await updateTask(newTask); this.newTaskContent = ''; this.loadTaskList(); // 重新加载列表 } // 切换任务完成状态 async toggleTaskStatus(task: Task) { const updatedTask = { ...task, completed: !task.completed, updateTime: Date.now() }; await updateTask(updatedTask); this.loadTaskList(); } // 删除任务 async removeTask(taskId: string) { await deleteTask(taskId); this.loadTaskList(); } build() { Column() { // 新增任务输入框 Row() { Input({ placeholder: '请输入新任务...', value: this.newTaskContent }) .onChange((value) => { this.newTaskContent = value; }) .flexGrow(1) .margin(10); Button('添加') .onClick(() => this.addTask()) .margin(10); } .width('100%') .padding(10); // 任务列表 List() { ForEach(this.taskList, (task) => { ListItem() { Row() { Checkbox() .checked(task.completed) .onChange(() => this.toggleTaskStatus(task)) .margin(10); Text(task.content) .fontSize(18) .textDecoration(task.completed ? TextDecorationType.LineThrough : TextDecorationType.None) .flexGrow(1); Button('删除') .onClick(() => this.removeTask(task.id)) .backgroundColor(Color.Red) .margin(10); } .width('100%') .padding(5) .borderBottom(1, Color.Grey); } }); } .width('100%') .flexGrow(1); } .width('100%') .height('100%') .backgroundColor(Color.White); } }
1.3 Electron 侧:MQTT 服务与任务列表增量同步

Electron 端启动本地 MQTT broker(使用mosca),接收鸿蒙设备的任务增量数据,并实现本地数据与鸿蒙设备的同步。

1.3.1 Electron 主进程:MQTT 服务与数据管理

javascript

运行

// main.js(新增部分) const mosca = require('mosca'); const jsondiffpatch = require('jsondiffpatch'); const fs = require('fs'); const path = require('path'); // 本地任务数据存储路径 const TASK_DATA_PATH = path.join(app.getPath('userData'), 'tasks.json'); // 任务列表数据 let taskList = []; // MQTT服务器实例 let mqttServer; // 初始化本地任务数据 function initTaskData() { try { if (fs.existsSync(TASK_DATA_PATH)) { taskList = JSON.parse(fs.readFileSync(TASK_DATA_PATH, 'utf8')); } else { fs.writeFileSync(TASK_DATA_PATH, JSON.stringify([]), 'utf8'); } } catch (error) { console.error('初始化任务数据失败:', error); taskList = []; } } // 保存任务数据到本地 function saveTaskData() { try { fs.writeFileSync(TASK_DATA_PATH, JSON.stringify(taskList), 'utf8'); } catch (error) { console.error('保存任务数据失败:', error); } } // 增量更新任务列表 function updateTaskList(newTaskList) { const diff = jsondiffpatch.diff(taskList, newTaskList); if (diff) { taskList = [...newTaskList]; saveTaskData(); // 通知渲染进程更新UI mainWindow.webContents.send('task-list-update', taskList, diff); } } // 启动MQTT服务器 function startMqttServer() { mqttServer = new mosca.Server({ port: 1883 }); console.log('MQTT服务器已启动,端口:1883'); mqttServer.on('clientConnected', (client) => { console.log('客户端连接:', client.id); // 连接成功后,推送当前任务列表到鸿蒙设备 client.publish({ topic: 'task/list/init', payload: JSON.stringify(taskList), qos: 1 }); }); mqttServer.on('published', (packet, client) => { if (!client) return; const topic = packet.topic; const payload = packet.payload?.toString() || ''; // 接收鸿蒙设备的任务列表变化 if (topic === 'task/list/change') { const newTaskList = JSON.parse(payload); updateTaskList(newTaskList); } // 接收鸿蒙设备的跨端事件 if (topic === 'cross/device/event') { const event = JSON.parse(payload); handleCrossDeviceEvent(event); } }); mqttServer.on('error', (error) => { console.error('MQTT服务器错误:', error); }); } // 处理跨设备事件(如鸿蒙触发Electron弹窗) function handleCrossDeviceEvent(event) { switch (event.type) { case 'showDialog': mainWindow.webContents.send('show-dialog', event.data); break; case 'playSound': mainWindow.webContents.send('play-sound', event.data); break; default: console.log('未知事件类型:', event.type); } } // 在app.whenReady中添加初始化 app.whenReady().then(() => { createWindow(); initTaskData(); // 新增:初始化任务数据 startMqttServer(); // 新增:启动MQTT服务器 // 原有代码... // 新增:监听渲染进程的任务更新请求 ipcMain.on('update-task', (event, task) => { // 推送任务更新到鸿蒙设备 mqttServer.publish({ topic: 'task/list/update', payload: JSON.stringify(task), qos: 1 }); // 更新本地任务列表 const index = taskList.findIndex((t) => t.id === task.id); if (index > -1) { taskList[index] = task; } else { taskList.push(task); } saveTaskData(); event.reply('task-updated', true); }); // 新增:监听渲染进程的跨端能力调用请求 ipcMain.on('call-harmony-api', (event, apiName, params) => { // 推送能力调用请求到鸿蒙设备 mqttServer.publish({ topic: 'cross/device/api', payload: JSON.stringify({ apiName, params }), qos: 1 }); // 监听鸿蒙设备的响应 mqttServer.once('published', (packet) => { if (packet.topic === 'cross/device/api/response') { const response = JSON.parse(packet.payload.toString()); event.reply('harmony-api-response', response); } }); }); });
1.3.2 Electron 渲染进程:任务列表 UI 与跨端交互

html

预览

<!-- index.html(替换为任务列表页面) --> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>鸿蒙Electron分布式任务列表</title> <style> /* 简化样式,实际项目可按需美化 */ body { font-family: Arial, sans-serif; padding: 20px; background-color: #f5f5f5; } .container { max-width: 800px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); } .task-input { display: flex; margin-bottom: 20px; } #taskContent { flex-grow: 1; padding: 10px; font-size: 16px; border: 1px solid #ccc; border-radius: 4px; } #addTaskBtn { padding: 10px 20px; margin-left: 10px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; } #taskList { list-style: none; padding: 0; } .task-item { display: flex; align-items: center; padding: 10px; border-bottom: 1px solid #eee; } .task-item input[type="checkbox"] { margin-right: 10px; } .task-item .task-content { flex-grow: 1; } .task-item .task-content.completed { text-decoration: line-through; color: #999; } .task-item .delete-btn { padding: 5px 10px; background-color: #dc3545; color: white; border: none; border-radius: 4px; cursor: pointer; } .api-call-btn { margin-top: 20px; padding: 10px 20px; background-color: #28a745; color: white; border: none; border-radius: 4px; cursor: pointer; } </style> </head> <body> <div class="container"> <h1>分布式任务列表(Electron ↔ 鸿蒙)</h1> <div class="task-input"> <input type="text" id="taskContent" placeholder="请输入任务内容..."> <button id="addTaskBtn">添加任务</button> </div> <ul id="taskList"></ul> <button class="api-call-btn" id="callCameraBtn">调用鸿蒙设备相机</button> <button class="api-call-btn" id="triggerHarmonyEventBtn">触发鸿蒙设备震动</button> </div> <script> const taskContent = document.getElementById('taskContent'); const addTaskBtn = document.getElementById('addTaskBtn'); const taskList = document.getElementById('taskList'); const callCameraBtn = document.getElementById('callCameraBtn'); const triggerHarmonyEventBtn = document.getElementById('triggerHarmonyEventBtn'); // 渲染任务列表 function renderTaskList(tasks) { taskList.innerHTML = ''; tasks.forEach(task => { const li = document.createElement('li'); li.className = 'task-item'; li.innerHTML = ` <input type="checkbox" ${task.completed ? 'checked' : ''} data-id="${task.id}"> <div class="task-content ${task.completed ? 'completed' : ''}">${task.content}</div> <button class="delete-btn" data-id="${task.id}">删除</button> `; taskList.appendChild(li); }); // 绑定复选框事件 document.querySelectorAll('.task-item input[type="checkbox"]').forEach(checkbox => { checkbox.addEventListener('change', (e) => { const taskId = e.target.dataset.id; const task = tasks.find(t => t.id === taskId); if (task) { const updatedTask = { ...task, completed: e.target.checked, updateTime: Date.now() }; window.electronAPI.updateTask(updatedTask); } }); }); // 绑定删除按钮事件 document.querySelectorAll('.task-item .delete-btn').forEach(btn => { btn.addEventListener('click', (e) => { const taskId = e.target.dataset.id; const updatedTasks = tasks.filter(t => t.id !== taskId); // 推送删除后的列表到鸿蒙(实际项目可单独写删除接口) window.electronAPI.updateTask({ id: taskId, isDelete: true }); renderTaskList(updatedTasks); }); }); } // 添加任务 addTaskBtn.addEventListener('click', () => { const content = taskContent.value.trim(); if (!content) return; const newTask = { id: Math.random().toString(36).substr(2, 9), content, completed: false, updateTime: Date.now() }; window.electronAPI.updateTask(newTask); taskContent.value = ''; }); // 调用鸿蒙相机能力 callCameraBtn.addEventListener('click', () => { window.electronAPI.callHarmonyApi('camera.takePhoto', { quality: 'high' }, (response) => { alert(`鸿蒙相机调用结果:${JSON.stringify(response)}`); }); }); // 触发鸿蒙设备震动 triggerHarmonyEventBtn.addEventListener('click', () => { window.electronAPI.callHarmonyApi('device.vibrate', { duration: 1000 }, (response) => { alert(`鸿蒙震动触发结果:${JSON.stringify(response)}`); }); }); // 监听主进程的任务列表更新 window.electronAPI.onTaskListUpdate((tasks) => { renderTaskList(tasks); }); // 监听主进程的弹窗事件 window.electronAPI.onShowDialog((data) => { alert(`鸿蒙触发弹窗:${data.message}`); }); // 初始化时请求任务列表 window.electronAPI.getTaskList((tasks) => { renderTaskList(tasks); }); </script> </body> </html>

场景 2:跨端能力调用(Electron 调用鸿蒙相机)

2.1 鸿蒙侧:RPC 远程调用处理相机能力

typescript

运行

// entry/src/main/ets/utils/RpcServer.ts import rpc from '@ohos.rpc'; import camera from '@ohos.camera'; import mqtt from '@ohos/mqtt'; // RPC服务端实现 class HarmonyApiServer extends rpc.RemoteObject { constructor(descriptor: string) { super(descriptor); } // 处理远程调用请求 onRemoteRequest(code: number, data: rpc.MessageParcel, reply: rpc.MessageParcel, option: rpc.MessageOption): boolean { switch (code) { case 1: // 相机拍照 const quality = data.readString(); const photoPath = this.takePhoto(quality); reply.writeString(JSON.stringify({ success: true, path: photoPath })); break; case 2: // 设备震动 const duration = data.readInt32(); this.vibrate(duration); reply.writeString(JSON.stringify({ success: true })); break; default: reply.writeString(JSON.stringify({ success: false, error: '未知接口' })); break; } return true; } // 拍照方法 private takePhoto(quality: string): string { // 简化实现,实际项目需调用相机原生API const cameraManager = camera.getCameraManager(getContext()); const cameraDevices = cameraManager.getSupportedCameras(); if (cameraDevices.length === 0) { return ''; } // 模拟拍照,返回路径 return `/sdcard/photo_${Date.now()}.jpg`; } // 震动方法 private vibrate(duration: number): void { // 调用鸿蒙震动API const vibrator = require('@ohos.vibrator'); vibrator.vibrate({ type: 'time', duration: duration }).catch((error) => { console.error('震动失败:', error); }); } } // 启动RPC服务并监听MQTT的API调用请求 export function startRpcServer() { const rpcServer = new HarmonyApiServer('harmony.api.server'); // 注册RPC服务(实际项目需通过分布式能力发布服务) rpc.registerRpcService('harmony.api', rpcServer); // 监听MQTT的API调用请求 mqttClient?.on('message', (topic, payload) => { if (topic === 'cross/device/api') { const { apiName, params } = JSON.parse(payload.toString()); // 处理API调用 let response; if (apiName === 'camera.takePhoto') { response = { success: true, path: `/sdcard/photo_${Date.now()}.jpg` }; } else if (apiName === 'device.vibrate') { this.vibrate(params.duration); response = { success: true }; } else { response = { success: false, error: '未知API' }; } // 回复Electron mqttClient?.publish('cross/device/api/response', JSON.stringify(response), { qos: 1 }); } }); }

四、运行与测试要点

  1. MQTT 服务器启动:确保 Electron 的 MQTT 服务器在 1883 端口正常启动,鸿蒙设备与 Electron 在同一局域网。
  2. 分布式权限配置:鸿蒙设备需开启分布式数据同步权限,在设置中允许应用的分布式能力。
  3. 增量数据同步测试:在 Electron 或鸿蒙设备中添加 / 修改 / 删除任务,观察另一端是否实时同步。
  4. 跨端能力调用测试:点击 Electron 的 “调用鸿蒙设备相机” 按钮,观察鸿蒙设备是否触发相机功能,并返回结果。

五、避坑指南与优化建议

1. 常见坑点

  • 分布式权限问题:鸿蒙的分布式数据同步需要申请DISTRIBUTED_DATASYNC权限,且设备需登录同一华为账号。
  • MQTT 断连重连:网络波动时需实现 MQTT 的自动重连机制,避免数据丢失。
  • 数据冲突处理:多端同时修改同一数据时,需通过时间戳或版本号解决冲突。

2. 优化建议

  • 数据压缩:使用gzip压缩增量数据,减少网络传输开销。
  • 离线同步:添加离线数据缓存,设备联网后自动同步未上传的数据。
  • 权限校验:跨端能力调用时添加身份校验,避免恶意设备调用。

六、总结

本文通过分布式任务列表同步和跨端能力调用两个核心案例,展示了鸿蒙 Electron 整合的进阶实践。相较于基础的消息交互,进阶整合更注重数据的一致性能力的互通性,这也是打造全场景应用的关键。

随着鸿蒙系统的持续迭代,以及 Electron 对跨平台支持的不断优化,两者的融合将覆盖更多场景:从桌面应用与鸿蒙智能设备的联动,到 Electron 应用直接部署到鸿蒙桌面版。开发者可以基于本文的思路,结合实际业务需求,探索更多跨端整合的可能性,真正实现 “一次开发,全场景部署” 的目的

欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/22 13:17:24

树莓派家庭服务器搭建指南从零到实用

本文详解如何将树莓派打造成家庭服务器,运行各种实用服务,并实现远程访问。 前言 想搭建家庭服务器,但又觉得NAS太贵、旧电脑功耗太高? 树莓派是一个很好的选择: 价格便宜(几百块) 功耗超低(5-10W) 体积小巧(手掌大小) 性能够用(日常服务绑绑有余) 今天就来把树…

作者头像 李华
网站建设 2026/6/23 7:09:11

黑客大神都会玩这 10 个 Linux 命令,我不允许你还不知道!

Linux当中有很多比较有趣的命令&#xff0c;可以动手看看&#xff0c;很简单的。 1.rev命令 一行接一行地颠倒所输入的字符串。 运行&#xff1a; $rev 如输入&#xff1a;shiyanlou shiyanlou 2.asciiview命令 1.先安装aview $sudo apt-get install aview 2.再安装im…

作者头像 李华
网站建设 2026/6/23 17:12:35

Wi-Fi CERTIFIED Data Elements™ 技术概述

引言 在住宅网络中,Wi-Fi 是占据主导地位的技术 。由于对互联设备的日益依赖,所以服务提供商确信有必要按照需求,在确保网络高效率运行的同时,提升 Wi-Fi 的服务质量。Wi-Fi CERTIFIED Data Elements™是 Wi-Fi Alliance 的一项认证计划,为 Wi-Fi 网络提供了一套标准化的…

作者头像 李华
网站建设 2026/6/23 15:25:04

基于YOLO的小目标检测增强:一种提升精度与效率的新框架

摘要 本文研究并开发了在大规模航拍图像中检测小目标的方法。当前航拍图像中的小目标检测方法通常涉及图像裁剪和检测器网络架构的修改。滑动窗口裁剪以及包括更高分辨率特征图和注意力机制在内的架构增强技术是常用的方法。鉴于航拍图像在各种关键和工业应用中的重要性日益增长…

作者头像 李华
网站建设 2026/6/18 11:07:15

stm32编码总结

总结&#xff1a; 一个工程里&#xff0c;同时有GBK、UTF-8两种编码的文件 &#xff1b; Keil编码使用GB2312 , 令编译时把汉字解释为两字节&#xff0c;以兼容目前的开发周边; 不要在生成的文件如main.c里头写中文&#xff0c;在用户文件实现中文。 1、用户文件&#xff1a;使…

作者头像 李华