electron vue 模仿qq登录界面功能实现

 

1、使用vuecli创建vue项目

我用的vue2

vue create qq_test

 

2、安装electron

npm install electron -g
//or
npm install electron@12.0.11   //老版本

 

3、vue项目安装Electron-builder打包工具

版本我选择的是12

vue add electron-builder

 

4、在vue项目的src下有个background.js文件

background.js里面有默认的配置,修改后我的配置大概如下

'use strict'

import { app, protocol, BrowserWindow, Menu, Tray } from 'electron'

import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
const isDevelopment = process.env.NODE_ENV !== 'production'

// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true } }
])

async function createWindow() {
// Create the browser window.
const win = new BrowserWindow({
  width: 432,
  height: 331,
  alwaysOnTop: true,//窗口一直保持在其他窗口前面
  modal: true,
  frame: false,
  darkTheme: true,
  resizable: false,//用户不可以调整窗口
  center: true, // 窗口居中
  transparent: false,//窗口透明
  show: false,// 显示窗口将没有视觉闪烁(配合下面的ready-to-show事件)
  hasShadow: true,//窗口是否有阴影
  webPreferences: {
    // Use pluginOptions.nodeIntegration, leave this alone
    // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
    // nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
    // contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
    devTools: true,//客户端可以打开开发者工具(在客户端打开快捷键:ctrl+shift+i)
    nodeIntegration: true,
    enableRemoteModule: true, // 使用remote模块(electron12版本就开始废除了,需要我们自己安装remote模块)
    contextIsolation: false,
    //解决axios请求跨域问题(不推荐,不安全,但简单好用)
    webSecurity: false,
  },
})

if (process.env.WEBPACK_DEV_SERVER_URL) {
  // Load the url of the dev server if in development mode
  await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
  if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
  createProtocol('app')
  // Load the index.html when not in development
  win.loadURL('app://./index.html')
}
}

// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
  app.quit()
}
})


app.on('activate', () => {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.once('ready-to-show', () => {
win.show();
})

app.on('ready', async () => {
if (isDevelopment && !process.env.IS_TEST) {
  // Install Vue Devtools
  try {
    await installExtension(VUEJS_DEVTOOLS)
  } catch (e) {
    console.error('Vue Devtools failed to install:', e.toString())
  }
}
createWindow()
})

// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
  process.on('message', (data) => {
    if (data === 'graceful-exit') {
      app.quit()
    }
  })
} else {
  process.on('SIGTERM', () => {
    app.quit()
  })
}
}

 

5、安装remote模块

因为electron12版本开始就废除了remote模块,我们需要自己安装

npm install @electron/remote --save
//or
cnpm install @electron/remote --save

能在客户端实现 最大化、最小化、关闭功能

 

6、代码

1、Login.vue页面(登录页面)

里面的最小化图标、关闭图标、最大化图标 请自己去iconfont-阿里巴巴矢量图标库下载

<template>
<div class="login" style="overflow:hidden;">
  <!-- 顶部 -->
  <header class="header">
    <!-- 最小化按钮 -->
    <span @click="minwin" class="iconfont icon-24gl-minimization"></span>
    <!-- 关闭按钮 -->
    <span @click="close" class="iconfont icon-guanbi"></span>
  </header>

  <main>
    <!-- 顶部背景图 -->
    <div class="bg">
      <img style="" src="../../assets/images/login.gif" />
    </div>
    <!-- 登录表单组件 -->
    <div class="body">
      <Login_form></Login_form>
    </div>
  </main>
  <footer class="footer">
    <div class="zczh" @click="zc">注册账号</div>
    <div>
      <span title="二维码登录" class="iconfont icon-erweima"></span>
    </div>
  </footer>
</div>
</template>

<script>
import "@/assets/css/login.css";
import "@/assets/fonts/gb/iconfont.css";
import "@/assets/fonts/zxh/iconfont.css";
import "@/assets/fonts/ewm/iconfont.css";
import Login_form from "@/components/Login_form.vue";

