前言
从这个项目开始,会通过模块的方式,将日常练习的功能迭代其中,在不断完善这个系统的同时,也会梳理完整的设计流程。这篇笔记是通过学习实践输出的项目成果,以下是在开发过程中做的笔记,可以作为学习参考。
环境准备
运行环境
- Node.js
- VScode
- Vue CLI
技术框架
- Vue3
- Typescript
- express
创建前端项目
项目初始化
创建一个新的Vue3项目,并添加Typescript语法支持、路由、状态管理
npm create vue@latest
cd workbench
npm install
安装依赖
npm install axios # 接口调用插件
项目结构
workbench/
├── src/
│ ├── api/
│ ├── ├── index.ts
│ ├── ├── login.ts
│ ├── assets/
│ ├── components/
│ ├── router/
│ ├── ├── index.ts
│ ├── stores/
│ ├── views/
│ ├── ├── Forgot/
│ ├── ├── ├── ForgotView.vue
│ ├── ├── Home/
│ ├── ├── ├── HomeView.vue
│ ├── ├── Login/
│ ├── ├── ├── LoginView.vue
│ ├── ├── Register/
│ ├── ├── ├── RegisterView.vue
│ ├── App.vue
│ ├── main.ts
├── package.json
├── .gitignore
├── vite.config.ts
├── README.md
创建后端项目
项目初始化
mkdir benchserve
npm init
安装依赖
npm install body-parser # 响应数据解析插件
npm install express # 服务框架
npm install nodemailer # 邮件收发插件
npm install sqlite3 # 数据库操作插件
npm install crypto-js # 加密插件
项目结构
benchserve/
├── src/
│ ├── common/
│ ├── data/
│ ├── models/
│ ├── ├── email.js
│ ├── ├── index.js
│ ├── ├── Personal.js
│ ├── router/
│ ├── ├── Personal.js
│ ├── index.js
├── package.json
├── .gitignore
├── README.md
前端功能开发
页面开发
- 创建登录页面:
views/Login/LoginView.vue
- 创建注册页面:
views/Register/RegisterView.vue
- 创建密码修改页面:
views/Forgot/ForgotView.vue
路由配置
引入创建好的页面路径:router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home/HomeView.vue')
},
{
path: '/forgot',
name: 'Forgot',
component: () => import('@/views/Forgot/ForgotView.vue')
},
{
path: '/register',
name: 'Register',
component: () => import('@/views/Register/RegisterView.vue')
},
{
path: '/login',
name: 'Login',
component: () => import('@/views/Login/LoginView.vue')
}
]
})
export default router
配置路由守卫,仅允许在已登录下访问首页
router.beforeEach((to) => {
const token = localStorage.getItem('token')
if (
!token &&
(to.name !== 'Login' && to.name !== 'Register' && to.name !== 'Forgot')
) {
return { name: 'Login' }
}
})
接口封装
创建一个index文件配置请求响应拦截:api/index.ts
import axios, { type AxiosInstance, type InternalAxiosRequestConfig, type AxiosResponse } from 'axios';
const baseURL: string = 'api' // 基础 URL
const request: AxiosInstance = axios.create({
baseURL,
timeout: 5000, // 超时时间
});
// 请求拦截
request.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
const token: string | null = localStorage.getItem('token')
config.headers.Authorization = `Bearer ${token}`;
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截
request.interceptors.response.use(
(response: AxiosResponse) => {
const res = response.data;
if (res.code !== 200) {
return Promise.reject(res.message);
} else {
return res;
}
},
(error) => {
return Promise.reject(error);
}
);
export default request;
配置跨域代理:vite.config.ts
server: {
proxy: {
'/api': {
target: 'http://127.0.0.1:4000/',
changeOrigin: true,
rewrite: (path) => path.replace(/^/api/, '')
}
}
}
后端功能开发
路由配置
const express = require('express')
const router = express.Router()
const url = require('url')
// 示例:账号注册
router.post("/register", function (req, res) {
let body = ''
req.on('data', (thunk) => {
body += thunk
})
req.on('end', () => {
const data = JSON.parse(body)
if(data.code == code) {
res.send(JSON.stringify({
code: 200,
message: "注册成功"
}))
}else {
res.send(JSON.stringify({
code: 400,
message: "注册失败"
}))
}
})
})
// 示例:检查账号注册情况
router.get("/checkAccount", function (req, res) {
let obj = url.parse(req.url, true).query
if(obj.flag) {
res.send(JSON.stringify({
code: 400,
message: '当前账号已注册'
}))
}else {
res.send(JSON.stringify({
code: 200,
message: '当前账号未注册'
}))
}
})
链接数据库
当前仅创建两个字段用于保存:email(邮箱),password(密码)
const sqlite = require('sqlite3').verbose()
class Personal {
constructor() {
this.db = new sqlite.Database('data/data.db', () => {
console.log('数据库打开成功')
this.db.run('CREATE TABLE IF NOT EXISTS personal (id INTEGER PRIMARY KEY AUTOINCREMENT, email VARCHAR, password VARCHAR)');
})
}
insert_personal(param) {
let add = this.db.prepare("INSERT OR REPLACE INTO personal (email, password) VALUES (?,?)");
add.run(param.email, param.password)
add.finalize()
console.log('数据插入成功')
}
update_personal(param) {
let update = this.db.prepare("UPDATE personal set password=? where email=?")
update.run(param.password, param.email)
update.finalize()
console.log('数据更新成功')
}
select_personal() {
return new Promise((resolve) => {
this.db.all("SELECT * FROM personal", (err, row) => {
resolve(row)
})
})
}
}
配置邮箱
当前项目使用的是网易邮箱,用于发送邮件信息
- 点击开启IMAP/SMTP服务,根据操作发送指定信息,会得到一个授权密码(注:授权密码仅显示一次,需要保存下来)。
const nodemailer = require('nodemailer');
let config = {
port: {
host: "smtp.163.com", // 网易邮箱默认服务地址,无需修改
port: 465, // 默认端口号
secure: true,
auth: {
user: '你的网易邮箱',
pass: '授权码'
}
},
mailOptions: { // 邮件信息配置
from: '你的网易邮箱',
to: '接收方的邮箱',
subject: 'Your verification code is: ${code}',
text: ''
}
}
function sendCodeToEmail(to, code) {
return new Promise((resolve, reject) => {
const transporter = nodemailer.createTransport(config.port);
transporter.sendMail(config.mailOptions, (error, info) => {
if(error) {
reject(error)
}else {
resolve(info)
}
});
})
}
项目运行
前端部分
npm run dev
后端部分
npm run start
后续
以上便是登录模块的完整开发流程,这一版仅是简单的初版设计,后面会不断优化和完善,让这个模块更加稳定。页面及交互的代码涉及篇幅较长,这里没有全部列出,可以根据情况自行设计。
下一章:工作台模块及个人信息模块