博客项目2

一、昨日回顾

1. 知识回顾

  1. mvc的三大组件是什么?

    M:model 模型

    V:view 视图

    C:Controller 控制器

  2. mvc的三大组件分别代表什么意思?

    M:专门用来操作数据表的

    V:专门用来展示视图模板的

    C:专门用来处理业务逻辑,比如说什么时候该调用模型操作数据,什么时候该调用视图展示模板,都是由控制器里来处理。

2. 昨日反馈

1531270551320

二、知识路径

  • web项目开发流程介绍

  • 需求分析

    ​ 功能分析

    ​ ER图设计

  • 表设计

  • 项目部署

  • 项目实现

    ​ 实现后台用户管理系统

    ​ 封装验证码工具类

==目标:能够设计项目所需的表、能够部署自主框架、能够实现后台用户管理系统==

三、今日课程内容:博客项目

1. web项目开发流程介绍

1)制定计划:博客项目(blog31) 开发周期:4天

2)需求分析:功能分析 ER图设计

3)软件设计:项目环境 表设计

4)程序编写

5)软件测试

6)运行维护

2. 需求分析

功能分析

  1. 后台用户管理系统;
  2. 后台分类管理系统;
  3. 后台博文管理系统;
  4. 评论管理系统;
  5. 后台登陆和七天免登录;
  6. 前台首页;
  7. 前台注册和登陆功能;
  8. 前台博客文章详情页;

ER图设计

E-R图也称实体-联系图(Entity Relationship Diagram),提供了表示实体类型、属性和联系的方法,用来描述现实世界的概念模型。它是描述现实世界概念结构模型的有效方法。是表示概念模型的一种方式,用矩形表示实体型,矩形框内写明实体名;用椭圆表示实体的属性,并用无向边将其与相应的实体型连接起来;用菱形表示实体型之间的联系,在菱形框内写明联系名,并用无向边分别与有关实体型连接起来,同时在无向边旁标上联系的类型(1:1,1:n或m:n)。

专业的ER图工具:Viso和Power Designer

==实体== ==》表:矩形表示

==属性== ==》表字段:椭圆

==关系== ==》表与表的关系:菱形(1:1表示1对1关系;1:n表示1对多关系;m:n表示多对多关系)

博客项目的ER关系图如下:

1531279764230

3. 表设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
drop database blog31;
create database blog31;

#############用户表:
CREATE TABLE `bg_user` (
`id` int unsigned auto_increment,
`acc` varchar(50) not null default '' COMMENT '帐号',
`nickname` varchar(30) not null default '' COMMENT '昵称',
`pwd` char(32) not null default '' COMMENT '密码',
`cell` varchar(15) not null default '' COMMENT '手机号',
`regtime` int unsigned default 0 COMMENT '注册时间',
`type` tinyint unsigned not null default 0 COMMENT '用户类型 0:普通用户 1:管理员',
PRIMARY KEY (`id`)
) ENGINE=MyISAM CHARSET=utf8;

#############博客文章表:
CREATE TABLE `bg_article` (
`id` int(11) unsigned AUTO_INCREMENT,
`title` varchar(100) not null default '' COMMENT '文章标题',
`intro` varchar(255) not null default '' COMMENT '文章简介',
`content` text not null,
`post_date` int unsigned not null default 0 COMMENT '发布时间',
`user_id` int unsigned not null default 0 COMMENT '发布管理员id',
`user_nickname` varchar(30) not null default '' COMMENT '发布管理员昵称',
`comment_num` smallint unsigned not null default 0 COMMENT '评论数量',
`cat_id` int unsigned not null default 0 COMMENT '所属分类id',
`cat_name` varchar(30) not null default '' COMMENT '所属分类名称',
PRIMARY KEY (`id`)
) ENGINE=MyISAM CHARSET=utf8;

