该版本添加了分类管理功能,可以实现分类的添加、编辑、删除等。分类之间可以建立父子关系。在install/create_table.zl脚本中,增加了分类表的创建语句,直接在浏览器中运行install/create_table.zl脚本,可以创建category分类表...

    页面导航: 项目下载地址:

    zenglBlog源代码的相关地址:https://github.com/zenglong/zenglBlog  当前版本对应的tag标签为:v0.2.0 - v0.2.1

zenglBlog v0.2.0:

    该版本添加了分类管理功能,可以实现分类的添加、编辑、删除等。分类之间可以建立父子关系。

    在install/create_table.zl脚本中,增加了分类表的创建语句:

..........................................................
// 创建分类表
if(mysqlQuery(con, "DROP TABLE IF EXISTS category"))
	finish_with_error(con);
endif
if(mysqlQuery(con, "CREATE TABLE category(
					  id int NOT NULL AUTO_INCREMENT, 
					  name varchar(100) NOT NULL DEFAULT '' COMMENT '分类名', 
					  description varchar(255) NOT NULL DEFAULT '' COMMENT '分类描述',
					  pid int NOT NULL DEFAULT '0' COMMENT '上级分类ID', 
					  childcnt int NOT NULL DEFAULT '0' COMMENT '子分类数量',
					  type tinyint NOT NULL DEFAULT '0' COMMENT '类型:0表示普通文章,1表示外部链接',
					  PRIMARY KEY (id)
					) ENGINE=MyISAM DEFAULT CHARSET utf8 COLLATE utf8_general_ci COMMENT='分类表'"))
	finish_with_error(con);
endif
..........................................................


    直接在浏览器中运行install/create_table.zl脚本,可以创建category分类表。如果之前已经创建过users用户表,可以将该脚本中创建users表的语句给注释掉。

    创建好表结构后,进入后台,可以看到"分类列表"菜单。点击该菜单,一开始会看到空的分类列表:



图1:分类列表

    点击分类列表右上角的添加分类按钮,可以进入添加分类页面:



图2:添加分类界面

    添加完分类后,就可以在列表中看到添加的分类了:



图3:分类列表页面

    可以在列表中对已添加的分类进行编辑和删除操作。如果某个分类有子分类,可以点击查看子分类按钮,来查看该分类的子分类列表。如果没有子分类,则该分类的操作栏中就不会出现查看子分类的按钮:



图4:查看子分类,编辑,删除

    点击查看所有分类按钮,可以查看添加过的所有分类,包括所有的子分类:



图5:查看所有分类

    和分类相关的操作都位于admin/category.zl脚本中:

inc 'common.zl';

querys = rqtGetQuery();
action = querys['act'] ? querys['act'] : 'list';
if(action == 'list')
	Category.list();
elif(action == 'ajaxList')
	Category.ajaxList();
elif(action == 'add')
	Category.add();
elif(action == 'edit')
	Category.edit();
elif(action == 'ajaxDelete')
	Category.ajaxDelete();
else
	print 'invalid act';
endif

class Category
	// 根据pid获取分类列表
	fun getListByPid(db, pid, field="")
		field = field ? field : "id,name,description,pid,childcnt,type";
		where = pid >= 0 ? ' where pid='+pid : '';
		return Mysql.fetchAll(db, "select "+field+" from category "+where + " order by id asc");
	endfun

	// 根据分类id获取分类信息
	fun getCateInfo(db, id, field="")
		field = field ? field : "*";
		return Mysql.fetchOne(db, "select "+field+" from category where id="+id);
	endfun

	// 初始化数据库连接
	fun initDB()
		global config;
		db = bltArray();
		Mysql.init(db, config, "tpl/error.tpl");
		return db;
	endfun

	// 异步加载分类列表
	fun ajaxList()
		global querys;
		db = Category.initDB();
		pid = querys['pid'] ? bltInt(querys['pid']) : 0;
		categories = Category.getListByPid(db, pid, "id,name,childcnt");
		for(i=0;bltIterArray(categories,&i,&v);)
			bltHtmlEscape(&v['name'], TRUE);
			bltHtmlEscape(&v['description'], TRUE);
		endfor
		rqtSetResponseHeader("Content-Type: application/json");
		print bltJsonEncode(categories);
	endfun

	// ajax返回成功或失败
	fun ajaxReturn(errmsg = '')
		ret['msg'] = errmsg ? 'failed' : 'success';
		ret['errmsg'] = errmsg;
		rqtSetResponseHeader("Content-Type: application/json");
		print bltJsonEncode(ret);
		bltExit();
	endfun

	// 异步ajax删除某个分类
	fun ajaxDelete()
		global querys;
		db = Category.initDB();
		id = querys['id'] ? bltInt(querys['id']) : 0;
		if(id <= 0) 
			Category.ajaxReturn('无效的分类id'); 
		endif
		cateinfo = Category.getCateInfo(db, id);
		if(cateinfo['childcnt'] > 0) 
			Category.ajaxReturn('该分类包含子分类,需要先删除子分类');
		endif
		Mysql.Exec(db, "DELETE FROM category WHERE id="+id);
		Category.updateChildCnt(db, cateinfo['pid']);
		Category.ajaxReturn();
	endfun

	// 显示分类列表
	fun list()
		global menus, querys;
		pid = querys['pid'] ? bltInt(querys['pid']) : 0;
		db = Category.initDB();
		data['title'] = '分类列表:';
		data['categories'] = Category.getListByPid(db, pid);
		if(pid > 0)
			data['category'] = Category.getCateInfo(db, pid);
			// 如果父分类中的childcnt字段的值,和实际读取出来的子分类的数量不一致的话,则更新父分类的childcnt值
			if(data['category', 'childcnt'] != bltCount(data['categories']))
				Category.updateChildCnt(db, pid);
			endif
			bltHtmlEscape(&data['category', 'name'], TRUE);
		endif
		for(i=0;bltIterArray(data['categories'],&i,&v);)
			switch(v['type'])
			case 0:
				v['type_name'] = '普通文章';
				break;
			case 1:
				v['type_name'] = '外部链接';
				break;
			default:
				v['type_name'] = '未知类型';
			endswitch
			bltHtmlEscape(&v['name'], TRUE);
			bltHtmlEscape(&v['description'], TRUE);
		endfor
		setCurMenu(menus, 'category.zl');
		data['menus'] = menus;
		if(pid==0)
			data['pid_title'] = '顶层';
		elif(pid < 0)
			data['pid_title'] = '所有';
		endif
		print bltMustacheFileRender("tpl/category_list.tpl", data);
	endfun

	// 显示添加分类页面
	fun showAdd(db, data, posts = 0, isadd = TRUE)
		global menus;
		setCurMenu(menus, 'category.zl');
		csses[] = 'category_add.css';
		data['csses'] = csses;
		data['menus'] = menus;
		data['title'] = isadd ? '添加分类' : '编辑分类';
		data['act'] = isadd ? 'add' : 'edit';
		if(posts && posts['pid'] > 0)
			data['category'] = Category.getCateInfo(db, bltInt(posts['pid']), "id,name");
			bltHtmlEscape(&data['category', 'name'], TRUE);
		else
			data['categories'] = Category.getListByPid(db, 0, "id,name,childcnt");
			for(i=0;bltIterArray(data['categories'],&i,&v);)
				bltHtmlEscape(&v['name'], TRUE);
				bltHtmlEscape(&v['description'], TRUE);
			endfor
		endif
		bltHtmlEscape(&posts['name'], TRUE);
		bltHtmlEscape(&posts['description'], TRUE);
		print bltMustacheFileRender("tpl/category_add.tpl",data);
	endfun

	// 校验数据
	fun validate(db, data, posts, isadd = TRUE)
		bltStr(&posts['name'], TRUE);
		bltStr(&posts['description'], TRUE);
		if(!posts['name'])
			data['err_msg'] = '分类名称不能为空';
		elif(!isadd && !posts['id'])
			data['err_msg'] = '无效的分类ID';
		endif
		if(data['err_msg'])
			data['posts'] = posts;
			if(posts['id'])
				data['id'] = posts['id'];
			endif
			Category.showAdd(db, data, posts, isadd);
			bltExit();
		endif
	endfun

	// 更新某个分类的子分类数量
	fun updateChildCnt(db, pid)
		if(pid > 0)
			ret = Mysql.fetchOne(db, "select count(1) as childcnt from category where pid="+pid);
			Mysql.Exec(db, "UPDATE category SET childcnt = '"+ret['childcnt']+"' where id="+pid);
		endif
	endfun

	// 添加分类
	fun add()
		posts = rqtGetBodyAsArray();
		db = Category.initDB();
		if(posts['submit'])
			Category.validate(db, data, posts);
			bltUnset(&posts['submit']);
			Mysql.Insert(db, 'category', posts);
			Category.updateChildCnt(db, bltInt(posts['pid']));
			data['success_msg'] = '添加分类成功';
		endif
		Category.showAdd(db, data);
	endfun

	// 编辑分类
	fun edit()
		global querys;
		posts = rqtGetBodyAsArray();
		db = Category.initDB();
		if(posts['submit'])
			id = bltInt(posts['id']);
			Category.validate(db, data, posts, FALSE);
			oldinfo = Category.getCateInfo(db, id, "pid");
			bltUnset(&posts['submit']);
			Mysql.Update(db, 'category', posts, 'id=' + id);
			if(oldinfo['pid'] != bltInt(&posts['pid'], TRUE))
				Category.updateChildCnt(db, posts['pid']);
				Category.updateChildCnt(db, oldinfo['pid']);
			endif
			data['success_msg'] = '编辑分类成功';
		else
			id = bltInt(querys['id']);
		endif
		data['posts'] = Category.getCateInfo(db, id);
		if(!bltCount(data['posts']))
			data['err_msg'] = '无效的分类ID';
			id = 0;
		endif
		data['id'] = id;
		Category.showAdd(db, data, data['posts'], FALSE);
	endfun
endclass


    和分类有关的模板文件为:admin/tpl/category_add.tpl以及admin/tpl/category_list.tpl。其中category_add.tpl模板是用于渲染添加和编辑界面的,category_list.tpl则是用于分类列表显示的模板。这些都是使用mustache语法的模板,例如 category_list.tpl的模板内容如下:

{{> tpl/header.tpl}}
<h2 class="sub-header">
	{{#category}}[{{name}}] {{/category}}
	{{pid_title}}{{title}}
	<a href="?pid=-1" class="btn btn-default pull-right" role="button" style="margin-left:10px">查看所有分类</a>
	<a href="?act=add" class="btn btn-primary pull-right" role="button">添加分类</a>
</h2>
<div class="table-responsive">
	<table class="table table-hover">
		<thead>
		<tr>
			<th>id</th>
			<th>分类名</th>
			<th>类型</th>
			<th>分类描述</th>
			<th>pid</th>
			<th>操作</th>
		</tr>
		</thead>
		<tbody>
			{{#categories}}
			<tr>
				<td>{{id}}</td>
				<td>{{name}}</td>
				<td>{{type_name}}</td>
				<td>{{description}}</td>
				<td>{{pid}}</td>
				<td>{{#childcnt}}
					<a href="?pid={{id}}" title="查看子分类({{childcnt}})" class="glyphicon glyphicon-list-alt" aria-hidden="true"></a>  
					{{/childcnt}}
					<a href="?act=edit&id={{id}}" title="编辑" class="glyphicon glyphicon-edit" aria-hidden="true"></a>  
					<a href="javascript:void(0)" data-id="{{id}}" title="删除" class="glyphicon glyphicon-trash del_category" aria-hidden="true"></a><span></span></td>
			</tr>
			{{/categories}}
			{{^categories}}
				<tr><td colspan=6>暂无分类或子分类</td></tr>
			{{/categories}}
		</tbody>
	</table>
</div>
<script type="text/javascript">
$( document ).ready(function() {
$('.del_category').click(function(){
	var id = $(this).data('id');
	var next_span = $(this).next('span');
	var r = confirm("删除确认");
	if(r == true) {
		var timestamp = new Date().getTime(); //IE浏览器ajax GET请求有缓存问题!
		$.ajax({
			type: 'GET',
			url: "category.zl?timestamp="+timestamp,
			dataType: "json",
			data: {
				"act": "ajaxDelete",
				"id": id
			},
			beforeSend:function(){
				next_span.text(' 删除中...');
			},
			success: function(data){
				if(data.msg != 'success') {					
					alert('删除失败: ' + data.errmsg);
					next_span.text('');
					return;
				}
				alert('删除成功');
				window.location.reload();
			},
			//调用出错执行的函数
			error: function(err){
				alert('未知异常');
				next_span.text('');
			}
		});
	}
});
});
</script>
{{> tpl/footer.tpl}}


    此外,在mysql.zl脚本的Mysql类中,添加了一些方便插入和更新数据的方法,例如:Insert,Update,Exec方法等:

class Mysql
	con;
	error_tpl;
	fun error(obj, sql='')
		Mysql obj;
		data['error'] = mysqlError(obj.con);
		mysqlClose(obj.con);
		if(obj.error_tpl)
			data['title'] = 'Mysql错误';
			print bltMustacheFileRender(obj.error_tpl,data);
		endif
		bltExit(data['error'] + (sql ? ' sql: ' + sql : ''));
	endfun

	................................................................

	fun Escape(obj, str)
		Mysql obj;
		return mysqlRealEscapeString(obj.con, bltStr(str));
	endfun

	fun Insert(obj, table, datas)
		Mysql obj;
		vals = keys = '';
		len = bltCount(datas);
		for(i=0;bltIterArray(datas,&i,&k,&v);)
			keys += "`" + Mysql.Escape(obj, k) + "`";
			vals += "'" + Mysql.Escape(obj, v) + "'";
			if(i < len)
				keys += ',';
				vals += ',';
			endif
		endfor
		if(len > 0)
			sql = "INSERT INTO `"+table+"` (" + keys + ") VALUES (" + vals + ")";
			if(mysqlQuery(obj.con, sql))
				Mysql.error(obj);
			endif
		endif
	endfun

	fun Update(obj, table, datas, where = '')
		Mysql obj;
		key_vals = '';
		len = bltCount(datas);
		for(i=0;bltIterArray(datas,&i,&k,&v);)
			key_vals += "`" + Mysql.Escape(obj, k) + "`=" + "'" + Mysql.Escape(obj, v) + "'";
			if(i < len) key_vals += ','; endif
		endfor
		if(len > 0)
			sql = "UPDATE `"+table+"` SET " + key_vals + (where != '' ? ' WHERE ' + where : '');
			if(mysqlQuery(obj.con, sql))
				Mysql.error(obj, sql);
			endif
		endif
	endfun

	fun Exec(obj, sql)
		Mysql obj;
		if(mysqlQuery(obj.con, sql))
			Mysql.error(obj);
		endif
	endfun
endclass


    以上就是当前版本的相关内容。OK,就到这里,休息,休息一下 o(∩_∩)o~~

结束语:

    实践是检验真理的唯一标准

——  《斗罗大陆》
 
上下篇

下一篇: zenglBlog v0.3.0 文章管理,图片上传,生成缩略图

上一篇: zenglBlog v0.1.0 登录和后台界面

相关文章

zenglBlog v0.1.0 登录和后台界面

zenglBlog v0.8.0 增加保存草稿,友情链接,内容页上下篇,手机端返回顶部图片

zenglBlog v0.5.0 发表评论,图形验证码

zenglBlog v0.4.0 前台首页,文章列表,文章内容页

zenglBlog v0.7.0 增加公告功能,内容页图片自适应,首页及列表页样式调整

zenglBlog v0.3.0 文章管理,图片上传,生成缩略图