//网页测试要关闭,打包成软件要启动
const { remote } = window.require("electron");

export default {
name: "login",
data() {
  return {};
},
components: {
  Login_form,
},
methods: {
  close() {
    // 只是关闭窗口,软件不退出
    // remote.getCurrentWindow().hide()

    // 关闭窗口,退出软件
    remote.getCurrentWindow().close();
  },
  // 最小化
  minwin() {
    // 最小化,在任务栏显示
    remote.getCurrentWindow().minimize()

    // 隐藏窗口,任务栏也隐藏
    // remote.getCurrentWindow().hide();
  },
  zc() {
    this.$router.push("/reg");
  },
  
},
mounted() {
  //显示窗口
  remote.getCurrentWindow().show();
},
};
</script>

<style lang="scss" scpoed>
.login {
max-width: 900px;
overflow: hidden;
min-width: 430px;
.bg {
  img {
    width: 430px;
    height: 124px;
    object-fit: cover;
  }
}
}
</style>

2、login.css

/**
取消全部的外边距和内边距
*/
* {
  padding: 0;
  margin: 0;
}

/*设置窗口的样式*/
.login {
  cursor: default;
  /*设置手型*/
  /* border: 1px solid red; */
  /*加一个边框 调试样式 最后要删除或者更改**/
  width: 430px;
  /*设置宽度  必须要和主进程中设置的一样 不能大于主进程中设置的宽度 否则会出现滚动条*/
  height: 329px;
  /*设置高度  必须要和主进程中设置的一样 不能大于主进程中设置的高度 否则会出现滚动条*/
  position: relative;
  /*设置为相对定位*/
  /* border-radius: 4px; */
  /*设置圆角*/
  border: 1px solid #464545;
  /* 拖 */
  -webkit-app-region: drag;
  /* 禁止滚动条 */
  overflow: hidden;
}

/**
header的样式 header中只会有一个关闭按钮 处于右上角
*/
.login header.header {
  position: absolute;
  /*设置绝对定位 因为背景在他下面*/
  height: 30px;
  /*设置高度*/

  /* border-radius: 20px 20px 0 0; */
  /*给header的左上角 右上角设置圆角 不然会出现很尴尬的页面*/
  width: 428px;
  /* 因为设置了绝对定位 设置宽度*/

  /* background: rgba(255, 255, 255, 0.2); */
  /*暂时设置的 后面要删除或者更改*/
  text-align: right;
}

/**
背景
*/
.login main .bg {
  height: 124px;
  /*设置高度*/
  width: 430px;
  /*设置宽度 也可以不用设置 因为这个元素没有设置绝对定位 所以默认就是100%*/
  /* border-radius: 4px 4px 0 0; */
  /*给左上角 右上角设置圆角 不然会出现很尴尬的页面 这里和header重合在一起了*/
  /* background: url("@/assets/images/login.gif") 10px; */
  background-size: 100%;
}

/**
放置表单的元素
*/
.login main .body {
  width: 428px;
  /*设置宽度 也可以不用设置 因为这个元素没有设置绝对定位 所以默认就是100%*/
  height: 200px;
  /*设置高度 这里的高度是 主窗口(326) - footer(30) - 背景(124) 因为header设置了绝对定位 所以不用关 */
  /* background: green; */
  /*暂时设置的 后面要删除或者更改*/
}

.login footer.footer {
  position: absolute;
  /* 设置绝对定位 要让他处于窗口的最底部*/
  height: 30px;
  /*设置高度 */
  /* background: red; */
  /*暂时设置的 后面要删除或者更改*/
  bottom: 0;
  /*让footer处于底部*/
  width: 397.99px;
  /* 因为设置了绝对定位 设置宽度*/
  /* border-radius: 0 0 5px 5px; */
  background-color: #FFFFFF;
}