#############博客文章分类表:
CREATE TABLE `bg_category` (
`id` int unsigned AUTO_INCREMENT,
`name` varchar(30) not null COMMENT '分类名称',
`parent_id` int unsigned not null default '0' COMMENT '上级分类ID,0表示顶级分类',
PRIMARY KEY (`id`)
) ENGINE=MyISAM CHARSET=utf8;


#############评论表:
CREATE TABLE `bg_comment` (
`id` int unsigned AUTO_INCREMENT,
`content` text not null,
`post_date` int unsigned not null default 0 COMMENT '评论时间',
`article_id` int unsigned not null default 0 COMMENT '所属文章ID',
`article_title` varchar(50) not null default '' COMMENT '所属文章标题',
`user_id` int unsigned not null default 0 COMMENT '用户ID',
`user_nickname` varchar(30) not null default '' COMMENT '用户昵称',
PRIMARY KEY (`id`)
) ENGINE=MyISAM CHARSET=utf8;

4. 项目部署

  1. 将code/mvc目录改名为blog31,

    下图这个mvc目录:

    1531291206947

    改名为blog31:

    1531291235746

  2. 配置一个基于域名虚拟主机,域名为www.blog31.com

    在httpd-vhosts.conf文件中增加如下图所示的虚拟主机配置项:

    1531291311545

    修改hosts文件,

    1531291371313

  3. 重启apache,测试访问效果:

    1531291456736

    访问效果为:

    1531291515331

    能够看到上图,则说明部署成功。

5. 项目实现

实现后台用户管理系统

实现列表页

  1. 将code/templates/admin目录中如下图所示的目录复制一份,

    1531291763786

    转移拷贝到code/blog31/app/admin/view目录中,

    1531291818821

  2. 在blog31/app/admin/controller目录中创建一个名为UserController.class.php的文件,

    1531291945355

  3. 在blog31/app/admin/controller/UserController.class.php中构建如下代码,展示三个页面,

    1531292339989

  4. 测试访问后台用户管理系统列表页

    1531292433660

  5. 进一步做调整,将后台模板使用到的资源文件全部转移进项目中,

    在blog31/public目录中创建一个名为admin的目录,专门保存后台的资源文件,

    1531292599939

    复制code/templates/admin/libs目录下的所有文件,如下图所示,

    1531292644972

    转移拷贝到blog31/public/admin目录中,

    1531292690333

  6. 调整blog31/app/admin/view/User/userIndex.html中引入资源文件的路径,

    在blog31/conf/conf.php中增加本网站域名配置项,

    1531293146377

    然后在blog31/app/admin/view/User/userIndex.html使用配置项URL的值来引入配置文件,

    1531293430682

    1531293486446

    上图中替换的是所有图片文件引入路径,截取不全,实际替换的内容以blog31/app/admin/view/User/userIndex.html中的为准。

  7. 再次测试访问后台新闻管理系统列表页,

    1531293567621

  8. 在blog31/app/model目录中创建UserModel.class.php文件,

    1531293712684

    内容如下:

    1531293731152

    调整blog31/conf/conf.php中PDO配置项的默认选择的数据库名,

    1531294133397

  9. 在blog31/app/admin/controller/UserController.class.php中showIndex方法里渲染模板之前调用模型查询数据,

    1531294688749

    然后在blog31/app/admin/view/User/userIndex.html中回显查询得到的用户数据,

    1531294782348

    测试访问后台用户管理系统列表页:

    1531294828901

实现添加页

  1. 访问后台用户管理系统添加页,

    1531295330114

  2. 调整blog31/app/admin/view/User/userAdd.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,

    1531295483768

  3. 调整blog31/app/admin/view/User/userAdd.html中的form表单域各项的值,

    1531295777460

  4. 在blog31/app/admin/controller/UserController.class.php中创建一个处理添加用户的方法,adh方法,

    1531296464355

  5. 测试添加用户使用效果:

    在添加表单页面构建数据,点击提交,

    1531296568559
    提交后的效果:

    1531296591547

    效果为添加成功,说明功能实现OK。

