## 九、项目 - 个人网站管理 ### 9.1 介绍 ### 9.2 功能模块 #### 整体采用前后端分离 后端: nodejs + expressjs + mysql 完成数据存取,如何验证是否已经登录(session、cookie) 前端:vue + element-ui #### 模块 **用户模块**:管理用户的登录、退出、信息修改等 **文章模块**:添加文章、修改文章、查询文章、删除文章、文件的上传等 - **前台模块** : 查询文章列表(分页|搜索)、查询文章的详情、更新文章的数据、文章的搜索、 **评论模块**:添加评论、查询评论、删除评论(管理员) ### 9.3 快速开始 - 搭建项目框架 1、使用npm初始化项目 ```shell mkdir pro-blog cd pro-blog # 使用npm初始化项目 npm init -y ``` 2、安装项目所需的依赖 ```shell npm i express mysql vue element-ui npm i -D nodemon ``` 3、package.json - 项目的信息 提交项目时不要提交node_modules,只需要有package.json文件,执行`npm i` ```json { "name": "pro-blog", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "nodemon index.js" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "element-ui": "^2.15.8", "express": "^4.18.1", "mysql": "^2.18.1", "vue": "^3.2.33" }, "devDependencies": { "nodemon": "^2.0.16" } } ``` 4、入口文件 - index.js ```javascript const express = require("express") const user = require('./modules/user') // 用户模块 const article = require('./modules/article') // 文章 const app = express(); // 创建express应用 app.use(express.static('public')) // 设置静态文件路径 // 使用路由模块 app.use('/api/user',user) app.use('/api/article',article) app.listen(3000); // 监听端口 console.log('server run success! http://localhost:3000') ``` 5、user 与 article 模块 ```javascript // article.js const router = require('express').Router(); // 创建一个路由对象 // 实现相应路由功能 // .... module.exports = router // 暴露路由对象 // user.js const router = require('express').Router(); module.exports = router ``` ### 9.4 数据库结构 用户登录表: 登录信息ID、登录时间、登录IP、客户端标识、有效期 文章表: 文章ID、标题、封面、发布时间、描述(一部分的文章内容)、文章内容、分类、阅读量、评论数、状态 评论表: 评论ID、文章ID、评论内容、发布时间、评论者IP ```sql create DATABASE pro_blog; use pro_blog; -- 用户登录表: 登录信息ID、登录时间、登录IP、客户端标识、有效期 create table login( id int(10) PRIMARY KEY AUTO_INCREMENT, login_time datetime not null, BIGINT -- xxx.xxx.xxx.xx -> ipv4 -- xxxx:xxxx:xxxx::xxxx -> ipv6 ip varchar(40) not null, client varchar(200) null, expire_time datetime null ); -- 文章表: 文章ID、标题、封面、发布时间、描述(一部分的文章内容)、文章内容、分类、阅读量、评论数、状态 create table article( id int(10) PRIMARY KEY AUTO_INCREMENT, title varchar(50) not null, cover varchar(200) null, publish_time datetime null, description varchar(500) null, content text not null, category varchar(20) not null, view_count int default 0, comment_count int default 0, status tinyint(1) default 1 -- 状态 1标识草稿 2表示已发布 0表示已删除 ); -- 评论表: 评论ID、文章ID、评论内容、发布时间、评论者IP create table comment ( id int(10) PRIMARY KEY AUTO_INCREMENT, article_id int(10) not null, content varchar(500) not null, publish_time datetime not null, ip varchar(40) not null ); ``` ### 9.6 功能实现 #### 9.6.1 文章模块 ##### 1、公共数据库查询工具模块 ```javascript // 数据查询工具封装 const mysql = require('mysql') // 创建数据库连接池 const pool = mysql.createPool({ user: 'root', password: '123', database: 'pro_blog', timezone: 'Asia/Shanghai' }); // 执行查询操作 返回Promise对象 function query(sql, values = []) { return new Promise(function (resolve, reject) { // resolve是成功的回调函数 reject是错误的回调函数 pool.query(sql, values, function (err, result) { if (err) { reject(err) // 有错误 直接回调错误的函数 } else { resolve(result) // 直接正常的回调 } }); }); } // 暴露接口 module.exports = { pool, createConnection: pool.getConnection, query } ``` ##### 2、实现分页查询文章列表 article.js ```javascript const db = require('./db'); // // 文章模块 const router = require('express').Router(); // 1.查询文章列表(分页) router.get("/web/list",async function (req, res) { const page = req.query['page'] || 1; // 获取页码 // 默认每页5条 const start = (page - 1) * 5; try{ const listResult = await db.query("select * from article limit ?,5", [start]); // 执行查询语句并等待结果出来后赋值给变量 const totalCount = await db.query('select count(*) as total from article'); // 执行获取总条数 res.send({count:totalCount[0]['total'],list:listResult,size:5}); }catch(e){ console.log(err) res.send({count:0,list:[]}); } }); module.exports = router ``` ##### 3、实现关键字查询 article.js ```javascript router.get("/web/list", async function (req, res) { const page = parseInt(req.query['page'] || 1); // 获取页码 const size = parseInt(req.query['size'] || 5); // 每天条数 // 默认每页5条 const start = (page - 1) * size; const search = '%' + (req.query['search'] || '') + '%'; // 搜索关键字 try { const listResult = await db.query( `select id,title,category,publish_time,cover,description,view_count,comment_count from article where title like ? or content like ? limit ?,?`, [search, search, start, size]); // 执行查询语句并等待结果出来后赋值给变量 const totalCount = await db.query( `select count(*) as total from article where title like ? or content like ?`, [search, search]); // 执行获取总条数 res.send({ total: totalCount[0]['total'], list: listResult, size: size }); } catch (e) { console.log(e) res.send({ count: 0, list: [] }); } }); ``` ##### 4、实现查询文章详情 article.js ```javascript // 2.查询单个文章详情 router.all('/web/detail',async (req,res)=>{ let id = req.query['id']; if(!id || id <1){ res.send([]) }else{ const article = await db.query('select * from article where id =?',[id]); res.send(article) } }); ``` ##### 5、实现更新文章阅读数 article.js ```javascript router.all('/web/update',async (req,res)=>{ let id = req.query['id']; if(!id || id <1){ res.send("参数不对") }else{ const article = await db.query('update article set view_count=view_count + 1 where id =?',[id]); res.send("true") } }); ```