使用 Vue3、Node.js、MySQL、Electron 和 Express 实现用户登录、文章管理和截屏功能

在现代 Web 开发中,前后端分离的架构已经成为主流。本文将详细介绍如何使用 Vue3、Node.js、MySQL、Electron 和 Express 实现一个完整的用户登录、文章管理和截屏功能的应用。我们将从项目的初始化开始,逐步实现各个功能模块,并提供详细的代码示例。

项目初始化

前端:Vue3

首先,我们使用 Vue CLI 创建一个新的 Vue3 项目:

bash 复制代码
npm install -g @vue/cli
vue create vue-electron-app
cd vue-electron-app

选择默认配置或根据需要进行自定义配置。

后端:Node.js 和 Express

在项目根目录下创建一个新的文件夹 server,并在其中初始化一个新的 Node.js 项目:

bash 复制代码
mkdir server
cd server
npm init -y
npm install express mysql body-parser cors

创建 server.js 文件并设置基本的 Express 服务器:

javascript 复制代码
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const app = express();
const port = 3000;

app.use(cors());
app.use(bodyParser.json());

app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

数据库:MySQL

创建一个新的 MySQL 数据库和表:

sql 复制代码
CREATE DATABASE vue_electron_app;

USE vue_electron_app;

CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(255) NOT NULL,
  password VARCHAR(255) NOT NULL
);

CREATE TABLE articles (
  id INT AUTO_INCREMENT PRIMARY KEY,
  title VARCHAR(255) NOT NULL,
  content TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

实现用户登录功能

后端:用户登录 API

server 文件夹中创建一个新的文件 auth.js,并实现用户注册和登录功能:

javascript 复制代码
const express = require('express');
const router = express.Router();
const mysql = require('mysql');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');

const db = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'vue_electron_app'
});

router.post('/register', (req, res) => {
  const { username, password } = req.body;
  const hashedPassword = bcrypt.hashSync(password, 10);

  db.query('INSERT INTO users (username, password) VALUES (?, ?)', [username, hashedPassword], (err, result) => {
    if (err) return res.status(500).send(err);
    res.status(201).send('User registered');
  });
});

router.post('/login', (req, res) => {
  const { username, password } = req.body;

  db.query('SELECT * FROM users WHERE username = ?', [username], (err, results) => {
    if (err) return res.status(500).send(err);
    if (results.length === 0) return res.status(404).send('User not found');

    const user = results[0];
    const isPasswordValid = bcrypt.compareSync(password, user.password);

    if (!isPasswordValid) return res.status(401).send('Invalid password');

    const token = jwt.sign({ id: user.id }, 'secret_key', { expiresIn: '1h' });
    res.status(200).send({ token });
  });
});

module.exports = router;

server.js 中引入并使用该路由:

javascript 复制代码
const authRoutes = require('./auth');
app.use('/auth', authRoutes);

前端:用户登录页面

在 Vue 项目中创建一个新的组件 Login.vue

vue 复制代码
<template>
  <div>
    <h2>Login</h2>
    <form @submit.prevent="login">
      <div>
        <label for="username">Username:</label>
        <input type="text" v-model="username" required />
      </div>
      <div>
        <label for="password">Password:</label>
        <input type="password" v-model="password" required />
      </div>
      <button type="submit">Login</button>
    </form>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      username: '',
      password: ''
    };
  },
  methods: {
    async login() {
      try {
        const response = await axios.post('http://localhost:3000/auth/login', {
          username: this.username,
          password: this.password
        });
        localStorage.setItem('token', response.data.token);
        this.$router.push('/dashboard');
      } catch (error) {
        console.error('Login failed:', error);
      }
    }
  }
};
</script>

实现文章管理功能

后端:文章管理 API

server 文件夹中创建一个新的文件 articles.js,并实现文章的 CRUD 操作:

javascript 复制代码
const express = require('express');
const router = express.Router();
const mysql = require('mysql');
const jwt = require('jsonwebtoken');

const db = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'vue_electron_app'
});

const authenticate = (req, res, next) => {
  const token = req.headers['authorization'];
  if (!token) return res.status(401).send('Access denied');

  jwt.verify(token, 'secret_key', (err, decoded) => {
    if (err) return res.status(401).send('Invalid token');
    req.userId = decoded.id;
    next();
  });
};

router.post('/articles', authenticate, (req, res) => {
  const { title, content } = req.body;

  db.query('INSERT INTO articles (title, content) VALUES (?, ?)', [title, content], (err, result) => {
    if (err) return res.status(500).send(err);
    res.status(201).send('Article created');
  });
});

router.get('/articles', authenticate, (req, res) => {
  db.query('SELECT * FROM articles', (err, results) => {
    if (err) return res.status(500).send(err);
    res.status(200).send(results);
  });
});

router.put('/articles/:id', authenticate, (req, res) => {
  const { id } = req.params;
  const { title, content } = req.body;

  db.query('UPDATE articles SET title = ?, content = ? WHERE id = ?', [title, content, id], (err, result) => {
    if (err) return res.status(500).send(err);
    res.status(200).send('Article updated');
  });
});

router.delete('/articles/:id', authenticate, (req, res) => {
  const { id } = req.params;

  db.query('DELETE FROM articles WHERE id = ?', [id], (err, result) => {
    if (err) return res.status(500).send(err);
    res.status(200).send('Article deleted');
  });
});

module.exports = router;

