Electron-vite【实战】MD 编辑器 -- 系统菜单(含菜单封装,新建文件,打开文件,打开文件夹,保存文件,退出系统)

最终效果

在这里插入图片描述

整体架构

src/main/index.ts

import { createMenu } from './menu'

在 const mainWindow 后

  // 加载菜单createMenu(mainWindow)

src/main/menu.ts

import { BrowserWindow, Menu, MenuItem, MenuItemConstructorOptions, dialog, shell } from 'electron'
import fs from 'fs/promises'
import path from 'path'
import { FileItem } from '../types'
// 系统菜单
const createMenu = (mainWindow: BrowserWindow): void => {const menuTemplate: (MenuItemConstructorOptions | MenuItem)[] = [{label: '文件',submenu: []}]const menu: Menu = Menu.buildFromTemplate(menuTemplate)Menu.setApplicationMenu(menu)
}

submenu 内添加自定义的菜单

src/types.ts

export interface FileItem {content: stringfileName: stringfilePath: stringeditable?: boolean
}

新建文件

在这里插入图片描述

src/main/menu.ts

        {label: '新建',accelerator: 'CmdOrCtrl+N',click: async () => {const { canceled, filePath } = await dialog.showSaveDialog({filters: [{name: 'Markdown Files',extensions: ['md']}]})if (!canceled) {try {await fs.writeFile(filePath, '')mainWindow.webContents.send('open-file', {content: '',filePath: filePath,fileName: path.basename(filePath)})} catch (error) {console.error('创建文件时出错:', error)}}}},

src/renderer/src/App.vue

  window.electron.ipcRenderer.on('open-file', (_, { content, fileName, filePath }) => {markdownContent.value = contentcurrentFilePath.value = filePathif (!isFileExists(filePath)) {fileList.value.unshift({content,fileName,filePath})}})

打开文件

在这里插入图片描述

src/main/menu.ts

        {label: '打开文件',accelerator: 'CmdOrCtrl+O',click: async () => {const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, {filters: [{ name: 'Markdown Files', extensions: ['md', 'markdown'] }],properties: ['openFile']})if (!canceled) {const content = await fs.readFile(filePaths[0], 'utf-8')mainWindow.webContents.send('open-file', {content,filePath: filePaths[0],fileName: path.basename(filePaths[0])})}return null}},

src/renderer/src/App.vue

  window.electron.ipcRenderer.on('open-file', (_, { content, fileName, filePath }) => {markdownContent.value = contentcurrentFilePath.value = filePathif (!isFileExists(filePath)) {fileList.value.unshift({content,fileName,filePath})}})

打开文件夹

src/main/menu.ts

        {label: '打开文件夹',accelerator: 'CmdOrCtrl+K',click: async () => {const { canceled, filePaths } = await dialog.showOpenDialog(mainWindow, {properties: ['openDirectory']})if (!canceled) {const folderPath = filePaths[0]try {const files = await fs.readdir(folderPath)const mdFiles = files.filter((file) =>['.md', '.markdown'].includes(path.extname(file)))const fileList: FileItem[] = []for (const mdFile of mdFiles) {const filePath = path.join(folderPath, mdFile)const content = await fs.readFile(filePath, 'utf-8')fileList.push({content,filePath,fileName: mdFile})}mainWindow.webContents.send('open-dir', fileList)mainWindow.webContents.send('open-file', fileList[0])} catch (error) {console.error('读取文件夹失败:', error)}}return null}},

