diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c71141 --- /dev/null +++ b/README.md @@ -0,0 +1,270 @@ +## 九、项目 - 个人网站管理 + +### 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") + } +}); +``` \ No newline at end of file diff --git a/callback-promise-test.js b/callback-promise-test.js new file mode 100644 index 0000000..d34fe96 --- /dev/null +++ b/callback-promise-test.js @@ -0,0 +1,42 @@ +// 实现暂停功能 +function sleep(time){ + let start = Date.now(); + let i = 0; + while(true){ + let currentTime = Date.now(); + if(currentTime - start >= time){ + break; + } + } +} +// console.log(Date.now()); +// sleep(2000); +// console.log(Date.now()); + +function sleep1(time,callback){ + setTimeout(callback,time) +} + +// console.log(Date.now()); +// sleep1(2000,function(){ +// console.log(Date.now()); +// }) +function sleep2(time){ + return new Promise((resovle)=>{ + setTimeout(resovle,time); + }); +} +// console.log(Date.now()); +sleep2(2000).then(()=>{ + sleep2(2000).then(()=>{ + console.log(Date.now()); + }) +}) + +async function test(){ + console.log(Date.now()); + await sleep2(2000); + await sleep2(2000); + console.log(Date.now()); +} +test(); diff --git a/db.sql b/db.sql index 76a2c54..74522e3 100644 --- a/db.sql +++ b/db.sql @@ -51,4 +51,7 @@ select * from article limit 0,2; -- 第1页 select * from article limit 2,2; -- 第2页 select * from article limit 4,2; -- 第3页 -- 假设条数位:size 页码为: page --- 那么分页的起始位置为: (page - 1) * size \ No newline at end of file +-- 那么分页的起始位置为: (page - 1) * size +-- 搜索文章 +select id,title,category,publish_time,cover,description,view_count,comment_count +from article where title like '%测试%' or content like '%测试%'; \ No newline at end of file diff --git a/modules/article.js b/modules/article.js index 956d2f7..5267f8e 100644 --- a/modules/article.js +++ b/modules/article.js @@ -3,37 +3,46 @@ const db = require('./db'); // const router = require('express').Router(); // 用户网站模块 -// 1.查询文章列表(分页) -router.get("/web/list",async function (req, res) { - const page = req.query['page'] || 1; // 获取页码 +// 1.查询文章列表(分页|搜索) +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) * 5; - // db.pool.query("select * from article limit ?,5", [start], (err, listResult) => { - // if (err) { - // console.log(err) - // res.send([]); - // } - // else{ - // // res.send(result); - // db.pool.query('select count(*) as total from article',(err,result)=>{ - // res.send({ - // count:result, - // list:listResult - // }); - // }) - // } - // }); - - 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:[]}); + 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: [] }); + } +}); +// 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) + } +}); +// 3.更新阅读数量 +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") } }); - // 管理后台模块 module.exports = router \ No newline at end of file