server.js 中引入并使用该路由:

javascript 复制代码
const articleRoutes = require('./articles');
app.use('/api', articleRoutes);

前端:文章管理页面

在 Vue 项目中创建一个新的组件 ArticleManager.vue

vue 复制代码
<template>
  <div>
    <h2>Article Manager</h2>
    <form @submit.prevent="createArticle">
      <div>
        <label for="title">Title:</label>
        <input type="text" v-model="title" required />
      </div>
      <div>
        <label for="content">Content:</label>
        <textarea v-model="content" required></textarea>
      </div>
      <button type="submit">Create Article</button>
    </form>
    <ul>
      <li v-for="article in articles" :key="article.id">
        <h3>{{ article.title }}</h3>
        <p>{{ article.content }}</p>
        <button @click="deleteArticle(article.id)">Delete</button>
        <button @click="editArticle(article)">Edit</button>
      </li>
    </ul>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      title: '',
      content: '',
      articles: []
    };
  },
  async created() {
    await this.fetchArticles();
  },
  methods: {
    async fetchArticles() {
      try {
        const response = await axios.get('http://localhost:3000/api/articles', {
          headers: { Authorization: localStorage.getItem('token') }
        });
        this.articles = response.data;
      } catch (error) {
        console.error('Failed to fetch articles:', error);
      }
    },
    async createArticle() {
      try {
        await axios.post('http://localhost:3000/api/articles', {
          title: this.title,
          content: this.content
        }, {
          headers: { Authorization: localStorage.getItem('token') }
        });
        this.title = '';
        this.content = '';
        await this.fetchArticles();
      } catch (error) {
        console.error('Failed to create article:', error);
      }
    },
    async deleteArticle(id) {
      try {
        await axios.delete(`http://localhost:3000/api/articles/${id}`, {
          headers: { Authorization: localStorage.getItem('token') }
        });
        await this.fetchArticles();
      } catch (error) {
        console.error('Failed to delete article:', error);
      }
    },
    editArticle(article) {
      this.title = article.title;
      this.content = article.content;
      // Implement update logic here
    }
  }
};
</script>

实现截屏功能

Electron:截屏功能

在项目根目录下安装 Electron:

bash 复制代码
npm install electron --save-dev

创建 main.js 文件并配置 Electron 主进程:

javascript 复制代码
const { app, BrowserWindow, ipcMain, desktopCapturer } = require('electron');
const path = require('path');

function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      enableRemoteModule: false,
      nodeIntegration: false
    }
  });

  win.loadURL('http://localhost:8080');
}

app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

ipcMain.handle('capture-screen', async () => {
  const sources = await desktopCapturer.getSources({ types: ['screen'] });
  return sources[0].thumbnail.toDataURL();
});

创建 preload.js 文件并配置预加载脚本:

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron');

contextBridge.exposeInMainWorld('electron', {
  captureScreen: () => ipcRenderer.invoke('capture-screen')
});

前端:截屏功能页面

在 Vue 项目中创建一个新的组件 ScreenCapture.vue

vue 复制代码
<template>
  <div>
    <h2>Screen Capture</h2>
    <button @click="captureScreen">Capture Screen</button>
    <img v-if="screenshot" :src="screenshot" alt="Screenshot" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      screenshot: null
    };
  },
  methods: {
    async captureScreen() {
      try {
        this.screenshot = await window.electron.captureScreen();
      } catch (error) {
        console.error('Failed to capture screen:', error);
      }
    }
  }
};
</script>

结语

通过本文,我们详细介绍了如何使用 Vue3、Node.js、MySQL、Electron 和 Express 实现一个完整的用户登录、文章管理和截屏功能的应用。希望这篇文章能为你提供有价值的参考,帮助你更好地理解和实现前后端分离的应用开发。

如果你有任何问题或建议,欢迎在评论区留言讨论。Happy coding!

相关推荐
学海无涯,行者无疆3 小时前
把 Web App 装进客户端——Tauri框架实战:托盘功能、消息通知、构建安装程序
electron·tauri·单例运行·web应用客户端化·托盘通知·tauri实战·tauri框架
萧曵 丶5 小时前
MySQL 主键不推荐使用 UUID 的深层原因
数据库·mysql·索引
kaico20188 小时前
MySQL的索引
数据库·mysql
资生算法程序员_畅想家_剑魔9 小时前
Mysql常见报错解决分享-01-Invalid escape character in string.
数据库·mysql
霖霖总总10 小时前
[小技巧14]MySQL 8.0 系统变量设置全解析:SET GLOBAL、SET PERSIST 与 SET PERSIST_ONLY 的区别与应用
数据库·mysql
alonewolf_9910 小时前
深入剖析MySQL索引底层:B+树、联合索引与跳跃扫描原理全解
数据库·b树·mysql
oMcLin10 小时前
如何在Debian 11上通过配置MySQL 8.0的分布式架构,提升跨区域数据同步的效率与延迟?
分布式·mysql·debian
计算机学姐11 小时前
基于SpringBoot的校园资源共享系统【个性化推荐算法+数据可视化统计】
java·vue.js·spring boot·后端·mysql·spring·信息可视化
霖霖总总11 小时前
[小技巧23]全面理解 MySQL 的 WAL 机制:原理、影响与可观测性
数据库·mysql
冰暮流星12 小时前
sql语句之select语句的基本使用
数据库·sql·mysql