.login header.header span{
  display: inline-block;
  height: 30px;
  width:30px;
  text-align: center;
  line-height: 30px;
  color:#E4393c;
  cursor: pointer;

}
.login header.header span:hover{
  background-color: rgba(255,255,255,0.6);
  cursor: pointer;
}
.zczh{
  cursor: pointer;
  color: #7c7a7a;
  user-select: none;
  -webkit-app-region: no-drag;
}
.zczh:hover{
  cursor: pointer;
  color: black;
  user-select: none;
  -webkit-app-region: no-drag;
}

3、Login_form.vue组件

里面的resetMessage信息提示是我重新封装的element组件(我就不放上去了)

里面的最小化图标、关闭图标、最大化图标 请自己去iconfont-阿里巴巴矢量图标库下载

<template>
<div class="login_form">
  <form>
    <div class="form_item">
      <i class="iconfont icon-zhanghao"></i
      ><input
        @focus="i1_get"
        @blur="i1_lose"
        @input="input1($event)"
        id="text"
        v-model="email"
        type="email"
        :placeholder="placeholder1"
        @keyup.enter="login"
      />
    </div>
    <div class="form_item">
      <i class="iconfont icon-mima"></i
      ><input
        @focus="i2_get"
        @blur="i2_lose"
        id="text2"
        v-model="password"
        type="password"
        :placeholder="placeholder2"
        @keyup.enter="login"
      />
    </div>
    <div class="login_options">
      <label
        ><div class="option_item">
          <input
            :disabled="jzmm"
            v-model="zddl"
            type="checkbox"
            autocomplete="off"
          />
        </div>
        <i class="text">记住账号</i></label
      >
      <label
        ><div class="option_item">
          <input
            @click="jzmm_mm"
            v-model="jzmm"
            type="checkbox"
            autocomplete="off"
          />
        </div>
        <i class="text">记住密码</i></label
      >
      <i class="text wjmm">忘记密码</i>
    </div>
  </form>
  <div class="buttons">
    <button @click="login">登录</button>
  </div>
</div>
</template>

<script>
import "@/assets/css/login_form.css";
import "@/assets/fonts/xlk/iconfont.css";
import "@/assets/fonts/slk/iconfont.css";
import "@/assets/fonts/zh/iconfont.css";
import "@/assets/fonts/mm/iconfont.css";

//网页测试要关闭,打包成软件要启动
const { remote } = window.require("electron");

