表白墙系统
文章目录
前言
本系统最初的模型是高校中传统的“表白墙”,以前大学食堂门口贴满A4纸的“表白墙”,逐渐演变成为一些很火的“万能墙”,但基本上通过都是QQ空间或微信朋友圈的方式发布出稿件!很费力!于是便萌生了这个想法,我们能不能用已学的知识做一个网页版的,全自动的万能墙系统。
此系统采用了layui2.0开源模块UI框架(镜像站www.layuiweb.com),感谢专业前端工程师(贤心)倾情打造,layui的样式本人十分喜爱。
一、需求分析
*MYSQL完成后台管理设计
*系统分为前后台前台展示文章,后台发布文章
*后台需要管理员的账号密码
*后台存在文章管理和栏目管理
*进入后台能够实现删除和修改发表标签功能
*在前台能够发布信息
二、系统分析
1.模块划分
2.E-R图
二、搭建准备工作(完成数据库和工具准备)
1.搭建环境
工具需要:宝塔面板
2.下载完成一键安装phpmyadmin nginx或apache php(php版本需要大于5.6以上)
3.创建数据库和数据表相关内容最后写成一键搭建
三、重要代码以及步骤
1.项目初始化和部分重要函数代码展示
init.php初始化代码:(配置文件操作)
<?php
//设置字符集
header('content-type:text/html;charset=utf-8');
ini_set('session.cookie_httponly', 1); //设置防止xss攻击
session_start(); //开启session
//调试开关
define('APP_DEBUG', true);
//开启调试时,显示错误报告
ini_set('display_errors', APP_DEBUG);
//定义前后台公共目录常量
define('APP_PATH', './app/'); //支持库文件目录
define('UPLOAD_PATH', './upload/'); //上传文件目录
//载入函数库
require APP_PATH . 'function.php';
//载入数据库函数
require APP_PATH . 'db.php';
//载入模块库
require APP_PATH . 'module.php';
app/function.php函数库部分代码
//配置文件操作
function C($name)
{
static $config = null; //保存项目中的设置
if (!$config) { //函数首次被调用时载入配置文件
$config = require APP_PATH . 'config.php';
}
return isset($config[$name]) ? $config[$name] : '';
}
app/config.php配置文件代码
return [
//数据库连接信息
'DB_CONNECT' => [
'host' => 'localhost', //服务器地址
'user' => 'wnq', //用户名
'pass' => 'PGcrPaaiTyWdcMLb', //密码
'dbname' => 'wnq', //数据库名
'port' => '3306', //端口
],
'DB_CHARSET' => 'utf8', //字符集
];
app/db.php数据库部分代码
//通过预处理方式执行SQL
function db_query($sql, $type = '', $data = [])
{
//获取数据库连接
$link = db_connect();
//预处理SQL语句
if (!$stmt = mysqli_prepare($link, $sql)) {
E('数据库操作失败。', mysqli_error($link) . "\nSQL语句:" . $sql);
}
//无参数绑定时,直接执行
if ($data == []) {
mysqli_stmt_execute($stmt);
} else {
//批量处理
$data = (array)$data; //如果不是数组,强制转换为数组
is_array(current($data)) || $data = [$data]; //自动识别批量模式
$params = array_shift($data); //准备待绑定的变量
db_bind_param($stmt, $type, $params); //参数绑定
mysqli_stmt_execute($stmt); //执行第1个
foreach ($data as $row) { //批量执行剩余的
foreach ($row as $k => $v) {
$params[$k] = $v; //变更参数值
}
mysqli_stmt_execute($stmt);
}
}
return $stmt;
}
2.管理员登录和后台相关代码
前台登录样式login.php
<div class="bg-image-pattern"></div>
<div class="layui-container">
<div class="admin-login-background">
<div class="layui-form login-form">
<form class="layui-form" action="">
<div class="layui-form-item logo-title">
<h1>系统登录</h1>
</div>
<div class="layui-form-item">
<label class="layui-icon layui-icon-username" for="username"></label>
<input type="text" id="username" lay-verify="required|account" placeholder="用户名或者邮箱" autocomplete="off" class="layui-input" >
</div>
<div class="layui-form-item">
<label class="layui-icon layui-icon-password" for="password"></label>
<input type="password" id="password" lay-verify="required|password" placeholder="密码" autocomplete="off" class="layui-input" value="">
</div>
<div class="layui-form-item">
<div class="layui-btn layui-btn-fluid login" lay-submit="" lay-filter="login">登 入</div>
</div>
</form>
</div>
</div>
</div>
后台登陆api/login.php
// 初始化
require './init.php';
class Login
{
/**
* 登录
* @return array|string
*/
public static function inLogin()
{
$username = I('username', 'post', '');
$password = I('password', 'post', '');
// 查询列表
$result = module_login_to($username, $password);
$arr = [];
if ($result) {
$arr = [
'code' => 200,
'tips' => "登录成功",
];
$_SESSION['islogin'] = true;
} else {
$arr = [
'code' => 100,
'tips' => "账号密码错误",
];
$_SESSION['islogin'] = false;
}
return toJson($arr);
}
后台相关功能部分代码:(留言增删api/comment.php,api/post.php,标签增删api/tag.php,)
// 初始化
require './init.php';
class Comment
{
/**
* 留言
* @return array|string
*/
public static function CommentList()
{
$id = I('id', 'post', 'string', "");
if ($id == "") {
return toJson([
'code' => 100,
'data' => [],
'tips' => "ID不能为空"
]);
}
// 查询列表
$result = module_comment_list($id);
$arr = [];
foreach ($result as $key => $row) {
$data[] = [
'id' => $id,
'cid' => $row['c_id'],
'content' => $row['c_content'],
'post_id' => $row['post_id'],
'avatar' => $row['user_id'] ? qqAvatar($row['user_id']) : 'asset/images/anonymous.png',
"c_time" => TimeAgo($row['c_time']),
"i" => $key + 1
];
}
$arr = [
'islogin' => $_SESSION['islogin'] ? '1' : '',
'code' => 200,
'data' => $data,
];
return toJson($arr);
}
/**
* 添加留言
*/
public static function SaveComment()
{
//id
$id = I('id', 'post', 'string', "");
if ($id == "") {
return toJson([
'code' => 100,
'data' => [],
'tips' => "ID不能为空"
]);
}
//QQ
$qq = baoguoInput($_POST['qq']);
//内容
$content = baoguoInput($_POST['content']);
// 表单类型验证
get_form_type($qq, $qq, $content);
//敏感词拦截
get_fuck($content);
//判断内容长度
get_text_length($content, 2);
$result = db_exec(DB_AFFECTED, 'INSERT INTO `post_comment` (`post_id`,`user_id`,`c_content`,`c_time`) VALUES(?,?,?,?)', 'ssss', [$id, $qq, $content, $_SERVER["REQUEST_TIME"]]);
//判断插入状态
if ($result) {
$sup = module_comment_count($id);
$sups = isset($sup['count']) ? $sup['count'] : 0;
$data = ['code' => 200, 'tips' => '发布成功,数据重载中', "count" => $sups];
exit(toJson($data));
} else {
$data = ['code' => 400, 'tips' => "发布失败"];
exit(toJson($data));
}
}
/**
* 删除留言
*/
public static function delComment()
{
$id = I('id', 'post', '');
if (module_comment_del($id)) {
return toJson([
'code' => 200,
'tips' => "删除成功",
]);
} else {
return toJson([
'code' => 500,
'tips' => "删除失败",
]);
}
}
}
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (I('save', 'post', 'string', "") == 1) {
// 添加数据
Comment::SaveComment();
} else if (I('save', 'post', 'string', "") == 2) {
if (isset($_SESSION['islogin']) && $_SESSION['islogin']) {
Comment::delComment();
} else {
exit(toJson(['code' => 400, 'tips' => '非法访问']));
}
} else {
// 获取列表
Comment::CommentList();
}
} else {
exit(toJson(['code' => 400, 'tips' => '非法访问']));
}
3.用户发表留言和前台相关代码
发送缩略图片api/image.php,留言内容限制,相关样式代码
html代码
<!--loading-->
<div id="loading"></div>
<!--背景-->
<div class="bg-image"></div>
<div class="bg-image-pattern"></div>
<!--提示-->
<div class="post-loading">
<div class="post-tips"></div>
</div>
<!--header-->
<div class="header">
<div class="top" id="main">
<div class="logo action"><a class="home">万能墙</a></div>
<span class="start"><i class="baoguo-write-l" style="margin-right:0px;font-weight: 600;"></i></span>
</div>
</div>
<!--发布表单-->
<div class="addPost">
<div class="inputPost" id="inputPost">
<!--<input type="text" id="code" name="code" placeholder="qq邮箱验证码">-->
<div class="tagList">
</div>
<!--<input type="text" id="tag" name="tag" placeholder="点击进行选择" readonly>-->
<textarea id="content" placeholder="说点你想说的话..."></textarea>
<div class="inputBottom">
<input type="text" id="qq" name="qq" placeholder="你的QQ...">
<a class="anonymous"><i class="baoguo-user-l"></i></a>
<a class="upimage"><i class="baoguo-image-l add"></i></a>
<input type="file" name="image" id="image" accept="image/*" style="display:none">
<div class="submit s1"><img src="/asset/images/submit.png"></div>
</div>
<div id="yulan-image"><img class="yulan"></div>
</div>
</div>
<!--评论表单-->
<div class="addComment">
<div class="inputComment inputPost">
<textarea id="ccontent" placeholder="说点你想说的话..."></textarea>
<div class="inputBottom">
<input type="text" id="cqq" name="qq" placeholder="你的QQ...">
<div class="submit s2"><img src="/asset/images/submit.png"></div>
<div class="submit-close closeComment">取消</div>
</div>
<div id="yulan-image"><img class="yulan"></div>
<div>
<style>
.ly-color .layui-timeline-item {
color: #ff7b8c;
}
</style>
<ul class="layui-timeline ly-color" id="CommentList">
<li class="layui-timeline-item">
<div class="layui-timeline-content layui-text">
<div class="user">
<div class="desc"><img class="avatar" src="">
<div>
<div class="title"> 一楼 </div>
<div class="subtitle">11分钟前 | 来自网页端</div>
</div>
</div>
</div>
<p class="comment-content">
万能墙的一切准备工作似乎都已到位。发布之弦,一触即发。
<br>不枉近百个日日夜夜与之为伴。因小而大,因弱而强。
<br>无论它能走多远,抑或如何支撑?至少我曾倾注全心,无怨无悔
</p>
</div>
</li>
</ul>
</div>
</div>
</div>
<!--帖子-->
<div class="post"></div>
<!--更多-->
<div class="more"><a><i class="baoguo-angle-down-l"></i></a></div>
<!--footer-->
<div class="footer">万能墙系统
<br>
此页面仅供演示使用!
<br>
发送缩略图app/image.php
//图像处理函数
//定义常量,表示缩略图缩放类型
define('IMAGE_FILL', 0); //等比例填充白色背景
define('IMAGE_SCALE', 1); //等比例缩放
define('IMAGE_SCALE_FILL', 2); //等比例缩放,最大缩放
/**
* 为图片生成缩略图
* @param int $mode 缩略图生成模式
* @param string $file_path 原图路径
* @param int $new_width 目标宽度
* @param int $new_height 目标高度
* @param array $config 保存选项
* @return array [0]表示成功或失败,成功时[1]表示文件路径,失败时[1]表示错误信息
*/
function image_thumb($mode, $file_path, $new_width, $new_height, $config=[]){
//获取原图的宽高,并判断文件类型
$info = getimagesize($file_path);
$width = $info[0]; //图片宽度
$height = $info[1]; //图片高度
$mime = $info['mime']; //图片类型
//关联图像 MIME 类型和文件扩展名
$file_type = [
'image/png' => '.png',
'image/jpeg' => '.jpg'
];
//判断图像类型,只允许 JPG 和 PNG 两种类型
if(!isset($file_type[$mime])){
return [false, '图片创建缩略图失败:只支持 jpg 和 png 类型的图片。'];
}
//生成缩略图
$im = image_create($mime, $file_path);
switch($mode){
case IMAGE_FILL:
$im = image_thumb_fill($im, $width, $height, $new_width, $new_height);
break;
case IMAGE_SCALE_FILL:
$im = image_thumb_scale($im, $width, $height, $new_width, $new_height, true);
break;
default:
$im = image_thumb_scale($im, $width, $height, $new_width, $new_height);
}
//准备保存选项
$config = array_merge([
'base_path' => './', //上传基本路径
'sub_path' => date('Y-m/d/'), //自动生成的子目录
'name'=> md5(uniqid(rand())).$file_type[$mime] //自动创建文件名
], $config);
//自动创建目录
$save_path = $config['base_path'].$config['sub_path'];
if(!file_exists($save_path) && !mkdir($save_path, 0777, true)){
return [false, '文件保存失败:创建目录失败'];
}
//将保存缩略图到指定目录
image_save($mime, $im, $save_path.$config['name']);
//返回文件路径(不包括base_path)
return [true, $config['sub_path'].$config['name']];
}
/**
* 生成缩略图(等比例填充白色背景)
* @param resource $im 原图资源
* @param int $width 原图宽度
* @param int $height 原图高度
* @param int $new_width 目标宽度
* @param int $new_height 目标高度
* @return 缩略图资源
*/
function image_thumb_fill($im, $width, $height, $new_width, $new_height){
//等比例缩放计算
if($width/$new_width > $height/$new_height) {
$dst_width = $new_width;
$dst_height = round($new_width / $width * $height);
}else{
$dst_height = $new_height;
$dst_width = round($new_height / $height * $width);
}
//创建缩略图资源
$thumb = imagecreatetruecolor($new_width, $new_height);
//填充白色背景
imagefill($thumb, 0, 0, imagecolorallocate($thumb, 255, 255, 255));
//计算缩略图在画布上的位置
$dst_x = ($new_width - $dst_width) / 2;
$dst_y = ($new_height - $dst_height) / 2;
//将按比例将缩略图重新采样,调整其位置
imagecopyresampled($thumb, $im, $dst_x, $dst_y, 0, 0, $dst_width, $dst_height, $width, $height);
return $thumb;
}
/**
* 生成缩略图(等比例缩放)
* @param resource $im 原图资源
* @param int $width 原图宽度
* @param int $height 原图高度
* @param int $max_width 最大宽度
* @param int $max_height 最大高度
* @param bool $fill 是否最大缩放
* @return 缩略图资源
*/
function image_thumb_scale($im, $width, $height, $max_width, $max_height, $fill=false){
$dst_width = $width;
$dst_height = $height;
if($width/$max_width > $height/$max_height) {
if($fill || ($width > $max_width)){
$dst_width = $max_width;
$dst_height = round($max_width / $width * $height);
}
}else{
if($fill || ($height > $max_height)){
$dst_height = $max_height;
$dst_width = round($max_height / $height * $width);
}
}
//创建缩略图资源
$thumb = imagecreatetruecolor($dst_width, $dst_height);
//将原图缩放填充到缩略图画布中
imagecopyresampled($thumb, $im, 0, 0, 0, 0, $dst_width, $dst_height, $width, $height);
return $thumb;
}
/**
* 根据原图文件创建图像资源
* @param string 图像MIME类型
* @param string 原图文件路径
* @return 返回创建的图像资源
*/
function image_create($mime, $file_path){
switch($mime){
case 'image/png': return imagecreatefrompng($file_path);
case 'image/jpeg': return imagecreatefromjpeg($file_path);
}
}
/**
* 保存图像资源
* @param string $mime 图像MIME类型
* @param resource $im 图像资源
* @param string $save_path 保存路径
* @return 成功返回true
*/
function image_save($mime, $im, $save_path){
switch($mime){
case 'image/png': return imagepng($im, $save_path);
case 'image/jpeg': return imagejpeg($im, $save_path, 100);
}
}
生成分页app/page.php
//分页函数
//获取SQL分页 Limit
function page_sql($page, $size){
return ($page-1) * $size . ',' . $size;
}
//生成URL参数
function page_url(){
$params = $_GET;
unset($params['page']);
return http_build_query($params);
}
//获取分页HTML(简单效果)
function page_html_simple($total, $page, $size){
//计算总页数
$maxpage = max(ceil($total/$size), 1);
//如果不足2页,则不显示分页导航
if($maxpage <= 1) return '';
//获取URL参数字符串
$url = page_url();
$url = $url ? "?$url&page=" : '?page=';
//拼接 首页
$first = "<a href=\"{$url}1\">首页</a>";
//拼接 上一页
$prev = ($page==1) ? '<span>上一页</span>' :
'<a href="'.$url.($page-1).'">上一页</a>';
//拼接 下一页
$next = ($page==$maxpage) ? '<span>下一页</span>' :
'<a href="'.$url.($page+1).'">下一页</a>';
//拼接 尾页
$last = "<a href=\"{$url}{$maxpage}\">尾页</a>";
//组合最终样式
return "$first $prev $next $last 当前为:$page/$maxpage";
}
//获取分页HTML
function page_html($total, $page, $size, $num=5){
$page = max((int)$page, 1); //当前访问的页码,最低为1
$maxpage = max(ceil($total/$size), 1); //计算总页数
$num = floor($num/2); //计算当期页前后显示的相关链接个数
$url = page_url(); //获取URL参数字符串
//拼接URL
$url = $url ? "?$url&page=" : '?page=';
$html = ''; //保存拼接结果
//生成首页、上一页
if($page > 1){
$html .= "<a href=\"{$url}1\">首页</a>";
$html .= '<a href="'.$url.($page-1).'">上一页</a>';
}else{
$html .= '<span>首页</span>';
$html .= '<span>上一页</span>';
}
//生成分页导航
$start = $page - $num;
$end = $page + $num;
if($start < 1){
$end = $end+(1-$start);
$start = 1;
}
if($end > $maxpage){
$start = $start-($end-$maxpage);
$end = $maxpage;
}
($start < 1) && $start = 1;
($start > 1) && $html .= '<i>...</i>';
for($i=$start; $i<=$end; ++$i){
if($i == $page){
$html .= "<a href=\"{$url}{$i}\" class=\"curr\">$i</a>";
} else {
$html .= "<a href=\"{$url}{$i}\">$i</a>";
}
}
($end < $maxpage) && $html .= '<i>...</i>';
//生成下一页、尾页
if($page == $maxpage){
$html .= '<span>下一页</span>';
$html .= '<span>尾页</span>';
}else{
$html .= '<a href="'.$url.($page+1).'">下一页</a>';
$html .= "<a href=\"{$url}{$maxpage}\">尾页</a>";
}
//返回结果
return $html;
}
留言字数限制app/function.php
function get_text_length($content, $len = 3)
{
//最少字数限制
$minCommentlength = $len;
//最多字数限制
$maxCommentlength = 500;
$pointCommentlength = mb_strlen($content, 'utf-8');
if ($pointCommentlength < $minCommentlength) {
header("Content-type: text/html; charset=utf-8");
$data = array(
'code' => 400,
'tips' => '内容最少输入10字'
);
exit(json_encode($data));
}
if ($pointCommentlength > $maxCommentlength) {
header("Content-type: text/html; charset=utf-8");
$data = [
'code' => 400,
'tips' => '内容最多输入500字'
];
exit(json_encode($data));
}
}