实现编辑页

  1. 访问后台新闻管理系统编辑页:

    1531296743041

  2. 修改blog31/app/admin/view/User/userIndex.html中编辑按钮的链接:

    1531296871382

  3. 调整blog31/app/admin/view/User/userEdit.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,

    1531296956168

  4. 调整blog31/app/admin/view/User/userEdit.html中form表单里的代码,

    1531297468408

  5. 在blog31/app/admin/controller/UserController.class.php中创建updh方法,代码如下:

    1531301022079

  6. 测试使用效果:

    点击进入编辑也,如下图所示:

    1531301120317

    修改数据,

    1531301172674

    点击更新,

    1531301192522

    跳回到更新页面:

    1531301213845

    密码也是被改变了的,如下图所示:

    1531301252698

    说明编辑功能实现OK。

6. 全天总结

  1. 项目中的建表部分:

    如果某字段是需要保存时间,一般在PHP的项目中给int数据类型;

    如果某字段需要保存状态类型的值,使用tinyint会更加好;

    如果某字段保存的数据,我们可以预期的很准确,比如说手机号码,通常就是11位,但是我们在项目中一般会采取宁大不小的原则,给11位以上的值,比如给varchar(15);

    在构建字段的时候,我们一般都会给字段设置default默认值,如果不设置默认值,则今后这个字段无论是否需要添加数据,都必须指定上值。

  2. 项目开发部分:

    我们使用框架进行开发,一般程序开发人员从模板入手,先尝试展示出模板页面的效果,然后再考虑是否需要调用模型操作数据库的问题,所以我们通常在MVC框架中,1)首先会将模板页面转移进项目目录;2)然后会创建控制器类,构建方法调用视图展示模板页面;3)调试模板页面展示的效果;4)以上都成功之后,如果没有模型则需要创建模型,如果有模型则直接去考虑调用模型操作数据;

四、昨日回顾

1. 知识回顾

  1. 建表注意事项:

    如果在项目中要保存一个时间的数据,则我们一般选择使用int类型来保存;

    如果在项目中要保存一个状态或类型这样的数据时,我们一般选择使用tinyint类型来保存;

    如果在项目中能够明确要保存的数据需要占多少个字节时,我们一般采用的做法是宁大不小,比如,保存手机号码一般是11位,但是我们通常会选择给varchar(15)

    如果在项目中构建字段时,通常我们会给字段指定一个默认值,一方面方便程序的默认操作,比如注册时,如果给用户类型指定了默认值为0表示普通会员,则构建注册功能的代码就无需给这个字段设置值,MYSQL会直接给这个字段用上默认值;另外一方面可以避免在添加数据操作时,如果没给这个字段指定值,将会报错的情况。

  2. 在项目代码开发中的基本流程是:

    1)从模板文件入手,创建相关的控制器类和该类中的相应方法,渲染模板展示到浏览器;

    2)在方法中渲染模板之前调用模型操作数据,比如我们之前开发的后台用户管理系统列表页,在渲染列表页模板之前,我们需要调用模型把列表页需要展示的数据查询出来;如果没有模型,则需要创建模型,如果有,则直接使用;

    3)在方法中构建逻辑处理代码,生成相关功能;

2. 昨日反馈

1531406278112

五、知识路径

六、今日课程内容:博客项目

