目录
- 项目简介:
- 项目目标:
- 效果展示:
- [一. 创建项目并初始化](#一. 创建项目并初始化)
- [二. 项目初始化](#二. 项目初始化)
- [三. 安装项目所需要的包](#三. 安装项目所需要的包)
- [四. 创建需要的数据库](#四. 创建需要的数据库)
- [五. 编写app.js](#五. 编写app.js)
- [六. 创建前端页面](#六. 创建前端页面)
项目简介:
这个项目是一个基于Node.js和Express框架构建的简易博客系统,用户可以在该系统中进行注册、登录、发布文章、查看文章、删除文章以及留言等操作。项目利用MySQL数据库存储用户和文章数据,并通过前端页面提供用户友好的交互界面。以下是项目的主要功能简介:
主要功能
用户注册与登录
- 用户可以通过提供用户名和密码进行注册,注册后凭借用户名和密码登录系统。 * 系统会对用户密码进行加密储存,提高安全性。
文章管理
- 登录用户可以发布新的文章,包括输入标题和内容。
- 用户可以查看所有已发布的文章列表,并可以通过文章ID查找具体文章的详细内容。
- 登录用户可以删除自己发布的文章。
留言板功能
- 用户可以在留言板上发布留言和查看其他用户的留言。
- 留言也可以被删除。
文章与留言分页
- 在文章列表和留言板上支持分页功能,每页显示固定数量的内容,用户可翻页查看更多内容。
用户会话管理
- 系统通过Session管理用户登录状态,确保用户的安全性与隐私。
前端页面
- 采用HTML、CSS和jQuery构建响应式用户界面,前端页面包括登录页、注册页、文章发布页、文章详情页、留言板等。
技术栈
- 后端:Node.js, Express, MySQL
- 前端:HTML, CSS, JavaScript (jQuery)
- 其他:bcrypt用于密码安全,Axios用于进行API请求。
项目目标:
该项目的目标是提供一个简易的博客系统,使得用户能够方便地进行文章管理和互动留言,增强用户之间的联系与交流。适合于学习Node.js、Express和前后端交互的初学者。
效果展示:
登录页
注册页
首页
文章详情页
添加文章页
留言页
修改和删除文章页
修改文章页
一. 创建项目并初始化
项目结构
二. 项目初始化
bash
//进入项目文件夹执行命令初始化
npm init -y
三. 安装项目所需要的包
bash
npm i bcrypt body-parser cors express express-session mysql
四. 创建需要的数据库
创建数据库和表用户表、文章表、留言表
sql
/*
Navicat Premium Dump SQL
Source Server : weblog2
Source Server Type : MySQL
Source Server Version : 80037 (8.0.37)
Source Host : localhost:3306
Source Schema : notebook
Target Server Type : MySQL
Target Server Version : 80037 (8.0.37)
File Encoding : 65001
Date: 17/12/2024 19:15:06
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for article
-- ----------------------------
DROP TABLE IF EXISTS `article`;
CREATE TABLE `article` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
`title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
`content` varchar(9999) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
`time` varchar(99) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 84 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_bin ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for leaving
-- ----------------------------
DROP TABLE IF EXISTS `leaving`;
CREATE TABLE `leaving` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
`content` varchar(9999) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
`time` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 54 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_bin ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`username` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL,
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_bin ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
五. 编写app.js
连接数据库和所有后端接口代码都在这里,都写在app.js
bash
//导入express模块
const express = require('express')
const bodyParser = require('body-parser')
const session = require('express-session')
//导入 mysql 模块
const mysql = require('mysql')
const bcrypt = require('bcrypt')
const cors = require('cors')
const fs = require('fs')
//建立与 MySQL 数据库的连接关系
const db = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'root',
database: 'notebook',
})
//创建app应用
const app = express()
app.use(cors(
{
origin: 'http://localhost',
credentials: true
}
))
//使用session中间件
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
cookie: { maxAge: 1000 * 60 * 60 * 24 }
}))
//定义post传递的格式
app.use(express.static('./pages'))
// 静态资源目录
app.use('/public', express.static('./public'))
//使用body-parser中间件
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
//登录接口
app.post('/api/login', (req, res) => {
const sqlStr = 'SELECT * FROM user WHERE username = ?'
const params = [req.body.username]
db.query(sqlStr, params, (err, results) => {
if (err) {
console.error(err.message)
return res.status(500).send({ message: '服务器内部错误' })
}
if (results.length === 0) {
return res.status(400).send({ message: '该用户不存在' })
}
// 验证密码
bcrypt.compare(req.body.password, results[0].password, (err, validPassword) => {
if (err) {
console.error(err.message)
return res.status(500).send({ message: '服务器内部错误' })
}
if (!validPassword) {
return res.status(401).send({ message: '密码错误' })
}
req.session.user = req.body.username;
req.session.islogin = true;
res.status(200).send({ message: '登录成功' })
})
})
})
//注册接口
app.post('/api/register', (req, res) => {
const sqlStr = 'SELECT * FROM user WHERE username = ?'
const params = [req.body.username]
db.query(sqlStr, params, (err, results) => {
if (err) {
console.error(err.message)
return res.status(500).send({ message: '服务器内部错误' })
}
if (results.length > 0) {
return res.status(400).send({ message: '该用户已存在' })
}
// 对密码进行哈希处理
bcrypt.hash(req.body.password, 10, (err, hashedPassword) => {
if (err) {
console.error(err.message)
return res.status(500).send({ message: '服务器内部错误' })
}
const sqlInsert = 'INSERT INTO user (username, password) VALUES (?, ?)'
db.query(sqlInsert, [req.body.username, hashedPassword], (err, results) => {
if (err) {
console.error(err.message);
return res.status(500).send({ message: '服务器内部错误' })
}
res.status(201).send({ message: '注册成功' })
})
})
})
})
// 获取用户姓名接口
app.get('/api/username', (req, res) => {
//从 Session 中获取用户的名称,响应给客户端
if (req.session.islogin && req.session.user) {
return res.send({
status: 200,
message: '获取用户名成功',
username: req.session.user // 返回用户名
})
}
})
// 退出登录接口
app.post('/api/logout', (req, res) => {
// 清除session
req.session.destroy()
res.send({ status: 200, message: '退出登录成功' })
})
// 获取文章列表接口
app.get('/api/getArticle', (req, res) => {
const sqlStr = 'SELECT * FROM article';
db.query(sqlStr, (err, results) => {
if (err) {
return res.status(500).send({
status: 500,
message: '服务器内部错误'
});
}
res.send({
status: 200,
message: '获取文章成功',
data: results // 将所有文章数据返回
});
});
});
// 新增文章接口
app.post('/api/addArticle', (req, res) => {
// 获取当前时间
let time = new Date().toLocaleString()
// 检查请求体中是否包含必要字段
if (!req.body.title || !req.body.content) {
return res.status(400).send({
status: 400,
message: '标题和内容不能为空',
})
}
const sqlStr = 'INSERT INTO article (username, title, content, time) VALUES (?,?,?,?)'
const params = [req.body.username, req.body.title, req.body.content, time]
db.query(sqlStr, params, (err, results) => {
if (err) {
console.log(err.message);
return res.status(500).send({
status: 500,
message: '服务器内部错误',
})
}
res.send({
status: 200,
message: '新增文章成功',
data: {
id: results.insertId, // 返回新文章的ID
username: req.session.user,
title: req.body.title,
content: req.body.content,
time: time
}
})
})
})
// 查找文章接口
app.post('/api/search', (req, res) => {
let time = new Date().toLocaleString()
const sqlStr = 'SELECT * FROM article WHERE id = ?'
const params = [req.body.id];
db.query(sqlStr, params, (err, results) => {
if (err) {
console.log(err.message);
return res.status(500).send({
status: 500,
message: '服务器内部错误',
})
}
if (results.length === 0) {
return res.send({
status: 404,
message: '未找到文章',
})
}
res.send({
status: 200,
message: '查找文章成功',
data: results[0] // 如果按ID查找,返回单个结果
})
})
})
// 删除文章接口
app.post('/api/delete', (req, res) => {
const sqlStr = 'DELETE FROM article WHERE id = ?'
const params = [req.body.id]
db.query(sqlStr, params, (err, results) => {
if (err) {
console.error(err.message)
return res.status(500).send({
status: 500,
message: '服务器内部错误'
})
}
if (results.affectedRows === 0) {
return res.status(404).send({
status: 404,
message: '未找到该文章'
})
}
res.send({
status: 200,
message: '删除文章成功'
})
})
})
// 动态获取文章内容接口
app.get('/api/article/:_id', (req, res) => {
const id = parseInt(req.params._id) // 获取并转为整数
const sqlStr = 'SELECT * FROM article WHERE id = ?'
const params = [id]
db.query(sqlStr, params, (err, results) => {
if (err) {
console.error(err.message)
return res.status(500).send({
status: 500,
message: '服务器内部错误'
})
}
if (results.length === 0) {
return res.status(404).send({
status: 404,
message: '未找到该文章'
})
}
// 发送 JSON 格式的响应
res.send({
status: 200,
message: '获取文章成功',
data: {
username: results[0].username,
title: results[0].title,
content: results[0].content,
time: results[0].time
}
})
})
})
//获取留言列表接口
app.get('/api/getlist', (req, res) => {
const sqlStr = 'SELECT * FROM leaving'
db.query(sqlStr, (err, results) => {
if (err) {
console.error(err.message)
return res.status(500).send({
status: 500,
message: '服务器内部错误'
})
}
// 获取总留言数
const totalCount = results.length;
res.send({
status: 200,
message: '获取评论列表成功',
data: results,
totalCount: totalCount, // 返回总条数
username: req.session.user // 返回当前登录的用户名
})
})
})
//新增留言接口
app.post('/api/addlist', (req, res) => {
// 获取当前时间
let time = new Date().toLocaleString()
const sqlStr = 'INSERT INTO leaving (username, content, time) VALUES (?,?,?)'
const params = [req.body.username, req.body.content, time]
db.query(sqlStr, params, (err, results) => {
if (err) {
console.error(err.message)
return res.status(500).send({
status: 500,
message: '服务器内部错误'
})
}
res.send({
status: 200,
message: '新增评论成功',
data: {
id: results.insertId, // 返回新留言的ID
username: req.session.user,
content: req.body.content,
time: time
}
})
})
})
// 删除留言接口
app.post('/api/deleteList', (req, res) => {
const sqlStr = 'DELETE FROM leaving WHERE id = ?'
const params = [req.body.id]
db.query(sqlStr, params, (err, results) => {
if (err) {
console.error(err.message)
return res.status(500).send({
status: 500,
message: '服务器内部错误'
})
}
if (results.affectedRows === 0) {
return res.status(404).send({
status: 404,
message: '未找到该评论'
})
}
res.send({
status: 200,
message: '删除评论成功'
})
})
})
// 留言分页查询接口
app.post('/api/limitList', (req, res) => {
const start = parseInt(req.body.num, 10) || 0; // 默认从第0条开始
const count = 10; // 每页显示10条
const sqlStr = 'SELECT * FROM leaving LIMIT ?, ?';
const params = [start, count];
db.query(sqlStr, params, (err, results) => {
if (err) {
return res.status(500).send({
status: 500,
message: '服务器内部错误'
});
}
res.send({
status: 200,
data: results
});
});
});
// 分页查询接口
app.post('/api/limit', (req, res) => {
const start = parseInt(req.body.num, 10) || 0; // 默认从第0条开始
const count = 10; // 每页显示10条
const sqlStr = 'SELECT * FROM article LIMIT ?, ?';
const params = [start, count];
db.query(sqlStr, params, (err, results) => {
if (err) {
return res.status(500).send({
status: 500,
message: '服务器内部错误'
});
}
res.send({
status: 200,
data: results
});
});
});
// 获取文章总数接口
app.get('/api/getArticleCount', (req, res) => {
const sqlStr = 'SELECT COUNT(*) AS count FROM article';
db.query(sqlStr, (err, results) => {
if (err) {
return res.status(500).send({
status: 500,
ge: '服务器内部错误'
});
}
res.send({
status: 200,
totalCount: results[0].count
});
});
});
app.listen(80, () => {
console.log('server is running at http://localhost:80')
})
六. 创建前端页面
登录页 login.html
bash
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录页</title>
<script src="../public/jQuery.js"></script>
<script src="../public/axios.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
height: 100%;
font-family: Arial, sans-serif;
}
.container {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(to right, #4facfe, #00f2fe);
}
.login-wrapper {
background-color: #ffffff;
width: 360px;
border-radius: 15px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
padding: 40px 30px;
}
.header {
font-size: 36px;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
}
.input-item {
width: 100%;
padding: 10px;
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 14px;
outline: none;
transition: border-color 0.3s;
}
.input-item:focus {
border-color: #4facfe;
}
.btn {
display: block;
width: 100%;
padding: 12px;
margin-top: 20px;
background: linear-gradient(to right, #4facfe, #00f2fe);
color: #fff;
text-align: center;
border: none;
border-radius: 5px;
cursor: pointer;
transition: transform 0.2s;
}
.btn:hover {
background-image: linear-gradient(to right, #00f2fe, #4facfe);
color: #000;
transform: translateY(-3px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.msg {
text-align: center;
margin-top: 20px;
}
.tankuang {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #ee3b3b;
color: white;
border-radius: 5px;
padding: 15px 20px;
z-index: 1000;
}
</style>
</head>
<body>
<div class="tankuang" id="error_message"></div>
<div class="container">
<div class="login-wrapper">
<div class="header">登录</div>
<form id="form_login">
<input type="text" name="username" placeholder="账号" class="input-item" id="user" required>
<input type="password" name="password" placeholder="密码" class="input-item" id="pwd" required>
<button type="submit" class="btn" id="login_btn">登录</button>
<button type="button" class="btn" id="register_btn">去注册</button>
</form>
<div class="msg">
有问题? <b>请联系QQ: 2655372128</b>
</div>
</div>
</div>
<script>
$(function () {
const apiUrl = 'http://localhost/api/login';
// 显示错误消息的通用函数
function showError(message) {
$('#error_message').text(message).fadeIn().delay(2000).fadeOut();
}
// 发送 API 请求的通用函数
function apiRequest(method, url, data = null) {
if (data === null) {
return $.ajax({
method: method,
url: url,
});
} else {
return $.ajax({
method: method,
url: url,
data: data,
});
}
}
// 登录处理函数
function handleLogin(event) {
event.preventDefault();
const data = $('#form_login').serialize();
apiRequest('POST', apiUrl, data)
.then(function (res) {
if (res.status === 400) {
showError('账号不存在,请先注册');
} else if (res.status === 401) {
showError('密码错误,请重新登录');
} else {
window.location.href = './index.html';
}
})
.catch(function () {
showError('服务器出现问题,请稍后再试。');
});
}
// 事件绑定
$('#form_login').on('submit', handleLogin);
$('#register_btn').click(function () {
window.location.href = './register.html';
});
});
</script>
</body>
</html>
注册页 register.html
bash
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>注册页</title>
<script src="../public/jQuery.js"></script>
<script src="../public/axios.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
height: 100%;
font-family: Arial, sans-serif;
}
.container {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(to right, #4facfe, #00f2fe);
}
.register-wrapper {
background-color: #ffffff;
width: 360px;
border-radius: 15px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
padding: 40px 30px;
}
.header {
font-size: 36px;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
}
.input-item {
width: 100%;
padding: 10px;
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 14px;
outline: none;
transition: border-color 0.3s;
}
.input-item:focus {
border-color: #4facfe;
}
.btn {
display: block;
width: 100%;
padding: 12px;
margin-top: 20px;
background: linear-gradient(to right, #4facfe, #00f2fe);
color: #fff;
text-align: center;
border: none;
border-radius: 5px;
cursor: pointer;
transition: transform 0.2s;
}
.btn:hover {
background-image: linear-gradient(to right, #00f2fe, #4facfe);
color: #000;
transform: translateY(-3px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.msg {
text-align: center;
margin-top: 20px;
}
.notification {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #ee3b3b;
color: white;
border-radius: 5px;
padding: 15px 20px;
z-index: 1000;
}
</style>
</head>
<body>
<div class="notification" id="error_message"></div>
<div class="container">
<div class="register-wrapper">
<div class="header">注册</div>
<form id="form_register">
<input type="text" name="username" placeholder="账号" class="input-item" id="user" required>
<input type="password" name="password" placeholder="密码" class="input-item" id="pwd" required>
<button type="submit" class="btn">注册</button>
<button type="submit" class="btn" id="back_btn">返回登录</button>
</form>
<div class="msg">
有问题? <b>请联系QQ: 2655372128</b>
</div>
</div>
</div>
<script>
$(function () {
const apiUrl = 'http://localhost/api/register'; // 注册 API 地址
// 显示错误消息的通用函数
function showError(message, redirectUrl = null) {
$('#error_message').text(message).fadeIn().delay(2000).fadeOut();
if (redirectUrl) {
setTimeout(function () {
location.href = redirectUrl; // 跳转至指定页面
}, 2000);
}
}
// 发送 API 请求的通用函数
function apiRequest(method, url, data = null) {
return data ? $.ajax({ method, url, data }) : $.ajax({ method, url });
}
// 注册表单提交处理
$('#form_register').on('submit', function (e) {
e.preventDefault();
const data = $(this).serialize();
apiRequest('POST', apiUrl, data)
.then(function (res) {
if (res.message === '注册成功') { // 使用message而不是status判断
showError('注册成功,跳转到登录页面...', './login.html');
} else {
showError('账号已存在,请重新注册');
}
})
.catch(function () {
showError('服务器错误,请稍后再试');
});
});
// 返回登录按钮点击处理
$('#back_btn').on('click', function () {
location.href = './login.html'; // 跳转到登录页
});
});
</script>
</body>
</html>
首页 index.html
bash
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>首页</title>
<script src="../public/jQuery.js"></script>
<link rel="stylesheet" href="../public/bootstrap.min.css">
<script src="../public/axios.js"></script>
<style>
body {
background-color: #f8f9fa;
font-family: Arial, sans-serif;
}
.navbar {
background-color: #007bff;
color: white;
}
.navbar-brand,
.navbar-nav li a {
color: white !important;
cursor: pointer;
}
.navbar-brand:hover,
.navbar-nav li a:hover {
color: #000 !important;
}
.container {
margin-top: 20px;
}
.header {
font-size: 32px;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
}
.article-list {
background-color: #ffffff;
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.article-title-bg {
background-color: #007bff;
/* 设置背景色 */
padding: 10px;
border-radius: 5px;
/* 添加圆角 */
text-align: center;
margin-bottom: 20px;
color: white;
}
.article-item {
display: flex;
justify-content: space-between;
padding: 10px;
border-bottom: 1px solid #e0e0e0;
}
.article-title {
flex-grow: 1;
margin-right: 20px;
}
.pagination {
display: flex;
justify-content: center;
margin-top: 20px;
}
.pagination li {
list-style: none;
margin: 0 5px;
display: flex;
align-items: center;
}
.pagination .page-link {
text-decoration: none;
padding: 8px 12px;
border: none;
border-radius: 5px;
color: white;
background-image: linear-gradient(to right, #4facfe, #00f2fe);
transition: background-color 0.3s, color 0.3s;
}
.pagination .page-link:hover {
background-image: linear-gradient(to right, #00f2fe, #4facfe);
color: #000;
transform: translateY(-3px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.pagination .page-link:disabled {
background-color: #e0e0e0;
color: #a0a0a0;
cursor: not-allowed;
}
.pagination .page-number {
display: flex;
align-items: center;
height: 100%;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg">
<div class="container-fluid">
<a class="navbar-brand" href="./index.html">首页</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto ml-auto">
<li class="nav-item">
<a class="nav-link" href="./add_article.html">添加文章</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./edit_delete_article.html">修改和删除文章</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./leaving.html">留言板</a>
</li>
<li class="nav-item">
<span class="navbar-text" id="username"></span>
</li>
<li class="nav-item">
<a class="nav-link" id="btnLogout">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="header">欢迎来到乐风刂(快乐自由)的博客</div>
<div class="article-list">
<h2 class="article-title-bg">文章列表</h2>
<div id="articleList"></div>
<nav aria-label="Page navigation" class="limit">
<ul class="pagination" id="pagination">
<li class="page-item"><a class="page-link" id="prevPage" href="#">上一页</a></li>
<li class="page-item"><span id="currentPage">1</span> / <span id="totalPages">1</span></li>
<li class="page-item"><a class="page-link" id="nextPage" href="#">下一页</a></li>
</ul>
</nav>
</div>
</div>
<script>
$(function () {
const apiUrl = 'http://localhost/api';
let currentPage = 1; // 当前页面
const pageSize = 10; // 每页显示10条
// 获取用户名
function fetchUsername() {
axios.get(`${apiUrl}/username`, { withCredentials: true })
.then(function (res) {
if (res.data.status === 200) {
$('#username').text(`用户: ${res.data.username}`);
} else {
$('#username').text('未登录');
}
})
.catch(function (error) {
console.error('获取用户名失败:', error);
});
}
// 获取文章列表
function fetchArticles(page) {
axios.post(`${apiUrl}/limit`, { num: (page - 1) * pageSize })
.then(function (res) {
const articles = res.data.data;
let articleListHtml = '';
articles.forEach(article => {
articleListHtml += `
<div class="article-item">
<div class="article-title">
<a href="./detail_page.html?id=${article.id}">${article.title}</a>
</div>
<div>用户: ${article.username} | 发布时间: ${article.time}</div>
</div>
`;
});
$('#articleList').html(articleListHtml);
updatePagination();
})
.catch(function (error) {
console.error('获取文章列表失败:', error);
});
}
// 更新分页
function updatePagination() {
axios.get(`${apiUrl}/getArticleCount`) // 假设有接口可以返回文章总数
.then(function (res) {
const totalArticles = res.data.totalCount;
const totalPages = Math.ceil(totalArticles / pageSize);
$('#totalPages').text(totalPages);
$('#currentPage').text(currentPage);
// 控制前后按钮显示
$('#prevPage').prop('disabled', currentPage === 1);
$('#nextPage').prop('disabled', currentPage === totalPages);
});
}
$('#prevPage').click(function () {
if (currentPage > 1) {
currentPage--;
fetchArticles(currentPage);
}
});
$('#nextPage').click(function () {
const totalPages = parseInt($('#totalPages').text());
if (currentPage < totalPages) {
currentPage++;
fetchArticles(currentPage);
}
});
// 退出登录
$('#btnLogout').click(function () {
axios.post(`${apiUrl}/logout`)
.then(function (res) {
if (res.status === 200) {
location.href = './login.html';
}
})
.catch(function () {
alert('退出登录失败,请重试');
});
});
// 页面加载时获取用户名和文章
fetchUsername();
fetchArticles(currentPage);
});
</script>
</body>
</html>
添加文章页 add_article.html
bash
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>添加文章</title>
<script src="../public/jQuery.js"></script>
<link rel="stylesheet" href="../public/bootstrap.min.css">
<script src="../public/axios.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
body {
background-color: #f8f9fa;
/* 背景色 */
font-family: Arial, sans-serif;
}
.navbar {
background-color: #007bff;
/* 导航栏颜色 */
color: white;
}
.navbar-brand,
.navbar-nav li a {
color: white !important;
/* 导航文字颜色 */
cursor: pointer;
/* 使得导航链接显示手形光标 */
}
.navbar-brand:hover,
.navbar-nav li a:hover {
color: #000 !important;
}
.container {
margin-top: 20px;
}
.form-wrapper {
background-color: #fff;
border-radius: 15px;
padding: 40px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
max-width: 800px;
margin: auto;
}
.header {
font-size: 32px;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
}
.input-item {
width: 100%;
padding: 10px;
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 16px;
outline: none;
}
textarea {
height: 400px;
}
.btn {
width: 100%;
padding: 12px;
background-image: linear-gradient(to right, #4facfe, #00f2fe);
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: transform 0.2s;
}
.btn:hover {
background-image: linear-gradient(to right, #00f2fe, #4facfe);
transform: translateY(-3px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.tankuang {
display: none;
position: fixed;
background: linear-gradient(to right, #4facfe, #00f2fe);
z-index: 99;
width: 200px;
height: 100px;
text-align: center;
line-height: 100px;
border-radius: 5px;
top: 30%;
left: 50%;
transform: translate(-50%, -50%);
}
#layer_msg {
color: #ffffff;
font-size: 20px;
}
</style>
</head>
<body>
<div class="tankuang">
<div id="header">
<span id="layer_msg">提交成功...</span>
</div>
</div>
<nav class="navbar navbar-expand-lg">
<div class="container-fluid">
<a class="navbar-brand" href="./index.html">返回首页</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto ml-auto">
<li class="nav-item">
<a class="nav-link" id="btnLogout">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="form-wrapper">
<div class="header">写文章</div>
<form id="form_add">
<input type="text" class="input-item" name="title" placeholder="文章标题" required>
<h5>文章内容</h5>
<textarea class="input-item" rows="10" name="content" placeholder="请输入文章内容" required></textarea>
<button type="submit" class="btn">发布文章</button>
</form>
<div class="msg text-center mt-3">
有问题? <b>请联系QQ: 2655372128</b>
</div>
</div>
</div>
<script>
$(function () {
const apiUrl = 'http://localhost/api';
// 显示消息的通用函数
function showMessage(message, redirect = false) {
document.querySelector('.tankuang').style.display = 'block';
$('#layer_msg').text(message);
if (redirect) {
setTimeout(function () {
location.href = './index.html';
}, 800);
} else {
setTimeout(function () {
document.querySelector('.tankuang').style.display = 'none';
}, 800);
}
}
// 发送 API 请求的通用函数
function apiRequest(method, url, data = {}) {
return axios({
method: method,
url: url,
data: data,
withCredentials: true // 确保可以携带凭证
});
}
// 发布文章
$('#form_add').on('submit', function (e) {
e.preventDefault();
const data = $(this).serialize();
// 检查文章内容,自动在开头添加两个空格
const content = $('#form_add textarea[name="content"]').val();
const formattedContent = ' ' + content; // 添加两个空格
const formattedData = data.replace(content, formattedContent); // 替换内容字符串
apiRequest('POST', `${apiUrl}/addArticle`, data)
.then(function (res) {
if (res.data.status === 200) {
showMessage('文章发布成功', true);
} else {
showMessage('文章发布失败');
}
})
.catch(function () {
showMessage('请求失败,请稍后再试');
});
});
// 获取用户信息判断是否登录
apiRequest('GET', `${apiUrl}/username`)
.then(function (res) {
if (res.status !== 200) {
showMessage('请先完成登录', true);
}
})
.catch(function () {
showMessage('获取用户信息失败,请重试', true);
});
// 退出登录
$('#btnLogout').click(function () {
apiRequest('POST', `${apiUrl}/logout`)
.then(function (res) {
if (res.status === 200) {
location.href = './login.html';
}
})
.catch(function () {
showMessage('退出登录失败,请重试');
});
});
});
</script>
</body>
</html>
文章详情页 detail_page.html
bash
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文章详情页</title>
<script src="../public/jQuery.js"></script>
<link rel="stylesheet" href="../public/bootstrap.min.css">
<script src="../public/axios.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
body {
background-color: #f8f9fa;
/* 背景色 */
font-family: Arial, sans-serif;
}
.navbar {
background-color: #007bff;
/* 导航栏颜色 */
color: white;
}
.navbar-brand,
.navbar-nav li a {
color: white !important;
/* 导航文字颜色 */
cursor: pointer;
/* 使得导航链接显示手形光标 */
}
.container {
margin-top: 20px;
}
.article-wrapper {
background-color: #ffffff;
border-radius: 15px;
padding: 40px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
max-width: 800px;
margin: auto;
}
.header {
font-size: 32px;
font-weight: bold;
margin-bottom: 20px;
text-align: center;
}
.content {
font-size: 18px;
line-height: 1.6;
margin-bottom: 20px;
white-space: pre-wrap;
/* 保留换行符和空格,同时文本在容器内自动换行 */
word-wrap: break-word;
/* 长单词自动换行 */
}
.article-meta {
font-size: 14px;
color: #888;
margin-bottom: 20px;
text-align: center;
}
#btnComment {
background-image: linear-gradient(to right, #4facfe, #00f2fe);
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
transition: background-color 0.3s;
/* 添加过渡效果 */
}
#btnComment:hover {
background-image: linear-gradient(to right, #00f2fe, #4facfe);
/* 鼠标悬停时颜色反转 */
}
.tankuang {
display: none;
position: fixed;
background-image: linear-gradient(to right, #4facfe, #00f2fe);
z-index: 99;
width: 200px;
height: 100px;
text-align: center;
line-height: 100px;
border-radius: 5px;
top: 30%;
left: 50%;
transform: translate(-50%, -50%);
}
#layer_msg {
color: #ffffff;
font-size: 20px;
}
.separator {
height: 1px;
background-color: #e0e0e0;
/* 分割线颜色 */
margin: 20px 0;
/* 分割线的上下间距 */
}
</style>
</head>
<body>
<div class="tankuang">
<div id="header">
<span id="layer_msg">加载中...</span>
</div>
</div>
<nav class="navbar navbar-expand-lg">
<div class="container-fluid">
<a class="navbar-brand" href="./index.html">返回首页</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto ml-auto">
<li class="nav-item">
<a class="nav-link user"></a>
</li>
<li class="nav-item">
<a class="nav-link" id="btnLogout">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="article-wrapper">
<div class="header" id="articleTitle">文章标题</div>
<div class="article-meta" id="articleMeta">
<span class="username">用户: </span>
<span class="time">发布于: </span>
</div>
<div class="separator"></div>
<pre class="content" id="articleContent">文章内容将显示在这里...</pre>
<div class="text-center">
<button class="btn btn-primary" id="btnComment">查看留言</button>
</div>
</div>
</div>
<script>
$(function () {
const apiUrl = 'http://localhost/api';
const urlParams = new URLSearchParams(window.location.search);
const articleId = urlParams.get('id'); // 从 URL 中获取文章 ID
// 显示消息的通用函数
function showMessage(message, redirectUrl = null) {
$('#error_message').text(message).fadeIn().delay(2000).fadeOut();
if (redirectUrl) {
setTimeout(function () {
location.href = redirectUrl; // 页面重定向
}, 2000);
}
}
// 发送 API 请求的通用函数
function apiRequest(method, url, data = null) {
return data ? $.ajax({ method, url, data }) : $.ajax({ method, url });
}
// 获取文章详情
apiRequest('POST', `${apiUrl}/search`, { id: articleId })
.then(function (res) {
if (res.status === 200) {
$('#articleTitle').text(res.data.title);
$('#articleContent').html(res.data.content); // 使用 .html() 以支持 HTML 内容
$('.username').text('用户: ' + res.data.username);
$('.time').text('发布于: ' + res.data.time);
} else {
showMessage('未找到文章');
}
})
.catch(function () {
showMessage('请求失败,请稍后再试');
});
// 获取用户名判断是否登录
apiRequest('GET', `${apiUrl}/username`)
.then(function (res) {
if (res.status === 200) {
$('.user').text(res.username);
} else {
showMessage('请先完成登录', './login.html'); // 重定向到登录页面
}
})
.catch(function () {
showMessage('获取用户信息失败,请重试', './login.html'); // 重定向到登录页面
});
// 退出登录
$('#btnLogout').click(function () {
apiRequest('POST', `${apiUrl}/logout`)
.then(function (res) {
if (res.status === 200) {
location.href = './login.html'; // 登录成功后重定向
}
})
.catch(function () {
showMessage('退出登录失败,请重试');
});
});
// 查看留言按钮
$('#btnComment').click(function () {
location.href = './leaving.html'; // 跳转到留言板
});
});
</script>
</body>
</html>
留言板 leaving.html
bash
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>留言板</title>
<script src="../public/jQuery.js"></script>
<link rel="stylesheet" href="../public/bootstrap.min.css">
<script src="../public/axios.js"></script>
<style>
body {
background-color: #f8f9fa;
font-family: Arial, sans-serif;
}
.navbar {
background-color: #007bff;
color: white;
}
.navbar-brand,
.navbar-nav li a {
color: white !important;
cursor: pointer;
}
.navbar-brand:hover,
.navbar-nav li a:hover {
color: #000 !important;
}
.container {
margin-top: 20px;
}
.form-wrapper {
background-color: #fff;
border-radius: 15px;
padding: 20px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
max-width: 800px;
margin: auto;
border: none;
}
.header {
font-size: 32px;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th,
td {
border: 1px solid #ccc;
padding: 10px;
text-align: left;
max-width: 400px;
overflow-wrap: break-word;
word-wrap: break-word;
overflow: hidden;
}
th {
background-color: #f2f2f2;
}
.btn-delete {
background: linear-gradient(to right, #4facfe, #00f2fe);
color: white;
border: none;
border-radius: 5px;
padding: 10px 15px;
cursor: pointer;
display: block;
margin: 0 auto;
}
.btn-delete:hover {
background-image: linear-gradient(to right, #00f2fe, #4facfe);
color: #000;
transform: translateY(-3px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.btn-submit {
width: 100%;
padding: 12px;
background-image: linear-gradient(to right, #4facfe, #00f2fe);
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: transform 0.2s;
}
.btn-pagination {
background: linear-gradient(to right, #4facfe, #00f2fe);
color: white;
border: none;
border-radius: 5px;
padding: 10px 15px;
cursor: pointer;
display: inline-block;
margin: 10px auto;
width: fit-content;
}
.btn-pagination:hover {
background-image: linear-gradient(to right, #00f2fe, #4facfe);
color: #000;
transform: translateY(-3px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.btn {
width: 100%;
padding: 12px;
background-image: linear-gradient(to right, #4facfe, #00f2fe);
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: transform 0.2s;
}
.btn:hover {
background-image: linear-gradient(to right, #00f2fe, #4facfe);
transform: translateY(-3px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
textarea {
width: 100%;
padding: 10px;
border-radius: 5px;
border: 1px solid #ccc;
outline: none;
}
.pagination-wrapper {
text-align: center;
margin-top: 20px;
}
.tankuang {
display: none;
position: fixed;
background: linear-gradient(to right, #4facfe, #00f2fe);
z-index: 99;
width: 300px;
height: auto;
text-align: center;
line-height: 100px;
border-radius: 5px;
top: 30%;
left: 50%;
transform: translate(-50%, -50%);
color: #ffffff;
font-size: 20px;
white-space: nowrap;
/* 防止换行 */
overflow: hidden;
/* 超出部分隐藏 */
text-overflow: ellipsis;
/* 超出部分用省略号表示 */
}
</style>
</head>
<body>
<div class="tankuang" id="successMessage">删除成功...</div>
<div class="tankuang" id="publishMessage">发布成功...</div>
<div class="tankuang" id="errorMessage">留言内容不能为空...</div>
<div class="tankuang" id="deleteErrorMessage">删除留言失败...</div>
<div class="tankuang" id="confirmDeleteMessage" style="height: auto; padding: 20px;">
<div style="margin-bottom: 10px; font-size: 18px;">您确定要删除这条留言吗?</div>
<div style="display: flex; justify-content: center; gap: 10px;">
<button class="btn" id="confirmDelete">确认</button>
<button class="btn" id="cancelDelete">取消</button>
</div>
</div>
<nav class="navbar navbar-expand-lg">
<div class="container-fluid">
<a class="navbar-brand" href="./index.html">返回首页</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto ml-auto">
<li class="nav-item">
<a class="nav-link" id="btnLogout">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="form-wrapper">
<div class="header">留言板</div>
<textarea id="messageInput" rows="5" placeholder="请输入留言..."></textarea>
<button class="btn" id="btnSubmit">发布留言</button>
<table>
<thead>
<tr>
<th>留言内容</th>
<th>时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="messageList"></tbody>
</table>
<div class="pagination-wrapper" id="pagination">
<button class="btn-pagination" id="prevPage">上一页</button>
<span id="currentPage">1</span> / <span id="totalPages">1</span>
<button class="btn-pagination" id="nextPage">下一页</button>
</div>
</div>
</div>
<script>
$(function () {
const messages = [];
const apiUrl = 'http://localhost/api';
let currentPage = 1;
const itemsPerPage = 10; // 每页显示的留言数量
let totalMessages = 0;
fetchMessages();
function fetchMessages() {
axios.get(`${apiUrl}/getlist`)
.then(function (res) {
if (res.data.status === 200) {
totalMessages = res.data.totalCount;
const totalPages = Math.ceil(totalMessages / itemsPerPage);
if (totalMessages > itemsPerPage) {
$('#pagination').show(); // 显示分页按钮
$('#totalPages').text(totalPages); // 更新总页数
} else {
$('#pagination').hide(); // 隐藏分页按钮
}
loadPage(currentPage);
}
})
.catch(function (error) {
console.error('获取留言列表失败:', error);
});
}
function loadPage(page) {
axios.post(`${apiUrl}/limitList`, { num: (page - 1) * itemsPerPage })
.then(function (res) {
if (res.data.status === 200) {
messages.length = 0;
res.data.data.forEach(msg => {
messages.push({
id: msg.id,
message: msg.content,
timestamp: msg.time
});
});
$('#currentPage').text(page);
displayMessages();
}
})
.catch(function (error) {
console.error('分页获取留言失败:', error);
});
}
$('#btnSubmit').click(function () {
const message = $('#messageInput').val().trim();
if (message) {
axios.post(`${apiUrl}/addlist`, { content: message })
.then(function (res) {
if (res.data.status === 200) {
$('#publishMessage').text('发布成功...').show();
setTimeout(function () {
$('#publishMessage').fadeOut();
}, 2000);
// 直接推入新消息对象
messages.unshift({
id: res.data.newId,
message: message,
timestamp: new Date().toLocaleString() // 当前时间
});
displayMessages(); // 更新显示
$('#messageInput').val('');
fetchMessages(); // 刷新留言列表
}
})
.catch(function (error) {
console.error('发布留言失败:', error);
});
} else {
$('#errorMessage').text('留言内容不能为空...').show();
setTimeout(function () {
$('#errorMessage').fadeOut();
}, 2000);
}
});
function displayMessages() {
$('#messageList').empty();
messages.forEach((msg) => {
$('#messageList').append(`
<tr>
<td style="white-space: pre-wrap; word-wrap: break-word;">${msg.message}</td>
<td>${msg.timestamp}</td>
<td><button class="btn-delete" data-id="${msg.id}">删除</button></td>
</tr>
`);
});
}
$(document).on('click', '.btn-delete', function () {
const id = $(this).data('id');
// 显示确认删除弹窗
$('#confirmDeleteMessage').show();
// 处理确认删除
$('#confirmDelete').off('click').on('click', function () {
axios.post(`${apiUrl}/deleteList`, { id })
.then(function (res) {
if (res.data.status === 200) {
$('#successMessage').text('删除成功...').show();
setTimeout(function () {
$('#successMessage').fadeOut();
}, 2000);
fetchMessages();
}
})
.catch(function (error) {
console.error('删除留言失败:', error);
$('#deleteErrorMessage').text('删除留言失败,请重试...').show();
setTimeout(function () {
$('#deleteErrorMessage').fadeOut();
}, 2000);
});
$('#confirmDeleteMessage').hide(); // 隐藏确认弹窗
});
// 处理取消删除
$('#cancelDelete').off('click').on('click', function () {
$('#confirmDeleteMessage').hide(); // 隐藏确认弹窗
});
});
$('#prevPage').click(function () {
if (currentPage > 1) {
currentPage--;
loadPage(currentPage);
}
});
$('#nextPage').click(function () {
const totalPages = Math.ceil(totalMessages / itemsPerPage);
if (currentPage < totalPages) {
currentPage++;
loadPage(currentPage);
}
});
$('#btnLogout').click(function () {
axios.post(`${apiUrl}/logout`)
.then(function (res) {
if (res.status === 200) {
location.href = './login.html';
}
})
.catch(function () {
alert('退出登录失败,请重试');
});
});
});
</script>
</body>
</html>
修改文章 edit_article.html
bash
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>修改文章</title>
<script src="../public/jQuery.js"></script>
<link rel="stylesheet" href="../public/bootstrap.min.css">
<script src="../public/axios.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
body {
background-color: #f8f9fa;
font-family: Arial, sans-serif;
}
.navbar {
background-color: #007bff;
color: white;
}
.navbar-brand,
.navbar-nav li a {
color: white !important;
cursor: pointer;
}
.navbar-brand:hover,
.navbar-nav li a:hover {
color: #000 !important;
}
.container {
margin-top: 20px;
}
.form-wrapper {
background-color: #fff;
border-radius: 15px;
padding: 40px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
max-width: 800px;
margin: auto;
}
.header {
font-size: 32px;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
}
.input-item {
width: 100%;
padding: 10px;
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 16px;
outline: none;
}
textarea {
height: 500px;
}
.btn-save {
width: 100%;
padding: 12px;
background-image: linear-gradient(to right, #4facfe, #00f2fe);
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: transform 0.2s;
}
.btn-save:hover {
background-image: linear-gradient(to right, #00f2fe, #4facfe);
transform: translateY(-3px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.tankuang {
display: none;
position: fixed;
background: linear-gradient(to right, #4facfe, #00f2fe);
z-index: 99;
width: 200px;
height: 100px;
text-align: center;
line-height: 100px;
border-radius: 5px;
top: 30%;
left: 50%;
transform: translate(-50%, -50%);
color: #ffffff;
font-size: 20px;
}
</style>
</head>
<body>
<div class="tankuang" id="successMessage">修改成功...</div>
<nav class="navbar navbar-expand-lg">
<div class="container-fluid">
<a class="navbar-brand" href="./index.html">返回首页</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto ml-auto">
<li class="nav-item">
<a class="nav-link" id="btnLogout">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="form-wrapper">
<div class="header">修改文章</div>
<form id="editArticleForm">
<input type="hidden" id="articleId" name="articleId">
<div class="mb-3">
<label for="title" class="form-label">标题</label>
<input type="text" class="input-item" id="title" name="title" placeholder="请输入文章标题" required>
</div>
<div class="mb-3">
<label for="content" class="form-label">内容</label>
<textarea class="input-item" id="content" name="content" rows="5" placeholder="请输入文章内容"
required></textarea>
</div>
<button type="submit" class="btn-save">保存修改</button>
</form>
</div>
</div>
<script>
$(function () {
const apiUrl = 'http://localhost/api';
const urlParams = new URLSearchParams(window.location.search);
const articleId = urlParams.get('id');
// 获取文章详情
function fetchArticle(id) {
axios.post(`${apiUrl}/search`, { id: id })
.then(function (res) {
if (res.status === 200) {
$('#articleId').val(res.data.data.id);
$('#title').val(res.data.data.title);
$('#content').val(res.data.data.content);
} else {
alert('未找到文章');
}
})
.catch(function () {
alert('请求失败,请稍后再试');
});
}
// 提交修改文章表单
$('#editArticleForm').on('submit', function (e) {
e.preventDefault();
const id = $('#articleId').val();
const title = $('#title').val();
const content = $('#content').val();
axios.post(`${apiUrl}/updateArticle`, { id, title, content })
.then(function (res) {
$('#successMessage').text('修改成功...').show();
setTimeout(function () {
$('#successMessage').fadeOut();
window.location.href = `./detail_page.html?id=${id}`;
}, 2000);
})
.catch(function (error) {
const response = error.response.data;
alert(response.message);
});
});
// 退出登录
$('#btnLogout').click(function () {
axios.post(`${apiUrl}/logout`)
.then(function (res) {
if (res.status === 200) {
location.href = './login.html';
}
})
.catch(function () {
alert('退出登录失败,请重试');
});
});
// 页面加载时获取文章信息
if (articleId) {
fetchArticle(articleId);
}
});
</script>
</body>
</html>
修改和删除文章 edit_delete_article.html
bash
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>修改和删除文章</title>
<script src="../public/jQuery.js"></script>
<link rel="stylesheet" href="../public/bootstrap.min.css">
<script src="../public/axios.js"></script>
<style>
body {
background-color: #f8f9fa;
font-family: Arial, sans-serif;
}
.navbar {
background-color: #007bff;
color: white;
}
.navbar-brand,
.navbar-nav li a {
color: white !important;
cursor: pointer;
}
.navbar-brand:hover,
.navbar-nav li a:hover {
color: #000 !important;
}
.container {
margin-top: 20px;
}
.form-wrapper {
background-color: #fff;
border-radius: 15px;
padding: 20px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
max-width: 800px;
margin: auto;
}
.header {
font-size: 32px;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
}
.article-item {
display: flex;
justify-content: space-between;
/* 使按钮右对齐 */
align-items: center;
padding: 10px;
border-bottom: 1px solid #ccc;
}
.article-item div {
flex-grow: 1;
/* 让标题部分占用剩余空间 */
}
.btn-delete {
background: linear-gradient(to right, #4facfe, #00f2fe);
color: white;
border: none;
border-radius: 5px;
padding: 5px 10px;
cursor: pointer;
}
.btn-delete:hover {
background-image: linear-gradient(to right, #00f2fe, #4facfe);
color: #000;
transform: translateY(-3px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.btn-edit {
background: linear-gradient(to right, #4facfe, #00f2fe);
color: white;
border: none;
border-radius: 5px;
padding: 5px 10px;
cursor: pointer;
margin-right: 5px;
/* 增加右边距以便于分隔 */
}
.btn-edit:hover {
background-image: linear-gradient(to right, #00f2fe, #4facfe);
color: #000;
transform: translateY(-3px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.tankuang {
display: none;
position: fixed;
background: linear-gradient(to right, #4facfe, #00f2fe);
z-index: 99;
width: 200px;
height: 100px;
text-align: center;
line-height: 100px;
border-radius: 5px;
top: 30%;
left: 50%;
transform: translate(-50%, -50%);
color: #ffffff;
font-size: 20px;
}
.pagination {
display: flex;
justify-content: center;
margin-top: 20px;
}
.pagination li {
list-style: none;
margin: 0 5px;
display: flex;
align-items: center;
}
.pagination .page-link {
text-decoration: none;
padding: 8px 12px;
border: none;
border-radius: 5px;
color: white;
background-image: linear-gradient(to right, #4facfe, #00f2fe);
transition: background-color 0.3s, color 0.3s;
}
.pagination .page-link:hover {
background-image: linear-gradient(to right, #00f2fe, #4facfe);
color: #000;
transform: translateY(-3px);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.pagination .page-link:disabled {
background-color: #e0e0e0;
color: #a0a0a0;
cursor: not-allowed;
}
.pagination .page-number {
display: flex;
align-items: center;
height: 100%;
}
</style>
</head>
<body>
<div class="tankuang" id="successMessage">删除成功...</div>
<nav class="navbar navbar-expand-lg">
<div class="container-fluid">
<a class="navbar-brand" href="./index.html">返回首页</a>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto ml-auto">
<li class="nav-item">
<a class="nav-link" id="btnLogout">退出登录</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="form-wrapper">
<div class="header">文章列表</div>
<div id="articleList"></div>
<!-- 新的分页结构 -->
<nav aria-label="Page navigation" class="limit">
<ul class="pagination" id="pagination">
<li class="page-item"><a class="page-link" id="prevPage" href="#">上一页</a></li>
<li class="page-item"><span id="currentPage">1</span> / <span id="totalPages">1</span></li>
<li class="page-item"><a class="page-link" id="nextPage" href="#">下一页</a></li>
</ul>
</nav>
</div>
</div>
<script>
$(function () {
const apiUrl = 'http://localhost/api';
let currentPage = 1; // 当前页
const pageSize = 10; // 每页条数
// 获取文章列表
function fetchArticles(page) {
axios.post(`${apiUrl}/limit`, { num: (page - 1) * pageSize })
.then(function (res) {
const articles = res.data.data;
let articleListHtml = '';
articles.forEach(article => {
articleListHtml += `
<div class="article-item">
<div>${article.title}</div>
<button class="btn-edit" data-id="${article.id}">修改</button> <!-- 修改按钮 -->
<button class="btn-delete" data-id="${article.id}">删除</button>
</div>
`;
});
$('#articleList').html(articleListHtml);
updatePagination(); // 更新分页
})
.catch(function (error) {
console.error('获取文章列表失败:', error);
});
}
// 更新分页
function updatePagination() {
axios.get(`${apiUrl}/getArticleCount`)
.then(function (res) {
const totalArticles = res.data.totalCount;
const totalPages = Math.ceil(totalArticles / pageSize);
$('#totalPages').text(totalPages);
$('#currentPage').text(currentPage);
// 控制前后按钮显示
$('#prevPage').prop('disabled', currentPage === 1);
$('#nextPage').prop('disabled', currentPage === totalPages);
});
}
// 删除文章
$(document).on('click', '.btn-delete', function () {
const articleId = $(this).data('id');
axios.post(`${apiUrl}/delete`, { id: articleId })
.then(function (res) {
$('#successMessage').text('删除成功...').show();
setTimeout(function () {
$('#successMessage').fadeOut();
}, 2000);
fetchArticles(currentPage); // 重新获取文章列表
})
.catch(function (error) {
const response = error.response.data;
alert(response.message);
});
});
// 修改按钮点击事件
$(document).on('click', '.btn-edit', function () {
const articleId = $(this).data('id');
location.href = `./edit_article.html?id=${articleId}`;
});
// 分页点击事件
$('#prevPage').click(function () {
if (currentPage > 1) {
currentPage--;
fetchArticles(currentPage);
}
});
$('#nextPage').click(function () {
// 假设有个接口提供总页数
const totalPages = parseInt($('#totalPages').text());
if (currentPage < totalPages) {
currentPage++;
fetchArticles(currentPage);
}
});
// 退出登录
$('#btnLogout').click(function () {
axios.post(`${apiUrl}/logout`)
.then(function (res) {
if (res.status === 200) {
location.href = './login.html';
}
})
.catch(function () {
alert('退出登录失败,请重试');
});
});
// 页面加载时获取文章
fetchArticles(currentPage);
});
</script>
</body>
</html>