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

来自:网络
时间:2022-12-31
阅读:
目录

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

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

返回顶部
顶部