1. 封装验证码工具类

  1. 在blog31/plugins目录下创建CaptchaTool.class.php的文件,

    1531449474729

    构建的代码如下,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    <?php
    namespace plugins;//创建一个 全局空间 下的 plugins空间

    class CaptchaTool{

    private $_w;//画布的宽度
    private $_h;//画布的高度

    private $_img;//画布资源

    public function __construct($w=200, $h=80){

    #初始化参数
    $this->_w = $w;
    $this->_h = $h;

    $this->_img = imagecreatetruecolor($this->_w, $this->_h);//创建画布

    #填充背景色
    $color = $this->color();//获得随机色
    imagefill($this->_img, 0, 0, $color);//填充背景色

    #写字
    $randStr = $this->randStr();//构建4个随机字
    $color = $this->color();//获得随机色
    $fontPath = PUBLIC_PATH . '/fonts/font1.ttf';//字体文件所在路径
    $bx = $this->_w/4;//左下角起点x坐标
    $by = $this->_h*3/4;//左下角起点y坐标
    $fontSize = $this->_h *37/80;//字体大小,高度尺寸的37/80

    imagettftext($this->_img, $fontSize, 0, $bx, $by, $color, $fontPath, $randStr);//写字

    #设置干扰元素
    //设置干扰点
    $this->setPoint(180);

    //设置干扰线
    $this->setLine(8);
    }

    #画干扰线
    private function setLine($num){

    for($i=0; $i<$num; $i++ ){ //画$num个点
    $color = $this->color();//获得随机色

    $bx = mt_rand(0, $this->_w/2);//起点x坐标
    $by = mt_rand(0, $this->_h);//起点y坐标
    $ex = mt_rand($this->_w/2, $this->_w);//终点x坐标
    $ey = mt_rand(0, $this->_h);//终点y坐标

    imageline($this->_img, $bx, $by, $ex, $ey, $color);//画点
    }
    }

    #画干扰点
    private function setPoint($num){

    for($i=0; $i<$num; $i++ ){ //画$num个点
    $color = $this->color();//获得随机色

    $bx = mt_rand(0, $this->_w);//起点x坐标
    $by = mt_rand(0, $this->_h);//起点y坐标
    $ex = mt_rand($bx-2, $bx+2);//终点x坐标
    $ey = mt_rand($by-2, $by+2);//终点y坐标

    imageline($this->_img, $bx, $by, $ex, $ey, $color);//画点
    }
    }

    #构建随机字
    private function randStr($num=4){

    $mixedArr = array_merge(range('a', 'z'), range('A', 'Z'), range(0, 9));//构建随机字采集库

    $str = '';
    for($i=0; $i<$num; $i++ ){ //采集$num次

    $key = mt_rand(0, count($mixedArr)-1);//取得当前随机字符对应的元素下标
    $str .= $mixedArr[$key];//从字库中取得下标为$key的元素值拼接给$str
    }

    return $str;

    }

    #分配颜色
    private function color($r='', $g='', $b=''){

    //初始化三原色
    $r = ($r==='') ? mt_rand(0, 255) : $r;//红
    $g = ($g==='') ? mt_rand(0, 255) : $g;//绿
    $b = ($b==='') ? mt_rand(0, 255) : $b;//蓝

    //分配颜色
    return imagecolorallocate($this->_img, $r, $g, $b);
    }

    #输出图像的方法
    public function output(){

    header('Content-type:image/jpeg');//指定一个响应协议项

    imagejpeg($this->_img);//直接将图像输出到浏览器
    }
    }
  2. 在blog31/public下创建fonts目录,

    1531449570234

    将F:\home\class\day15\source\font1.ttf复制转移到blog31/public/fonts目录中,

    1531449634142

  3. 在blog31/conf/define.php中定义一个目录常量,

    1531449686817

  4. 在blog31/core/App.class.php中的autoload方法中实现对工具类的自动加载,

    1531449747232

  5. 测试使用工具类,

    在blog31/app/admin/controller/UserController.class.php中构建测试方法test,代码如下:

    1531449796360

    访问test方法,

    1531449846347

    验证码制作成功。

2. 实现后台分类管理系统

在blog31/app/admin/controller中创建名为CatController.class.php文件,专门用于操作后台分类管理系统各程序,

1531406458061

代码如下:

1531406531563