export default {
name: "Login_form",
data() {
  return {
    email: "",
    password: "",
    placeholder1: "邮箱",
    placeholder2: "密码",
    zddl: false,
    jzmm: false,
  };
},
methods: {
  i1_get() {
    if (!this.email) {
      this.placeholder1 = "";
    }
  },
  i2_get() {
    if (!this.password) {
      this.placeholder2 = "";
    }
  },
  i1_lose() {
    if (!this.email) {
      this.placeholder1 = "邮箱";
    }
  },
  i2_lose() {
    if (!this.password) {
      this.placeholder2 = "密码";
    }
  },
  input1(e) {
    if (e.target.value == "") {
      // console.log(val);
      this.password = "";
      this.placeholder2 = "密码";
    }
  },
  login() {
    var eamil_gz =
      /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
    switch (true) {
      case this.email == "":
        this.waring("邮箱不能为空!");
        break;
      case !eamil_gz.test(this.email):
        this.error("邮箱格式错误");
        break;
      case this.password == "":
        this.waring("密码不能为空!");
        break;
      default:
        let data = {
          log_email: this.email,
          log_password: this.password,
        };
        this.$axios
          .post("/login/login", data)
          // 在后台查询信息 返回res结果
          .then((res) => {
            // console.log(res.data);
            switch (true) {
              case res.data.code == 200:
                //登录成功
                this.success(res.data.msg);
                //本地存储
                localStorage.setItem("email", this.email);
                localStorage.setItem("token", res.data.token);
                if (this.zddl == true) {
                  localStorage.setItem("zddl", true);
                } else {
                  localStorage.removeItem("zddl");
                  localStorage.removeItem("email");
                }
                if (this.jzmm == true) {
                  localStorage.setItem("zddl", true);
                  localStorage.setItem("jzmm", true);
                  localStorage.setItem("mm", this.password);
                } else {
                  localStorage.removeItem("jzmm");
                  localStorage.removeItem("mm");
                }
                this.$router.push("/chat_index");
                //登录成功跳转会有点闪屏,先隐藏,到另外一个路由再显示
                remote.getCurrentWindow().hide();
                //登录之后设置窗口在电脑桌面显示位置
                remote.getCurrentWindow().setPosition(386, 192);
                break;
              case res.data.code == 404:
                //邮箱不存在
                this.error(res.data.msg);
                break;
              case res.data.code == 300:
                //密码错误
                this.error(res.data.msg);
                break;
              case res.data.code == 400:
                //用户异常
                this.error(res.data.msg);
                break;
              default:
                //未知错误
                this.error(res.data.msg);
                break;
            }
          })
          .catch((error) => {
            //报错
          });
        break;
    }
  },
  //记住密码
  jzmm_mm() {
    if (this.zddl == false) {
      this.zddl = true;
    }
    this.jzmm = true;
  },
  // 警告提示
  waring(title) {
    this.$resetMessage({
      message: `${title}`,
      type: "warning",
    });
  },
  // 成功提示
  success(title) {
    this.$resetMessage({
      message: `${title}`,
      type: "success",
    });
  },
  // 错误提示
  error(title) {
    this.$resetMessage.error(`${title}`);
  },
},
mounted() {
  // 读取本地存储账号信息
  if (localStorage.getItem("zddl")) {
    this.zddl = true;
    this.email = localStorage.getItem("email");
  }
  if (localStorage.getItem("jzmm")) {
    this.jzmm = true;
    this.password = localStorage.getItem("mm");
  }
  if (localStorage.getItem("email")) {
    this.email = localStorage.getItem("email");
  }
  if (!this.email) {
    document.getElementById("text").focus();
  } else {
    document.getElementById("text2").focus();
  }
},
};
</script>

4、login_form.css

.login_form{
  -webkit-app-region: drag;
  background-color: #FFFFFF;

}
.login_form form {
  padding: 10px 90px 0 90px;

}

.form_item {
  height: 40px;
  position: relative;

}

.form_item div {
  float: right;
  margin-right: 5px;
}

.form_item i.iconfont {
  position: absolute;
  top: 5px;

}

.form_item input {
  outline: none;
  border: none;
  padding-left: 30px;
  font-size: 16px;
  width: 230px;
  height: 30px;
  border-bottom: 1px solid #CCC;
  user-select: none;
  -webkit-app-region: no-drag;
}

.buttons {
  text-align: center;
}

.buttons button {
  background-color: #0786b4;
  border: none;
  width: 250px;
  color: #FFFFFF;
  height: 35px;
  cursor: pointer;
  font-size: 14px;
  border-radius: 4px;
  outline: none;
-webkit-app-region: no-drag;
user-select: none;
}
.buttons button:hover {
  background-color: #0aaae4;
  border: none;
  width: 250px;
  color: #FFFFFF;
  height: 35px;
  cursor: pointer;
  font-size: 14px;
  border-radius: 4px;
  outline: none;
  -webkit-app-region: no-drag;
  user-select: none;
}

.checkbox {
  display: flex;
  justify-content: center;
  align-items: center;
}

.footer {
  display: flex;
  justify-content: space-between;
  padding: 5px 15px 5px 15px;
  align-items: center;
}


.login_options {
  margin-bottom: 10px;
  margin-top: 5px;
}

.login_options .option_item {
  display: inline-block;
  align-items: center;
  width: 13px;
  height: 13px;
  margin-right: 5px;
  position: relative;
  cursor: pointer;
  top: 2px;
  -webkit-app-region: no-drag;
}