src/renderer/src/App.vue

  window.electron.ipcRenderer.on('open-dir', (_, newFileList) => {// 使用 splice 方法更新数组fileList.value.splice(0, fileList.value.length, ...newFileList)})
  window.electron.ipcRenderer.on('open-file', (_, { content, fileName, filePath }) => {markdownContent.value = contentcurrentFilePath.value = filePathif (!isFileExists(filePath)) {fileList.value.unshift({content,fileName,filePath})}})

保存

src/main/menu.ts

        {label: '保存',accelerator: 'CmdOrCtrl+S',click: () => {mainWindow.webContents.send('save-file')}},

src/renderer/src/App.vue

  window.electron.ipcRenderer.on('save-file', () => {const content = markdownContent.valueif (currentFilePath.value) {// 存在文件路径时,保存文件const filePath = currentFilePath.value// 更新文件列表内容fileList.value.forEach((file) => {if (file.filePath === filePath) {file.content = content}})window.electron.ipcRenderer.send('save-file', { content, filePath })} else {// 无文件路径时,新建文件window.electron.ipcRenderer.send('new-file', content)}})

src/main/ipc.ts

  // 处理新建文件请求ipcMain.on('new-file', async (_e, content) => {const { canceled, filePath } = await dialog.showSaveDialog({filters: [{name: 'Markdown Files',extensions: ['md']}]})if (!canceled) {try {await fs.writeFile(filePath, content)mainWindow.webContents.send('open-file', {content: content,filePath: filePath,fileName: path.basename(filePath)})} catch (error) {console.error('创建文件时出错:', error)}}})// 处理保存文件请求ipcMain.on('save-file', async (_e, data) => {try {await fs.writeFile(data.filePath, data.content, 'utf-8')} catch (error) {console.error('保存文件失败:', error)}})

ipc.ts 的架构

src/main/index.ts

import { setupIPC } from './ipc'
setupIPC(mainWindow)

src/main/ipc.ts

import { ipcMain, BrowserWindow, shell, dialog } from 'electron'
import fs from 'fs/promises'
import path from 'path'
import { createContextMenu } from './menu'
export function setupIPC(mainWindow: BrowserWindow): void {// IPC相关代码
}

退出

src/main/menu.ts

        {label: '退出',role: 'quit'}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/907521.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【第4章 图像与视频】4.5 操作图像的像素

文章目录 前言示例-获取和修改图像数据图像数据的遍历方式图像滤镜负片滤镜黑白滤镜浮雕滤镜filter滤镜属性 前言 getImageData() 与 putImageData() 这两个方法分别用来获取图像的像素信息,以及向图像中插入像素。与此同时,如果有需要,也可…

【Docker 从入门到实战全攻略(一):核心概念 + 命令详解 + 部署案例】

1. 是什么 Docker 是一个用于开发、部署和运行应用程序的开源平台,它使用 容器化技术 将应用及其依赖打包成独立的容器,确保应用在不同环境中一致运行。 2. Docker与虚拟机 2.1 Docker(容器化) 容器化是一种轻量级的虚拟化技术…

Vue:axios(POST请求)

发送 POST 请求 基本用法 axios.post(/api/login, {username: lcyyyy,password: 123456 }) .then(response > {console.log(请求成功:, response.data); }) .catch(error > {console.error(请求失败:, error); });在 Vue 组件中使用 export default {methods: {async …

一周学会Pandas2之Python数据处理与分析-数据重塑与透视-unstack() - 解堆 (行 -> 列)

锋哥原创的Pandas2 Python数据处理与分析 视频教程: 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili unstack() 是 pandas 中用于数据重塑的重要方法,它与 stack() 互为逆操作。unstack() 的主要功能是将行索…

基于大模型预测的FicatIII-IV期股骨头坏死综合治疗研究报告

目录 一、引言 1.1 研究背景与目的 1.2 国内外研究现状 1.3 研究意义和创新点 二、FicatIII-IV 期股骨头坏死概述 2.1 疾病定义与分期 2.2 病因与病理机制 2.3 临床症状与诊断方法 三、大模型预测原理与方法 3.1 大模型简介 3.2 数据收集与预处理 3.3 模型训练与优…

C++?多态!!!

一、引言 众所周知,C有三大特性,它们分别是封装、继承和多态,在之前的文章中已经详细介绍过封装和继承了,今天我们将一起学习多态相关的知识,如果还想了解封装、继承相关的知识,可以跳转到以下链接&#xf…

electron安装报错处理

electron安装报错 解决方法: 修改 C:\Users\用户名.npmrc下配置文件 添加代码 electron_mirrorhttps://cdn.npmmirror.com/binaries/electron/ electron_builder_binaries_mirrorhttps://npmmirror.com/mirrors/electron-builder-binaries/最后代码 registryhtt…

Windows10下使用QEMU安装Ubuntu20.04虚拟机,并启用硬件加速

Windows10下使用QEMU安装Ubuntu20.04虚拟机,并启用硬件加速 作者将狼才鲸创建日期2025-05-30 CSDN阅读地址:Windows10下使用QEMU安装Ubuntu20.04虚拟机,并启用硬件加速 本文档源码地址:Windows10下使用QEMU安装Ubuntu20.04虚拟机…

顶刊SCS | 基于视觉语言大模型推理分割的建筑足迹尺度功能分类, 样本数据和代码已开源!

论文介绍 题目:Visual-language reasoning segmentation (LARSE) of function-level building footprint across Yangtze River Economic Belt of China 期刊:Sustainable cities and society(中科院一区TOP,IF10.5)…

【软件】navicat 官方免费版

Navicat Premium Lite https://www.navicat.com.cn/download/navicat-premium-lite

每个路由器接口,都必须分配所属网络内的 IP 地址,用于转发数据包

在IP网络中,主机(Host)和路由器接口(Router Interface)都需要分配网络地址(IP地址)。 1. 主机(Host)的IP地址分配 (1) 作用 主机的IP地址用于唯一标识该设备&#xff0…

鸿蒙OSUniApp页面切换动效实战:打造流畅精致的转场体验#三方框架 #Uniapp

UniApp页面切换动效实战:打造流畅精致的转场体验 引言 在移动应用开发中,页面切换动效不仅能提升用户体验,还能传达应用的品质感。随着HarmonyOS的普及,用户对应用的动效体验要求越来越高。本文将深入探讨如何在UniApp中实现流畅…

Tesseract OCR 安装与中文+英文识别实现

一、下载 https://digi.bib.uni-mannheim.de/tesseract/ 下载,尽量选择时间靠前的(识别更好些)。符合你的运行机(我的是windows64) 持续点击下一步安装,安装你认可的路径即可,没必要配置环境变…

Visual Studio 2022 发布独立的 exe 文件

我们在用 Visual Studio 2022 写好一个 exe 程序之后,如果想把这个拿到其他地方运行,需要把 exe 所在的文件夹一起拿过去。 编译出来的 exe 文件需要其他几个文件一同放在同一目录才能运行,原因在于默认情况下,Visual Studio 是把…

Kotlin-特殊类型

文章目录 数据类型枚举类型匿名类和伴生对象单例类伴生对象 数据类型 声明一个数据类非常简单: //在class前面添加data关键字表示为一个数据类 data class Student(var name: String, var age: Int)数据类声明后,编译器会根据主构造函数中声明的所有属性自动为其生成以下函数…

在线博客系统【测试报告】

🕒 一. 项目背景 由于纸质笔记容易丢失,携带不变,为了方便自己学习的过程中记录笔记,特开发了这个博客系统。这个系统后端采用 SpringBoot MyBatis SpringMVC ;前端使用Html CSS JS;数据库使用的是Mysq…

每日刷题c++

快速幂 #include <iostream> using namespace std; #define int long long int power(int a, int b, int p) {int ans 1;while (b){if (b % 2){ans * a;ans % p; // 随时取模}a * a;a % p; // 随时取模b / 2;}return ans; } signed main() {int a, b, p;cin >> a …

Python中的变量、赋值及函数的参数传递概要

Python中的变量、赋值及函数的参数传递概要 python中的变量、赋值 python中的变量不是盒子。 python中的变量无法用“变量是盒子”做解释。图说明了在 Python 中为什么不能使用盒子比喻&#xff0c;而便利贴则指出了变量的正确工作方式。 如果把变量想象为盒子&#xff0c;那…

KVM 安装 Ubuntu 22

在 KVM 中安装 Ubuntu 22 虚拟机。 首先创建硬盘文件 sudo qemu-img create -f qcow2 /app/vms/ubuntu22.qcow2 100G安装Ubuntu 22 sudo virt-install \--name ubuntu22 \--ram 4096 \--vcpus 2 \--disk path/app/vms/ubuntu22.qcow2,formatqcow2 \--os-type linux \--os-va…

基于生产-消费模式,使用Channel进行文件传输(Tcp方式)

Client端&#xff1a; #region 多文件传输 public class FileMetadata {public string FileName { get; set; }public long FileSize { get; set; } }class Program {const int PORT 8888;const int BUFFER_SIZE 60 * 1024 * 1024;//15s-50 25s-64 33s-32 27s-50 31s-40 25…