spring boot + vue + element-ui全栈开发入门——基于Electron桌面应用开发
前言
Electron是由Github开发,用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库。 Electron通过将Chromium和Node.js合并到同一个运行时环境中,并将其打包为Mac,Windows和Linux系统下的应用来实现这一目的。
Electron于2013年作为构建Github上可编程的文本编辑器Atom的框架而被开发出来。这两个项目在2014春季开源。
目前它已成为开源开发者、初创企业和老牌公司常用的开发工具。 看看谁在使用Electron 。
继续往下阅读可以了解Electron的贡献者们和已经发布的版本,或者直接阅读快速开始指引来开始用Electron来构建应用。
(摘抄至electronjs.org)
一、初始化项目
运行,vue init simulatedgreg/electron-vue 项目名称
vue init simulatedgreg/electron-vue admin
这里的项目名称是“admin”
如果没有安装vue脚手架,请查看《spring boot + vue + element-ui全栈开发入门——windows开发环境》
一路回车
然后运行npm install来安装依赖,运行方式和之前一样。
如果遇到run dev或者run build的时候出错,可能是因为国内的网络下载“electron-v1.8.3-win32-x64.zip”出错,这时,你需要设置npm的代理:
npm config set proxy http://服务器IP或域名:端口号
npm config set https-proxy http://服务器IP或域名:端口号
如果需要用户名密码:
npm config set proxy http://用户名:密码@服务器IP或域名:端口号
npm config set https-proxy http://用户名:密码@服务器IP或域名:端口号
设置回原库
npm config set registry http://registry.npmjs.org
关闭代理
npm config delete proxy
npm config delete https-prox
也可以使用yarn。
npm install -g yarn
安装依赖、开发模式运行和编程的命令分别是:
yarn install
yarn run dev
yarn run build
项目构建完毕后,结构如下图所示:
和之前项目区别是,main是用于桌面程序的代码,render是用于渲染的代码。我们只需要在render文件夹里写代码就可以。
开发模式运行:
npm run dev
二、代码编写
参照《spring boot + vue + element-ui全栈开发入门——集成element-ui》安装所需的依赖
cnpm install --save element-ui
cnpm install --save-dev node-sass
cnpm install --save-dev sass-loader
cnpm install --save font-awesome
参照《spring boot + vue + element-ui全栈开发入门——前端列表页面开发》的代码如下:
入口文件:
import Vue from 'vue'
import axios from 'axios' import App from './App'
import router from './router'
import store from './store' if (!process.env.IS_WEB) Vue.use(require('vue-electron')) Vue.http = Vue.prototype.$http = axios
axios.defaults.baseURL = 'http://localhost:18080' Vue.config.productionTip = false import 'font-awesome/css/font-awesome.min.css' import ElementUI from 'element-ui'
//原始风格
// import 'element-ui/lib/theme-chalk/index.css'
//自定义风格
import './assets/theme/element-#09345f/index.css'
Vue.use(ElementUI) /* eslint-disable no-new */
new Vue({
components: {
App
},
router,
store,
template: '<App/>'
}).$mount('#app')
main.js
其中 axios.defaults.baseURL = 'http://localhost:18080' 是设置后端项目URL,而这可以根据具体情况写到配置文件中,开发环境调用开发环境的配置,生产环境调用生产环境配置。
路由文件:
import Vue from 'vue'
import Router from 'vue-router' Vue.use(Router) import Main from '@/pages/Main'
import Dashboard from '@/pages/Dashboard' import Member from '@/pages/Member' // let routes = [
// {
// path: '/',
// name: 'landing-page',
// component: require('@/components/LandingPage').default
// },
// {
// path: '*',
// redirect: '/'
// }
// ] let routes = [{
path: '/',
component: Main,
hidden: true,
children: [{
path: '/',
component: Dashboard,
name: '首页'
}]
}] routes.push({
path: '/member',
name: '会员管理',
component: Main,
iconCls: 'fa fa-user-circle-o',
children: [{
path: '/member/data',
component: Member,
name: '会员信息管理'
}]
}) const router = new Router({
routes: routes
}) export default router
router/index.js
主页面:
<template>
<section>
<el-container class="container">
<!--左边-->
<el-aside :width="collapsed? '75px' : '280px' ">
<el-container>
<el-header>
<span class="menu-button" v-if="collapsed" @click.prevent="collapsed=!collapsed">
<i class="fa fa-align-justify"></i>
</span>
<span v-else class="system-name">{{systemName}}</span>
</el-header>
<el-main>
<el-menu :default-active="$route.path" :collapse="collapsed" :style="{'height':menuHeight}">
<template v-for="(item,index) in menus">
<el-submenu :index="index+''" v-if="!item.leaf">
<template slot="title"><i :class="item.iconCls"></i><span v-if="!collapsed">{{item.name}}</span></template>
<el-menu-item v-for="child in item.children" :index="child.path" :key="child.path" @click="$router.push(child.path)">{{child.name}}</el-menu-item>
</el-submenu>
<el-menu-item v-if="item.leaf&&item.children.length>0" :index="item.children[0].path"><i :class="item.iconCls"></i>{{item.children[0].name}}</el-menu-item>
</template>
</el-menu>
</el-main>
</el-container>
</el-aside>
<!--内容-->
<el-container>
<!--页眉-->
<el-header class="header">
<el-row>
<el-col :span="18" class="header-title">
<span v-if="collapsed" class="system-name">{{systemName}}</span>
<span v-else class="menu-button" @click.prevent="collapsed=!collapsed">
<i class="fa fa-align-justify"></i>
</span>
</el-col>
<el-col :span="6"><span class="el-dropdown-link userinfo-inner">你好:{{userName}}</span></el-col>
</el-row>
</el-header>
<!--中间-->
<el-main class="main">
<transition name="fade" mode="out-in">
<router-view></router-view>
</transition>
</el-main>
</el-container>
</el-container>
</section>
</template> <script>
let data = () => {
return {
collapsed: false,
systemName: '后台管理',
userName: '系统管理员',
menuHeight: '100%',
menus: []
}
} let initMenu = function() {
for (let i in this.$router.options.routes) {
let root = this.$router.options.routes[i]
if (root.hidden)
continue
let children = []
for (let j in root.children) {
let item = root.children[j]
if (item.hidden)
continue
children.push(item)
} if (children.length < 1)
continue this.menus.push(root)
root.children = children
}
} let initHeight = function() {
this.menuHeight = (document.documentElement.clientHeight - 60) + 'px'
} export default {
data: data,
methods: {
initMenu,
//初始化高度
initHeight
},
mounted: function() {
this.initHeight()
window.addEventListener('resize', this.initHeight)
this.initMenu()
}
}
</script> <style scoped="scoped"
lang="scss">
$width: 100%;
$height: 100%;
$background-color: #09345f;
$header-color: #fff;
$header-height: 60px; .container {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
.el-aside {
.el-header {
line-height: $header-height;
background-color: $background-color;
color: $header-color;
text-align: center;
}
.el-container {
height: $height;
.el-main {
padding: 0;
}
}
} .main {
width: $width;
height: $height;
} .menu-button {
width: 14px;
cursor: pointer;
} .userinfo-inner {
cursor: pointer;
} .el-menu {
height: $height;
} .header {
background-color: $background-color;
color: $header-color;
text-align: center;
line-height: $header-height;
padding: 0; .header-title {
text-align: left;
span {
padding: 0 20px;
}
}
} .system-name {
font-size: large;
font-weight: bold;
}
}
</style>
Main.vue
会员数据列表页面:
<template>
<section>
<!--工具条-->
<el-col :span="24" class="toolbar" style="padding-bottom: 0px;">
<el-form :inline="true" :model="filters">
<el-form-item>
<el-input v-model="filters.query" placeholder="姓名/手机号等条件" />
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="handleQuery" icon="el-icon-search">查询</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" v-on:click="handleAdd" icon="el-icon-plus">添加</el-button>
</el-form-item>
</el-form>
</el-col>
<el-table :data="rows" style="width: 100%;overflow: auto;" :height="clientHeight" stripe border highlight-current-row v-loading="pageLoading">
<el-table-column label="注册日期" width="180">
<template slot-scope="scope">
<i class="el-icon-time"></i>
<span style="margin-left: 10px">{{ scope.row.date }}</span>
</template>
</el-table-column>
<el-table-column label="姓名" width="180" :show-overflow-tooltip="true">
<template slot-scope="scope">
<el-popover trigger="hover" placement="top">
<p>姓名: {{ scope.row.name }}</p>
<p>住址: {{ scope.row.address }}</p>
<div slot="reference" class="name-wrapper">
<el-tag size="medium">{{ scope.row.name }}</el-tag>
</div>
</el-popover>
</template>
</el-table-column>
<el-table-column prop="sex" label="性别" width="100" align="center" :show-overflow-tooltip="true">
<template slot-scope="scope">
{{scope.row.sex===1?'男':'女'}}
</template>
</el-table-column>
<el-table-column label="操作">
<template slot-scope="scope">
<el-button
size="mini"
type="primary"
@click="handleEdit(scope.$index, scope.row)"><i class="el-icon-edit"></i>编辑</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)"><i class="el-icon-delete"></i>删除</el-button>
</template>
</el-table-column>
</el-table>
<!--底部-->
<el-col :span="24" class="toolbar">
<el-pagination layout="prev, pager, next" @current-change="handleCurrentChange" :page-size="20" :total="total" style="float:right;">
</el-pagination>
</el-col> <!--对话框-->
<el-dialog :title="form && form.id ? '编辑' : '新增' " :visible.sync="formVisible" :close-on-click-modal="false">
<el-form :model="form" label-width="100px" :rules="rules" ref="form">
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name" />
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-radio-group v-model="form.sex">
<el-radio :label="1">男</el-radio>
<el-radio :label="2">女</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click.native="formVisible = false">取消</el-button>
<el-button type="primary" @click.native="handleSubmit" :loading="formLoading">提交</el-button>
</div>
</el-dialog> </section>
</template> <script>
const rules = {
name: [{
required: true,
message: '请输入姓名',
trigger: 'blur'
}],
sex: [{
required: true,
message: '请选择性别',
trigger: 'change'
}]
} let data = () => {
return {
//页码
page: 1,
//每页数量
size: 20,
//总数
total: 0,
//查询条件
filters: {},
//页面数据
rows: [],
//页面载入状态
pageLoading: false,
//列表高度
clientHeight: '100%',
//表单数据
form: {},
//验证规则
rules: rules,
//对话框隐藏状态
formVisible: false,
//表单提交状态
formLoading: false
}
} let handleAdd = function() {
this.form = {}
this.form.sex = 1
this.formVisible = true
} let handleEdit = function(index, row) {
this.form = Object.assign({}, row)
this.formVisible = true
} let handleDelete = function(index, row) {
if (this.pageLoading)
return this.$confirm('此操作将永久删除该数据, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.pageLoading = true
this.$http.get('/member/remove/' + row.id).then(res => {
this.pageLoading = false
if (!res.data.success) {
this.$message({
type: 'error',
message: res.data.message
})
return
}
this.$message({
type: 'success',
message: '删除成功!'
})
this.page = 1
this.getRows()
}).catch(e => this.pageLoading = false)
}).catch(e => {})
} let getRows = function() {
if (this.pageLoading)
return
this.pageLoading = true let params = {
page: this.page,
size: this.size,
query: this.filters.query
}
//调用post请求
this.$http.post('/member/loadPage', params).then(res => {
this.pageLoading = false
if (!res.data || !res.data.rows)
return
//总数赋值
this.total = res.data.total
this.page++;
//页面元素赋值
this.rows = res.data.rows
}).catch(e => this.pageLoading = false)
} let handleSubmit = function() {
if (this.formLoading)
return this.$refs.form.validate(valid => {
if (!valid)
return this.formLoading = true //调用http协议
this.$http.post('/member/save', this.form).then(res => {
this.formLoading = false
if (!res.data.success) {
this.$message({
showClose: true,
message: res.data.message,
type: 'error'
});
return
}
this.$message({
type: 'success',
message: '保存成功!'
}) //重新载入数据
this.page = 1
this.getRows()
this.formVisible = false
}).catch(e => this.formLoading = false)
})
} let handleQuery = function() {
this.page = 1
this.getRows()
} let handleCurrentChange = function(val) {
this.page = val
this.getRows()
} let initHeight = function() {
this.clientHeight = (document.documentElement.clientHeight - 258) + 'px'
} export default {
data: data,
methods: {
//查询
handleQuery,
//添加
handleAdd,
//修改
handleEdit,
//删除
handleDelete,
//页数改变
handleCurrentChange,
//获取分页
getRows,
//初始化高度
initHeight,
//提交数据
handleSubmit
},
mounted: function() {
window.addEventListener('resize', this.initHeight)
this.initHeight()
this.getRows()
}
}
</script> <style scoped>
</style>
Member.vue
结构如下图所示:
还有,在运行之前,我们需求修改src/main/index.js的配置:
function createWindow() {
/**
* Initial window options
*/
mainWindow = new BrowserWindow({
height: 563,
useContentSize: true,
width: 1000,
webPreferences: {
webSecurity: false
}
})
其目的是为了实现js跨域。
运行之前项目的后端项目《spring boot + vue + element-ui全栈开发入门——spring boot后端开发》:
mvn package
java -jar target/demo.jar
运行项目,效果如下:
二、生成安装包
npm run build
如提示缺少vue组建,是因为registry的问题,因为国内taobao镜像没有Electron的依赖环境。所以需要设置回默认的 registry,并使用设置proxy的方式下载依赖环境。
如果提示“icon source "build/icons/icon.ico" not found”
就把“icons”加到build目录下,下载icons请点击链接,根据具体情况修改icons。
生成好后,出现“admin Setup 0.0.0.exe”的文件,即安装程序。
我运用这个安装程序后,打开刚刚开发好的程序,效果如图所示:
发现,虽然只用到了一些前端技术,但已经能够开发出桌面应用了。小时候,老师说:“学好数理化,走遍天下都不怕”。而现在是:“学会了node,任何平台的前端都不怕”。
代码下载地址 : https://github.com/carter659/electron-vue-example.git
如果你觉得我的博客对你有帮助,可以给我点儿打赏,左侧微信,右侧支付宝。
有可能就是你的一点打赏会让我的博客写的更好:)
spring boot + vue + element-ui全栈开发入门——基于Electron桌面应用开发的更多相关文章
- 分享一个自搭的框架,使用Spring boot+Vue+Element UI
废弃,新的:https://www.cnblogs.com/hackyo/p/10453243.html 特点:前后端分离,可遵循restful 框架:后端使用Spring boot,整合了aop.a ...
- 自搭的一个系统框架,使用Spring boot+Vue+Element
基于:jdk1.8.spring boot2.1.3.vue-cli3.4.1 特性: ~ 数据库访问使用spring data jpa+alibaba druid ~ 前后端数据交互使用 ...
- spring boot + vue + element-ui全栈开发入门
今天想弄弄element-ui 然后就在网上找了个例子 感觉还是可以用的 第一步是完成了 果断 拿过来 放到我这里这 下面直接是连接 点进去 就可以用啊 本想着不用vue 直接导入连接 ...
- 前后端分离,我怎么就选择了 Spring Boot + Vue 技术栈?
前两天又有小伙伴私信松哥,问题还是职业规划,Java 技术栈路线这种,实际上对于这一类问题我经常不太敢回答,每个人的情况都不太一样,而小伙伴也很少详细介绍自己的情况,大都是一两句话就把问题抛出来了,啥 ...
- spring boot + vue + element-ui
spring boot + vue + element-ui 一.页面 1.布局 假设,我们要开发一个会员列表的页面. 首先,添加vue页面文件“src\pages\Member.vue” 参照文档h ...
- spring boot + vue + element-ui全栈开发入门——开篇
最近经常看到很多java程序员朋友还在使用Spring 3.x,Spring MVC(struts),JSP.jQuery等这样传统技术.其实,我并不认为这些传统技术不好,而我想表达的是,技术的新旧程 ...
- 新书上线:《Spring Boot+Spring Cloud+Vue+Element项目实战:手把手教你开发权限管理系统》,欢迎大家买回去垫椅子垫桌脚
新书上线 大家好,笔者的新书<Spring Boot+Spring Cloud+Vue+Element项目实战:手把手教你开发权限管理系统>已上线,此书内容充实.材质优良,乃家中必备垫桌脚 ...
- koa+mysql+vue+socket.io全栈开发之数据访问篇
后端搭起大体的框架后,接着涉及到的就是如何将数据持久化的问题,也就是对数据库进行 CURD 操作. 关于数据库方案, mongodb 和 mysql 都使用过,但我选用的是 mysql,原因: 目前为 ...
- 实习模块vue+java小型全栈开发(三)
实习模块vue+java小型全栈开发(三) --dx 背景 首先,先给自己一个答案:这篇博客我定义为(三),因为之前的两个模块页面,内容都是一样的,但是被改了几次需求,就一直拖着没有上传. 今天是真正 ...
随机推荐
- trie 树 模板
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> ...
- 使用angular/react/vue实现相同的面试题组件
面试题要求如下所示 1.angular: <!DOCTYPE html><html lang="en"><head> <meta char ...
- Windows Server 2016-重命名域控制器
当公司发展到一定规模或者信息化建设到一定程度的情况下,很多信息化规范出台:很多初期服务器搭建包括服务搭建等计算机名等都是按照默认或者随机命名的,不便于区分业务或服务等.通过前边的章节我们对Active ...
- Spring Task定时任务Scheduled
Spring的任务调度,采用注解的形式 Spring中@Scheduled的用法. spring的配置文件如下,先扫描到任务的类,打开spirng任务的标签 <beans xmlns=" ...
- dig挖出DNS的秘密
[最简单的dig用法] 最简单的dig用法,当然就是直接输入dig按回车. 1 2 3 4 5 6 $ dig ; <<>> DiG 9.8.2rc1-RedHat-9.8 ...
- virtualbox命令行共享CentOS目录
virtualbox命令行共享CentOS目录 1. 安装virtualbox增强工具 "右ctrl+c" 显示和隐藏virtualbox虚拟机的菜单栏. 在VirtualBo ...
- 学习资料分享:Python能做什么?
最近一直忙着研究学习Python,很久没更新博客了,整理了一些Python学习资料,和大家分享一下!每天更新一篇~ 一.Python 特点 1.易于学习:Python有相对较少的关键字,结构简单,和一 ...
- Sql Server 常用事务处理总结
在数据库操作中,常用事务写法: 1. 通过 @@error 判断一批sql 执行完毕,是否有异常. @@error 为系统变量,每次执行完 sql 都会返回一个数值, 0 表示 执行成功 ,非0 ...
- python进阶学习笔记(二)
1.模块和包的概念 python的解决方案是把同名的模块放到不同的包中 1.1,导入模块 要使用一个模块,我们必须首先导入该模块.Python使用import语句导入一个模块.例如,导入系统自带的模块 ...
- 对TCP三次握手四次分手还不清楚的速度进,超简单解析,明白了就很好记!
关于TCP三次握手四次分手,之前看资料解释的都很笼统,很多地方都不是很明白,所以很难记,前几天看的一个博客豁然开朗,可惜现在找不到了.现在把之前的疑惑总结起来,方便一下大家. 先上个TCP三次握手和四 ...