.login_options .option_item input {
  /* font-size: 13px; */
  -webkit-app-region: no-drag;
}

.login_options i.text {
  display: inline-block;
  margin-right: 14px;
  font-size: 13px;
  font-style: normal;
  user-select: none;
  -webkit-app-region: no-drag;
}

.login_options .option_item span.checked {
  position: absolute;
  top: -4px;
  right: -3px;
  font-weight: bold;
  display: inline-block;
  width: 20px;
  height: 20px;
  cursor: pointer;
}

.option_item span.checked img {
  width: 100%;
  height: 100%;
}

input[type="checkbox"]+span {
  opacity: 0;
}

input[type="checkbox"]:checked+span {
  opacity: 1;
}

.wjmm {
  cursor: pointer;
  color: rgb(139, 134, 134);
}

.wjmm:hover {
  cursor: pointer;
  color: rgb(19, 18, 18);
}

5、package.json文件

{
"name": "qq_test",
"version": "0.1.0",
"private": true,
"scripts": {
  "serve": "vue-cli-service serve",
  "build": "vue-cli-service build",
  "electron:build": "vue-cli-service electron:build",
  "electron:serve": "vue-cli-service electron:serve",
  "postinstall": "electron-builder install-app-deps",
  "postuninstall": "electron-builder install-app-deps"
},
"main": "background.js",
"dependencies": {
  "axios": "^0.27.2",
  "core-js": "^3.8.3",
  "element-ui": "^2.15.9",
  "express": "^4.18.1",
  "qs": "^6.11.0",
  "socket.io": "^4.5.1",
  "socket.io-client": "^3.1.0",
  "vscode-material-icon-theme-js": "^1.0.7",
  "vue": "^2.6.14",
  "vue-router": "^3.5.1",
  "vue-socket.io": "^3.0.10",
  "vuex": "^3.6.2"
},
"devDependencies": {
  "@vue/cli-plugin-babel": "~5.0.0",
  "@vue/cli-plugin-router": "~5.0.0",
  "@vue/cli-plugin-vuex": "~5.0.0",
  "@vue/cli-service": "~5.0.0",
  "electron": "^12.0.0",
  "electron-devtools-installer": "^3.1.0",
  "sass": "^1.32.7",
  "sass-loader": "^12.0.0",
  "vue-cli-plugin-electron-builder": "~2.1.1",
  "vue-template-compiler": "^2.6.14"
},
"browserslist": [
  "> 1%",
  "last 2 versions",
  "not dead"
]
}

6、main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

import axios from "axios";      //引入axios

Vue.prototype.$axios = axios;   //axios跟很多第三方模块不同的一点是它不能直接使用use方法,而是用这种方法

//配合文章第4步解释        本地网站
axios.defaults.baseURL = 'http://www.electron.com/index.php/api';//开发环境

//引入element组件
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

//重写提示框只提示一次
import resetMessage from '@/assets/js/resetMessage'
Vue.prototype.$resetMessage = resetMessage

Vue.use(
new VueSocketIO({
  debug: false, 
  // 宝塔   IP:端口号      (生产环境)
  connection: SocketIO('http://127.0.0.1:3000', {//开发环境
    autoConnect: false // 取消自动连接     
  }),
  extraHeaders: { 'Access-Control-Allow-Origin': '*' }
})
)

//客户端禁止按鼠标返回键返回上一个路由
window.addEventListener('popstate', function() {
history.pushState(null, null, document.URL)
})

Vue.config.productionTip = false

new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')

 

7、效果图

本地测试

npm run electron:serve

关于electronvue模仿qq登录界面的文章就介绍至此,更多相关electronvueqq登录界面内容请搜索编程宝库以前的文章,希望以后支持编程宝库

 1.引入cdn资源​<link rel="stylesheet" href="https://unpkg.com/element-ui@2.3.5/lib/theme-c ...