赞
踩
在现代 Web 开发中,前后端分离的架构已经成为主流。本文将详细介绍如何使用 Vue3、Node.js、MySQL、Electron 和 Express 实现一个完整的用户登录、文章管理和截屏功能的应用。我们将从项目的初始化开始,逐步实现各个功能模块,并提供详细的代码示例。
首先,我们使用 Vue CLI 创建一个新的 Vue3 项目:
npm install -g @vue/cli
vue create vue-electron-app
cd vue-electron-app
选择默认配置或根据需要进行自定义配置。
在项目根目录下创建一个新的文件夹 server
,并在其中初始化一个新的 Node.js 项目:
mkdir server
cd server
npm init -y
npm install express mysql body-parser cors
创建 server.js
文件并设置基本的 Express 服务器:
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 数据库和表:
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 );
在 server
文件夹中创建一个新的文件 auth.js
,并实现用户注册和登录功能:
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
中引入并使用该路由:
const authRoutes = require('./auth');
app.use('/auth', authRoutes);
在 Vue 项目中创建一个新的组件 Login.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>
在 server
文件夹中创建一个新的文件 articles.js
,并实现文章的 CRUD 操作:
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
中引入并使用该路由:
const articleRoutes = require('./articles');
app.use('/api', articleRoutes);
在 Vue 项目中创建一个新的组件 ArticleManager.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:
npm install electron --save-dev
创建 main.js
文件并配置 Electron 主进程:
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
文件并配置预加载脚本:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electron', {
captureScreen: () => ipcRenderer.invoke('capture-screen')
});
在 Vue 项目中创建一个新的组件 ScreenCapture.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!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。