1301 字
7 分钟
electron开发流程
创建应用
使用 electron-vite 快速搭建一个项目
npm create @quick-start/electron@latest测试打包
npm run build:win配置打包
electron-builder.yml中 nsis 的配置
nsis: artifactName: ${name}-${version}-setup.${ext} shortcutName: ${productName} uninstallDisplayName: ${productName} createDesktopShortcut: always # 始终创建桌面快捷方式 oneClick: false # 一键安装(直接安装,不显示界面) true=一键安装 false=手动安装 perMachine: false # 仅当前用户安装(true=所有用户) true=所有用户 false=当前用户 allowElevation: true # 允许提权(如需安装到 Program Files) true=允许 false=不允许 allowToChangeInstallationDirectory: true # 用户选择安装路径 true=允许 false=不允许 runAfterFinish: true # 安装完成后自动运行应用 true=允许 false=不允许 # silent: true # 完全静默(无任何界面,适合企业部署)prettier/prettier 格式化问题
修改.prettierrc.yml 添加
endOfLine: auto修改env.d.ts文件
添加以下代码
declare module '*.vue' { import { DefineComponent } from 'vue' const component: DefineComponent<{}, {}, any> export default component}目的:
- TypeScript 默认不支持
.vue文件 TypeScript 只认识.ts、.js等文件类型,需要通过声明文件告诉它如何处理.vue文件。 - Vue SFC 的特殊性
.vue文件由<template>、<script>和<style>组成,编译后会变成一个 JavaScript 对象(组件选项)。这段代码帮助 TypeScript 理解这种转换。 - 避免 IDE(如 VS Code)报错 如果没有这个声明,VS Code 会提示模块找不到或类型错误,即使代码能正常运行。
渲染进程配置
安装element ui
npm install element-plus --save修改main.ts 添加一下代码
import ElementPlus from 'element-plus'import 'element-plus/dist/index.css'import zhCn from 'element-plus/es/locale/lang/zh-cn' // 中文语言包
const app = createApp(App)
app.use(ElementPlus, { locale: zhCn})router 路由
npm install vue-router@4创建src/router/index.ts
注意!!!!!!!!!
路由模式一定要是createWebHashHistory 如果是createWebHistory,打包之后的页面会无法显示
import { createMemoryHistory, createRouter } from 'vue-router'
import HomeView from './HomeView.vue'import AboutView from './AboutView.vue'
const routes = [ { path: '/', component: HomeView }, { path: '/about', component: AboutView }]
const router = createRouter({ history: createWebHashHistory(), routes})export default routerlayout配置
问题:
-
当设置
height: 100vh;出现滚动条那是因为body的问题body {margin: 0;}
新建一个layout文件夹 里面只存放布局相关的页面
index.vue
<template> <el-container style="height: 100vh"> <MenuComponent /> <PlanMain /> </el-container></template>
<script setup lang="ts">import MenuComponent from '@/layout/menu/menu.vue'import PlanMain from '@/layout/main/planMain.vue'</script>
<style scoped></style>main/planMain.vue
<template> <el-main> <router-view></router-view> </el-main></template>
<script setup lang="ts"></script>
<style scoped></style>menu/menu.vue
<template> <el-aside width="200px" class="full-height-aside"> <el-menu class="el-menu-vertical-demo full-height-menu" router :default-active="$route.path" :collapse="isCollapse" @open="handleOpen" @close="handleClose" > <el-menu-item index="/home"> <el-icon><icon-menu /></el-icon> <template #title>首页</template> </el-menu-item> <el-menu-item index="/planManage"> <el-icon><CircleCheck /></el-icon> <template #title>任务管理</template> </el-menu-item> <el-menu-item index="/logRecord"> <el-icon><document /></el-icon> <template #title>日志管理</template> </el-menu-item> <el-menu-item index="/settingManage"> <el-icon><setting /></el-icon> <template #title>设置</template> </el-menu-item> </el-menu> </el-aside></template>
<script setup lang="ts">import { ref } from 'vue'import { Document, Menu as IconMenu, Setting, CircleCheck } from '@element-plus/icons-vue'
defineOptions({ name: 'MenuComponent'})
const isCollapse = ref(false)const handleOpen = (key: string, keyPath: string[]) => { console.log(key, keyPath)}const handleClose = (key: string, keyPath: string[]) => { console.log(key, keyPath)}</script>
<style scoped>/* 关键样式 */.full-height-aside { height: 100%; overflow-y: auto; /* 垂直滚动 */ overflow-x: hidden; /* 隐藏水平滚动 */ /* border-right: 3px solid #eee; */}
.full-height-menu { height: 100%; /* 继承父级高度 */ overflow-y: hidden; /* 禁用垂直滚动 */ border-right: inset; /* 可选:移除默认边框 */}
/* 保留原有样式 */.el-menu-vertical-demo:not(.el-menu--collapse) { width: 100%;}</style>还要修改路由 router/index.ts
import { createWebHistory, createRouter } from 'vue-router'
import Layout from '@/layout/index.vue'import HomeView from '@/views/home/index.vue'import logRecord from '@/views/logRecord/index.vue'import planManage from '@/views/planManage/index.vue'import settingManage from '@/views/settingManage/index.vue'const routes = [ { path: '/', component: Layout, redirect: '/home', children: [ { path: '/home', component: HomeView }, { path: '/logRecord', component: logRecord }, { path: '/planManage', component: planManage }, { path: '/settingManage', component: settingManage } ] }, { path: '/', redirect: '/home' }]
const router = createRouter({ history: createWebHistory(), routes})export default routerpinia配置
安装
npm install pinia修改main.ts 添加以下代码
import { createPinia } from 'pinia'
/* 创建pinia */const pinia = createPinia()/* 使用插件 */app.use(pinia)新建一个store目录
在store目录下新建一个index.ts
// 引入defineStore用于创建storeimport { defineStore } from 'pinia'// 定义并暴露一个storeexport const useToolStore = defineStore('count', { // 动作 actions: {}, // 状态 state() { return { planState: 1 } }, // 计算 getters: {}})使用
import { useToolStore } from '@renderer/store'
// 存储状态const toolStore = useToolStore()const { planPriorityList, planTypeList } = toolStore安装sqlite
npm install sqlite3调试
import sqlite3 from 'sqlite3'
app.whenReady().then(() => { const dbDir = join(app.getPath('userData'), 'database')
// 保证目录存在 if (!fs.existsSync(dbDir)) fs.mkdirSync(dbDir) // 打开 SQLite 数据库(如果没有则自动创建) db = new sqlite3.Database(join(dbDir, 'mydb.sqlite'))
console.log('ddbDir', dbDir)
// 创建 plans 表 db.run( ` CREATE TABLE IF NOT EXISTS plans ( id INTEGER PRIMARY KEY AUTOINCREMENT, planName TEXT NOT NULL, planPriority TEXT, planType TEXT, sort INTEGER DEFAULT 0, stratTime TEXT, endTime TEXT, createTime TEXT DEFAULT CURRENT_TIMESTAMP ) ` )
// 创建 signins 表 db.run( ` CREATE TABLE IF NOT EXISTS signins ( id INTEGER PRIMARY KEY AUTOINCREMENT, planId INTEGER, type TEXT, isDaily INTEGER DEFAULT 0, time TEXT DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(planId) REFERENCES plans(id) ) ` )
})注意
在IPC通信时如果是TS,类型一定要匹配上
之后在preload定义完接口后还需要在index.d.ts定义一遍
import { ElectronAPI } from '@electron-toolkit/preload'// 👉 添加对 window.api 的类型定义import type { Plan, PageInfo } from '@main/types/db' // 根据你实际路径调整
export interface PlanApi { getAllPlans(): Promise<any> getPlans(plan: Partial<Plan>, pageInfo: PageInfo): Promise<any> addPlan(plan: Plan): Promise<any> updatePlan(plan: Plan): Promise<any> deletePlan(id: number): Promise<any> getQuerySignin(): Promise<any> queryPlanProgress(): Promise<any> signinToday(): Promise<any> planAloneSign(planId: number): Promise<any> querySignList(): Promise<any>}
declare global { interface Window { electron: ElectronAPI planApi: PlanApi }}打包注意
如果打包安装后的应用是空白页,检查路由配置
Vue Router 默认的 createWebHistory() 模式 依赖 HTML5 history API,这在 Electron 的 file:// 协议中会导致无法路由跳转(页面路径不会匹配成功,页面内容空白)。
将createWebHistory() 改为 createWebHashHistory
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({ history: createWebHashHistory(), routes: [...]})