实现列表页

  1. 在blog31/app/admin/controller/CatController.class.php中创建名为showList的方法,渲染后台分类列表页模板,

    1531406637793

  2. 通过http://www.blog31.com/index.php?p=admin&m=cat&a=showIndex访问后台分类管理系统列表页,发现页面虽然能够展示,但是样式乱了,

    于是进一步调整blog31/app/admin/view/Category/categoryIndex.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,

    1531406850800

  3. 在blog31/app/model目录下创建CatModel.class.php的文件,

    1531450315893

    代码如下:

    1531450340259

  4. 在blog31/app/admin/controller/CatController.class.php文件showList方法中构建如下代码,

    1531451514806

    调整模板文件回显查询的数据blog31/app/admin/view/Category/categoryIndex.html:

    1531451572387

  5. 访问后台分类管理系统列表页:

    1531451726375

我们进一步调整程序,将所有分类数据进行整理,

实现无限级递归分类

  1. 在blog31/app/admin/controller/CatController.class.php文件中创建两个方法,代码和作用如下图所示:

    1531465269865

    1531465303880

  2. 然后在blog31/app/admin/controller/CatController.class.php文件中的showList方法里调整获得所有分类数据的代码,如下图所示将蓝框中的代码改为红框中的代码:

    1531465362917

  3. 调整模板页面中的代码,在输出分类名称之前还需要输出缩进符,

    1531465455827

  4. 测试最终效果:

    访问后台分类管理系统列表页

    1531465510155

实现添加页

  1. 在blog31/app/controller/CatController.class.php中创建名为showAd的方法,渲染后台分类添加页模板,

    1531407090431

  2. 通过http://www.blog31.com/index.php?p=admin&m=cat&a=showAd访问后台分类管理系统添加页,发现页面虽然能够展示,但是样式乱了,

    于是进一步调整blog31/app/admin/view/Category/categoryAdd.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,

    1531406850800

实现编辑页

  1. 调整blog31/app/admin/view/Category/categoryIndex.html中的编辑按钮链接,

    1531465870984

  2. 在blog31/app/admin/controller/CatController.class.php中创建名为showUpd的方法,渲染后台分类编辑页模板,

    1531465965417

  3. 通过从后台分类列表页点击编辑按钮访问后台分类管理系统编辑页,发现页面虽然能够展示,但是样式乱了,

    于是进一步调整blog31/app/admin/view/Category/categoryEdit.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,

    1531466023359

  4. 在blog31/app/admin/controller/CatController.class.php中的showUpd方法里构建相应的程序功能代码,如下图所示:

    1531466996992

  5. 调整blog31/app/admin/view/Category/categoryEdit.html中需要回显数据的部分及表单域的相关属性值,

    1531467078936

  6. 在blog31/app/admin/controller/CatController.class.php构建updh方法,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    //编辑处理方法
    public function updh(){

    //接收表单提交的数据
    $id = $_GET['id'];

    $name = trim($_POST['name']);//新的分类名称
    $old_name = trim($_POST['old_name']);//老的分类名称
    $parent_id = trim($_POST['parent_id']);//新的父级id
    $old_parent_id = trim($_POST['old_parent_id']);//老的父级id

    //检查是否有数据真的被修改了
    $target = [];

    if( $name!=$old_name && $name!='' ){//如果新的名称不等于老的名称并且新的名称不为空,则需要修改name值
    $target[] = "name='{$name}'";
    }

    if( $parent_id!=$old_parent_id ){//如果新的父级id不等于老的父级id
    $target[] = "parent_id={$parent_id}";
    }

    if( !empty($target) ){//如果$target不为空,说明有数据需要被更新

    $strTarget = implode(', ', $target);// $strTarget="name='xxxx', parent_id=xxxx";
    $sql = "update bg_category set {$strTarget} where id={$id}";

    //调用模型执行更新操作
    $model = \core\App::single('\model\CatModel');
    $re = $model->setData($sql);

    if( $re ){//更新成功
    echo '嘿嘿嘿,运气不错,更新成功咯~';
    }else{//更新失败
    echo '哈哈哈,你更新失败了!';
    }
    }else{//否则给出提示没有数据被更新
    echo '您当前还没有更新数据哟~';
    }

    //2秒之后跳转回编辑页
    $url = C('URL') . '/index.php?p=admin&m=cat&a=showUpd&id='.$id;
    header('Refresh:2; url='.$url);
    exit;
    }
  7. 测试使用效果,效果OK,功能成功。

