# electron_program **Repository Path**: jyeontostore/electron_program ## Basic Information - **Project Name**: electron_program - **Description**: electron程序开发。 - **Primary Language**: JavaScript - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 4 - **Created**: 2023-10-28 - **Last Updated**: 2023-10-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ![gitee图床.gif](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d08d9f274c134efaaa68ca56f330df6a~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=720&h=405&s=13584129&e=gif&f=270&b=f2f2f2) ## 说在前面 > 平时写文章或写代码的时候,都少不了需要将本地图片转成在线图片链接,大家都是使用什么工具进行转换的呢?相信很多人都有自己的图床工具,今天来给大家介绍一下,怎么基于Gitee和Electron来开发一个便捷的图床工具,支持图片的上传、删除、复制和快速生成markdown链接、快捷键唤起和隐藏面板,粘贴剪切板图片上传等…… ## 框架选型 原本只是想写一个Chrome插件来实现简单功能,后面发现Chrome插件的局限性太大了,所以最后还是选择使用`Electron`来制作一个桌面程序。 存储方面我们可以直接使用gitee来用做图库存储,不需要额外去购买存储服务器。 ## 准备工作 ### 一、Gitee创建图床仓库目录 #### 1、Gitee注册 直接到Gitee官网: [Gitee - 基于 Git 的代码托管和研发协作平台](https://gitee.com/) ,点击注册即可。 ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a1ba65660cae4c74a43815425fa772a2~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1329&h=869&s=216705&e=png&b=f9f9f9) #### 2、仓库创建 注册完账号后直接登录,在首页点击右上角的加号可以新建仓库 ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6a94dcec27454e61be739b25a3a51fcd~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1915&h=761&s=126733&e=png&b=fafafb) 仓库信息自行填写即可 ![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2f58fd3fe24240e6afc1c236a534dd14~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1322&h=741&s=59468&e=png&b=ffffff) 创建完仓库后我们可以新建一个文件夹用来存储图片: ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e39ed8ba12eb436bb496ff30612d1cd4~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1181&h=851&s=91604&e=png&b=fefefe) #### 3、生成授权码 打开设置里的私人令牌页面 ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8da102fe74194458a108a6d66300404b~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1446&h=840&s=87144&e=png&b=ffffff) 点击生成新令牌,根据提示填写信息即可,注意保存好生成的令牌。 ![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1257cf48d85044a58f6f89ff4c1b56c2~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1345&h=452&s=48948&e=png&b=fefefe) ### 二、搭建electron项目 我们可以先搭建一个简单的electron项目: 1. 安装 Node.js:确保你的电脑上已经安装了 Node.js。你可以从 Node.js 官方网站([https://nodejs.org](https://nodejs.org/))下载并安装最新版本的 Node.js。 2. 创建项目目录:在你想要创建项目的位置,创建一个新的文件夹作为项目目录。 3. 初始化项目:打开命令行终端,进入到项目目录,并执行以下命令初始化一个新的 npm 项目: ```shell npm init -y ``` 4. 安装 Electron:在命令行终端中执行以下命令来安装 Electron: ```shell npm install electron ``` 5. 创建主文件:在项目目录中创建一个名为 `main.js` 的文件,作为 Electron 应用的主文件。 6. 编写主文件代码:在 `main.js` 文件中编写 Electron 应用的主要逻辑。例如,下面是一个简单的示例: ```javascript const { app, BrowserWindow } = require('electron'); function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true } }); win.loadFile('index.html'); } app.whenReady().then(() => { createWindow(); app.on('activate', function () { if (BrowserWindow.getAllWindows().length === 0) createWindow(); }); }); app.on('window-all-closed', function () { if (process.platform !== 'darwin') app.quit(); }); ``` 7. 创建 HTML 文件:在项目目录中创建一个名为 `index.html` 的文件,作为 Electron 应用的初始页面。 8. 编写 HTML 文件代码:在 `index.html` 文件中编写你的应用界面的 HTML 代码。 9. 在 `package.json` 文件中添加启动命令:打开 `package.json` 文件,在 `"scripts"` 部分添加以下内容: ```json "scripts": { "start": "electron ." } ``` 10. 启动 Electron 应用:在命令行终端中执行以下命令来启动 Electron 应用: ```shell npm start ``` ## 功能实现 前面准备工作全都完成后,现在我们就有了一个简单electron项目架子和一个gitee仓库,可以开始来实现相关的功能了。 ### 一、git操作 gitee提供了api文档,我们可以通过gitee的api文档来对我们的仓库进行上传图片和获取图片的操作。 gitee API文档地址:[https://gitee.com/api/v5/swagger#/getV5ReposOwnerRepoStargazers?ex=no](https://gitee.com/api/v5/swagger#/getV5ReposOwnerRepoStargazers?ex=no) 这里我将需要使用到的功能写成了一个类: #### 1、初始化,获取配置信息 - accessToken 用户授权码,也就是我们前面生成的私人令牌。 ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c6f4972e6f1e425a9f500b522830a307~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1060&h=477&s=24803&e=png&b=ffffff) - username 仓库所属空间地址(企业、组织或个人的地址path),如下图: ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/77d2f26ca25f45258fb9d8dc41f71b6b~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=762&h=375&s=34679&e=png&b=fcfbfb) - repo 仓库路径(path),如下图: ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/696380d244f34be4937606e6206f818b~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1096&h=492&s=54512&e=png&b=fcfcfc) - dirPath 图片存放目录地址,如下图: ![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/32ab1cdefcae4c9b92270b86791fbd2f~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1229&h=382&s=41727&e=png&b=fcfcfc) - branchName 分支名,默认为`master`,我们可以修改成指定分支: ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a77b1f2d9b354253a61c9e9c668ca735~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1339&h=449&s=48215&e=png&b=fdfdfd) ```javascript init(config = {}) { // 设置 Gitee 仓库信息和目录路径 this.username = config.username; this.repo = config.repo; this.accessToken = config.accessToken; this.branchName = config.branchName || "master"; this.apiUrl = "https://gitee.com/api/v5/repos/"; this.dirPath = config.dirPath; } ``` 将以上配置信息在程序的配置里设置好即可: ![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b8b00e4200554492ab658f54cd2468a2~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1144&h=817&s=44556&e=png&b=f3f3f3) #### 2、上传图片到gitee图床目录下 根据API文档进行请求即可: ```javascript async uploadToGitee(base64Data) { try { const formData = new FormData(); formData.append("content", base64Data); formData.append("access_token", this.accessToken); formData.append("message", "上传图片"); const timeStamp = new Date().getTime(); Toast.showLoading("正在上传"); const response = await fetch( `${this.apiUrl}${this.username}/${this.repo}/contents/${this.dirPath}${timeStamp}.jpg`, { method: "POST", body: formData, } ); Toast.hide(); if (!response.ok) { throw new Error("上传图片失败"); } const data = await response.json(); Toast.showToast("图片上传成功!"); return data.content.download_url; } catch (error) { console.error(error); Toast.showToast("图片上传失败!"); throw error; } } ``` #### 3、获取gitee图床目录下的所有图片 根据API文档进行请求即可: ```javascript async getImg() { try { const response = await fetch( `${this.apiUrl}${this.username}/${this.repo}/contents/${this.dirPath}`, { headers: { Authorization: `token ${this.accessToken}`, }, } ); if (!response.ok) { throw new Error("获取图片列表失败"); } const data = await response.json(); // 筛选出图片文件 const imageFiles = data.filter( (file) => file.type === "file" && file.name.match(/\.(jpg|jpeg|png|gif)$/i) ); return imageFiles; } catch (error) { console.error(error); throw error; } } ``` #### 4、删除gitee图床目录下指定图片 根据API文档进行请求即可: ```javascript async deleteImg(fileName, sha, cb) { try { const response = await fetch( `${this.apiUrl}${this.username}/${this.repo}/contents/${this.dirPath}/${fileName}?access_token=${this.accessToken}&ref=${this.branchName}`, { method: "DELETE", headers: { "Content-Type": "application/json;charset=utf-8", }, body: JSON.stringify({ message: "删除图片", sha, prune: true, }), } ); if (!response.ok) { throw new Error("删除图片失败"); } Toast.showToast("删除成功"); cb && cb(); } catch (error) { console.error(error); Toast.showToast("删除失败"); throw error; } } ``` ### 二、拖拽点击、粘贴、选择文件夹上传图片 我们可以通过三种方式来上传我们本地的图片 ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/321e8163ac674862b288de38f64ebc1a~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1737&h=756&s=48270&e=png&b=f1f1f1) ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/69c930b47ed2460cb99be63168e0187a~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1096&h=824&s=198320&e=png&b=f3f2f2) #### 1、拖拽或点击上传图片 前面有写了一篇实现拖拽或点击上传图片的文章,这里就不详细再赘述了,有兴趣的可以去看看:[《文件拖拽上传功能已经烂大街了,你还不会吗?》](https://juejin.cn/post/7281581471898222655) #### 2、粘贴上传 平时我们经常会使用到截图,所以我们希望可以直接将截图粘贴到工具中进行上传,这里我们可以通过监听页面上的`paste`事件,直接读取剪切板的图片展示到页面上并进行上传。 ```javascript document.addEventListener("paste", function (e) { const items = e.clipboardData.items; for (const item of items) { if (item.type.indexOf("image") !== -1) { const blob = item.getAsFile(); showPreview(blob); } } }); ``` #### 3、选择文件夹上传文件夹中的所有图片 一张图片一张图片上传太慢了,所以我们也支持直接选择一个文件夹,将文件夹里的所有图片一次性上传到gitee上, - 1. 创建一个 HTML 文件,包含一个用于选择文件夹的 `` 元素和一个按钮用于触发上传操作。代码如下: ```html ``` `webkitdirectory`:告诉浏览器文件选择器应该允许选择文件夹(目录),而不仅仅是单个文件。 `multiple`:告诉浏览器文件选择器应该允许选择多个文件或文件夹。 - 2、获取到选择的文件并做相关处理 ```javascript function blobToBase64(blob) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => { const base64String = reader.result.split(",")[1]; resolve(base64String); }; reader.onerror = reject; reader.readAsDataURL(blob); }); } function selectFolder() { const folderInput = document.getElementById("folderInput"); folderInput.click(); } async function uploadImages() { const folderInput = document.getElementById("folderInput"); let files = folderInput.files || []; files = [...files].filter((file) => file.type.startsWith("image/")); for (let i = 0; i < files.length; i++) { Toast.showLoading(`上传中,${i}/${files.length}`); const file = files[i]; const base64Data = await blobToBase64(file); await gitOperate.uploadToGitee(base64Data,false); } Toast.hide(); Toast.showToast(`已全部上传`); waterfall.init(); } ``` ### 三、瀑布流展示图片 ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b20f5a204b094cd3ae16acbaf563d4c4~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1101&h=746&s=705659&e=png&b=efeded) 上传完图片后,我们还希望可以看到之前上传的图片,这里我们需要对图片列表做一个瀑布流展示。 ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ec10a83c06654e27a0507ce0ec09276e~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1880&h=929&s=2233860&e=png&b=edeae9) 之前我也有写过一个瀑布流组件详细的实现步骤,有兴趣的同学可以看看:[《Vue封装一个瀑布流图片容器组件》](https://juejin.cn/post/7083511816902410254?searchId=20231028175303D4788940AF11B789AE33)。 这里我们可以通过原生JavaScrip来快速实现一个,具体代码如下: ```javascript class WaterfallContent { constructor(config = {}) { this.init(config); } init(config = {}) { this.imgList = config.imgList || []; this.column = config.column || 8; this.imgMargin = config.imgMargin || 0.5; this.domId = config.domId || "waterfall-container"; this.minHeight = []; this.arr = []; const ul = document.getElementById(this.domId); ul.innerHTML = ""; } async create(imgList = this.imgList, cb) { this.init(); this.imgList = imgList; const ul = document.getElementById(this.domId); ul.innerHTML = ""; let trueWidth = Math.floor( (100 - this.column * this.imgMargin * 2) / this.column ); let trueWidthPx = 0; for (let i = 0; i < this.column; i++) { let li = document.createElement("li"); li.style.listStyle = "none"; li.style.float = "left"; li.style.width = `${trueWidth}%`; li.style.margin = `0 ${this.imgMargin}%`; li.classList.add("git-img"); ul.appendChild(li); this.arr.push(li); this.minHeight.push(0); trueWidthPx = li.offsetWidth; } this.loadHandler(trueWidthPx, cb); } getBase64(file) { const reader = new FileReader(); reader.readAsDataURL(file); return new Promise((resolve) => { reader.onload = () => { resolve(reader.result); }; }); } getImgPx(img, maxWidth) { const image = new Image(); image.src = img; return new Promise((resolve) => { image.onload = () => { const width = image.width; const height = image.height; image.width = maxWidth; image.height = image.height * (maxWidth / width); resolve({ width, height, image }); }; }); } async loadHandler(trueWidth, cb) { for (let i = 0; i < this.imgList.length; i++) { const imgItem = this.imgList[i]; const src = imgItem.download_url; const res = await this.getImgPx(src, trueWidth); const { image } = res; const minHeight = this.minHeight; const arr = this.arr; // 高度数组的最小值 const min = Math.min.apply(null, minHeight); // 高度数组的最小值索引 const minIndex = minHeight.indexOf(min); // 克隆一份图片 const im = image.cloneNode(true); im.setAttribute("data-sha", imgItem.sha); im.onclick = this.imgClick; // 将图片假如对应最小值索引的容器中 arr[minIndex].appendChild(im); // 更新最小值索引的容器的高度 minHeight[minIndex] += im.height; if (i === 0 && cb) { cb(); } } } } ``` ### 四、自定义鼠标右键菜单栏 ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/77dde685632f455bb9b1bb3d80e363ad~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1866&h=896&s=2085440&e=png&b=eeebea) 查看图片的时候我们希望可以对图片进行操作,这里的操作我们通过鼠标右键点击弹出,所以我们可以实现一个自定义鼠标右键菜单栏。 具体代码如下: ```javascript class MouseMenu { constructor(config) { this.menuClass = config.menuClass || "j-mouse-menu"; this.menuId = config.menuId || "JMouseMenu"; this.contentId = config.contentId || "j-mouse-content"; this.init(); } init() { const dom = document.getElementById(this.contentId); dom.oncontextmenu = (e) => { const clickItem = e.path[0]; if (clickItem.localName !== "img") return; const menu = document.getElementById(this.menuId); this.clickItem = clickItem; this.hideAllMenu(); // 自定义body元素的鼠标事件处理函数 e = e || window.event; e.preventDefault(); let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; // 获取垂直滚动条位置 let scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft; // 获取水平滚动条位置 menu.style.display = "block"; let left = e.clientX + scrollLeft; let top = e.clientY + scrollTop; if (menu.offsetHeight + top > window.innerHeight) { top = window.innerHeight - menu.offsetHeight - 5; } if (menu.offsetWidth + left > window.innerWidth) { left = window.innerWidth - menu.offsetWidth - 5; } menu.style.left = left + "px"; menu.style.top = top + "px"; document.onclick = () => { this.hideAllMenu(); }; }; } hideAllMenu() { const jMenu = document.getElementsByClassName("j-mouse-menu"); for (const item of jMenu) { item.style.display = "none"; } } } ``` ### 五、Toast提示功能 ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9d5da6d2ba554f7588540f5f427494b5~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1069&h=796&s=223590&e=png&b=f2f2f2) 交互少不了Toast弹窗提示,这里我们使用JavaScrip简单实现一个,具体代码如下: ```javaScript class ToastC { constructor(config) { this.config = config; this.init(); } init() { const body = document.body; const toastContainer = document.createElement("div"); toastContainer.id = "toastContainer"; const styleObj = { position: "fixed", top: "50%", left: "50%", transform: "translate(-50%, -50%)", background: "rgba(0, 0, 0, 0.8)", color: "#ffffff", fontSize: "16px", opacity: 0.7, transition: "opacity 0.3s ease-in-out", padding: "10px", "border-radius": "5px", display: "none", textAlign: "center", }; for (const key in styleObj) toastContainer.style[key] = styleObj[key]; body.appendChild(toastContainer); const loader = document.createElement("div"); loader.id = "toastLoader"; const loaderStyleObj = { border: "4px solid #f3f3f3", borderTop: "4px solid #3498db", borderRadius: "50%", width: "20px", height: "20px", animation: "spin 1s linear infinite", margin: "0 auto 10px", display: "none", }; for (const key in loaderStyleObj) loader.style[key] = loaderStyleObj[key]; toastContainer.appendChild(loader); const text = document.createElement("div"); text.id = "toastText"; const textStyleObj = { marginTop: "5px", }; for (const key in textStyleObj) text.style[key] = textStyleObj[key]; toastContainer.appendChild(text); const keyframes = ` @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } `; const style = document.createElement("style"); style.innerHTML = keyframes; document.head.appendChild(style); } showToast(text) { const textElem = document.getElementById("toastText"); // 设置Toast提示文本 textElem.innerText = text; // 显示Toast提示 toastContainer.style.display = "block"; // 3秒后隐藏Toast提示 setTimeout(() => this.hide(), 3000); } showLoading(text = "加载中...") { const loader = document.getElementById("toastLoader"); const textElem = document.getElementById("toastText"); // 设置Toast提示文本为加载中 textElem.innerText = text; // 显示Toast提示和加载动画 loader.style.display = "block"; this.show(); } show() { const toastContainer = document.getElementById("toastContainer"); // 显示Toast提示 toastContainer.style.display = "block"; } hide() { try { const toastContainer = document.getElementById("toastContainer"); const loader = document.getElementById("toastLoader"); // 隐藏Toast提示和加载动画 toastContainer.style.display = "none"; loader.style.display = "none"; } catch (err) {} } } ``` ### 六、快捷键打开隐藏窗口 我们可以设置快捷键快速唤起和隐藏窗口,在根目录下的main.js文件中注册快捷键,这里我设置的是`alt + x`,大家也可以改成自己喜欢的快捷键,具体代码如下: ```javascript // 注册快捷键 globalShortcut.register("Alt+X", () => { if (mainWindow.isVisible()) { mainWindow.hide(); } else { mainWindow.show(); } }); ``` ## 工具使用 ### 一、源码下载 直接到 [gitee](https://gitee.com/zheng_yongtao/electron_program) 上下载即可。 ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1ac52c01b958471ca7564a2538337d8d~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1677&h=770&s=142028&e=png&b=fdfcfc) ### 二、依赖安装 下载完源码之后,我们到gitImgBed目录下运行`npm i`安装依赖,等待依赖安装完成。 ![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7469d1a82e54412d8e90f33ef781085c~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=639&h=242&s=13241&e=png&b=191919) ### 三、程序打包 依赖安装完成之后,我们可以在gitImgBed目录下运行`npm run build`进行打包 ![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/933188e39c04471794de62fbfa015e53~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=600&h=134&s=12941&e=png&b=181818) 打包完成后我们可以在当前目录下看到一个叫`jyeontuGitImgBed-win32-x64`文件夹,打开文件夹,找到里面一个叫`jyeontuGitImgBed`的应用程序,双击启动即可 ![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d87803da4ee6499eb667f9f2249f712a~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=641&h=307&s=29734&e=png&b=fefdfd) ### 四、配置填写 将之前准备工作时间的gitee仓库相关信息填写到配置中。 ![image.png](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d51122f80ee042a59a7fcd31ec08aa92~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1236&h=990&s=64484&e=png&b=f4f4f4) 输入正确信息后保存,便可以上传和查看gitee图床中的图片了。 ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/115c5c1a49b546a9ba3b3bc6725f0db7~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1197&h=952&s=61926&e=png&b=f3f3f3) ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2e0527467ca5477b9309ff8d09a27a05~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1195&h=894&s=68916&e=png&b=f2f2f2) ![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/600bb0ade3954ce38035534366b1a386~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1226&h=901&s=1274673&e=png&b=eeebeb) ## 源码 ### 一、gitee gitee 地址:[https://gitee.com/zheng_yongtao/electron_program](https://gitee.com/zheng_yongtao/electron_program) ### 二、公众号 关注公众号『前端也能这么有趣』发送 `图床`即可获取源码。 ## 说在后面 > 🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。