实现删除功能

3. 实现后台博文管理系统

在blog31/app/admin/controller中创建名为ArticleController.class.php文件,专门用于操作后台博文管理系统各程序,

1531407704334

代码如下:

1531407665958

实现添加页

  1. 在blog31/app/controller/ArticleController.class.php中创建名为showAd的方法,渲染后台博文添加页模板,

    1531468943273

  2. 通过http://www.blog31.com/index.php?p=admin&m=article&a=showAd访问后台博文管理系统添加页,发现页面虽然能够展示,但是样式乱了,

    于是进一步调整blog31/app/admin/view/Article/articleAdd.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,

    1531469128676

  3. 调整无限级递归分类方法的归属

    将blog31/app/admin/controller/CatController.class.php文件中如下图所示的方法剪切下来,

    1531470492230

    将剪切下来的代码全部粘贴到blog31/app/model/CatModel.class.php的类中,

    1531470605131

    再调整blog31/app/model/CatModel.class.php内的代码,如下图所示将蓝框中的代码改为红框中的代码:

    1531470734590

    将blog31/app/admin/controller/CatController.class.php中的showUpd和showList方法调用无限级递归分类方法的方式进行调整,如下图中将蓝框部分改为红框部分:

    1531470829870

    1531470912888

    在blog31/app/controller/ArticleController.class.php中showAd方法里添加如下代码:

    1531472987997

  4. 调整blog31/app/admin/view/Article/articleAdd.html中的表单各成员代码:

    1531472047225

  5. 在blog31/app/model下创建ArticleModel.class.php的文件,如下图所示:

    1531472247204

  6. 在blog31/app/controller/ArticleController.class.php中添加adh方法,处理添加功能,

    1531473089893

  7. 测试使用效果:

    在添加页构建如下内容,

    1531473145850

    点击添加按钮,效果为:

    1531473185491

    说明添加功能OK。

实现列表页

  1. 在blog31/app/admin/controller/ArticleController.class.php中创建名为showIndex的方法,渲染后台博文列表页模板,

    1531473364554

  2. 通过http://www.blog31.com/index.php?p=admin&m=article&a=showIndex访问后台博文管理系统列表页,发现页面虽然能够展示,但是样式乱了,

    于是进一步调整blog31/app/admin/view/Article/articleIndex.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,

    1531473422417

  3. 在blog31/app/admin/controller/ArticleController.class.php中的showList方法里在渲染模板之前查询数据,

    1531473884257

  4. 将查得的数据在blog31/app/admin/view/Article/articleIndex.html中回显,

    1531473945005

  5. 测试访问效果:

    1531473996556

实现编辑页

  1. 调整blog31/app/admin/view/Category/articleIndex.html中的编辑按钮链接,
  1. 在blog31/app/controller/ArticleController.class.php中创建名为showUpd的方法,渲染后台博文编辑页模板,
  1. 通过从后台博文列表页点击编辑按钮访问后台博文管理系统编辑页,发现页面虽然能够展示,但是样式乱了,

    于是进一步调整blog31/app/admin/view/Category/articleUpd.html中引入资源文件的路径,将”../libs”全部替换为”{C(‘URL’)}/public/admin”,如下图所示,代码截取不全,以实际文件中的代码为准,

实现删除功能

4. 整理模板文件

5. 全天总结

  1. 制作的页面和功能:

    封装验证码工具类

    实现后台分类管理系统的列表页和编辑页

    实现无限级递归分类

    实现博客文章管理系统添加页

    实现博客文章管理系统列表页基础功能