model
app/model/goods_cate.js
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
var d=new Date();
const GoodsCateSchema = new Schema({
title:{type:String},
cate_img: {type: String},
filter_attr: {type: String}, // 筛选 id
link:{type: String},
template:{/* 指定当前分类的模板 */
type:String
},
pid:{
type:Schema.Types.Mixed // 混合类型
},
sub_title:{/*seo 相关的标题 关键词 描述 */
type:String
},
keywords:{
type:String
},
description:{
type:String
},
status: {type: Number,default:1},
add_time: {
type:Number,
default: d.getTime()
}
});
return mongoose.model(‘GoodsCate’, GoodsCateSchema,’goods_cate’);
}
router.js
router.get(‘/admin/goodsCate’, controller.admin.goodsCate.index);
router.get(‘/admin/goodsCate/add’, controller.admin.goodsCate.add);
router.post(‘/admin/goodsCate/doAdd’, controller.admin.goodsCate.doAdd);
增加
controller
app/controller/admin/goodsCate.js
async add(){
var result = await this.ctx.model.GoodsCate.find({‘pid’:’0′})
await this.ctx.render(‘admin/goodsCate/add’,{
cateList:result
});
}
async doAdd(){
let parts = this.ctx.multipart({autoFields: true});
let files = {};
let stream;
while ((stream = await parts()) != null) {
if (!stream.filename) {
break;
}
let fieldname = stream.fieldname; //file 表单的名字
// 上传图片的目录
let dir=await this.service.tools.getUploadFile(stream.filename);
let target = dir.uploadDir;
let writeStream = fs.createWriteStream(target);
await pump(stream, writeStream);
files=Object.assign(files,{
[fieldname]:dir.saveDir
})
}
if(parts.field.pid!=0){
parts.field.pid=this.app.mongoose.Types.ObjectId(parts.field.pid); // 调用 mongoose 里面的方法把字符串转换成 ObjectId
}
let goodsCate =new this.ctx.model.GoodsCate(Object.assign(files,parts.field));
await goodsCate.save();
await this.success(‘/admin/goodsCate’,’ 增加分类成功 ’);
}
view
app/view/admin/goodsCate/add.html
一级分类的 pid 是 0,顶级分类
二级分类的 pid 是_id, 一级分类 (如下图的手机 / 电话卡)
<%- include ../public/page_header.html %>
<div class=”panel panel-default”>
<div class=”panel-heading”>
增加商品分类
</div>
<div class=”panel-body”>
<div class=”table-responsive input-form”>
<form action=”/admin/goodsCate/doAdd?_csrf=<%=csrf%>” method=”post” enctype=”multipart/form-data”>
<ul class=”form_input”>
<li> <span> 分类名称:</span> <input type=”text” name=”title” class=”input”/></li>
<li> <span> 上级分类:</span>
<select name=”pid” id=”pid”>
<option value=”0″> 顶级分类 </option>
<%for(var i=0;i<cateList.length;i++){%>
<option value=”<%=cateList[i]._id%>”><%=cateList[i].title%></option>
<%}%>
</select>
</li>
<li> <span> 分类图片:</span> <input type=”file” name=”cate_img”/></li>
<li> <span> 筛选属性:</span> <input type=”text” name=”filter_attr” class=”input”/></li>
<li> <span> 跳转地址:</span> <input type=”text” name=”link” class=”input”/></li>
<li> <span> 分类模板:</span> <input type=”text” name=”template” class=”input”/><span> 空表示默认模板 </span></li>
<li> <span>Seo 标题:</span> <input type=”text” name=”sub_title” class=”input”/></li>
<li> <span>Seo 关键词: </span><input type=”text” name=”keywords” class=”input”/></li>
<li> <span>Seo 描述:</span> <textarea name=”description” id=”description” cols=”84″ rows=”4″></textarea></li>
<li> <span> 排 序:</span> <input type=”text” name=”sort”/></li>
<li> <span> 状 态:</span> <input type=”radio” name=”status” checked value=”1″ id=”a”/> <label for=”a”> 显示 </label> <input type=”radio” name=”status” value=”0″ id=”b”/><label for=”b”> 隐藏 </label> </li>
<li>
<br/>
<button type=”submit” class=”btn btn-primary”> 提交 </button>
</li>
</ul>
</form>
</div>
</div>
</div>
</body>
</html>
效果
查找
controller
app/controller/admin/goodsCate.js
通过 pid 关联自己查询
match 筛选 pid 为 0 的数据
async index() {
var result=await this.ctx.model.GoodsCate.aggregate([
{
$lookup:{
from:’goods_cate’,
localField:’_id’,
foreignField:’pid’,
as:’items’
}
},
{
$match:{
“pid”:’0′
}
}
])
console.log(JSON.stringify(result));
await this.ctx.render(‘admin/goodsCate/index’,{
list:result
});
}
view
app/view/admin/goodsCate/index.html
<%- include ../public/page_header.html %>
<div class=”panel panel-default”>
<div class=”panel-heading clear”>
<span> 商品分类列表 </span> <a href=”/admin/goodsCate/add” class=”btn btn-primary fr”> 增加商品分类 </a>
</div>
<div class=”panel-body”>
<div class=”table-responsive”>
<table class=”table table-bordered”>
<thead>
<tr class=”th”>
<th> 分类名称 </th>
<th> 分类图片 </th>
<th class=”text-center”> 排序 </th>
<th class=”text-center”> 状态 </th>
<th class=”text-center”> 操作 </th>
</tr>
</thead>
<tbody>
<%for(var i=0;i<list.length;i++){%>
<tr>
<td><%=list[i].title%></td>
<td><img class=”pic” src=”<%=list[i].cate_img%>” /></td>
<td class=”text-center”><span onclick=”app.editNum(this,’GoodsCate’,’sort’,'<%=list[i]._id%>’)”><%=list[i].sort%></span></td>
<td class=”text-center”>
<%if(list[i].status==1){%>
<img src=”/public/admin/images/yes.gif” onclick=”app.changeStatus(this,’GoodsCate’,’status’,'<%=list[i]._id%>’)” />
<%}else{%>
<img src=”/public/admin/images/no.gif” onclick=”app.changeStatus(this,’GoodsCate’,’status’,'<%=list[i]._id%>’)” />
<%}%>
</td>
<td class=”text-center”> <a href=”/admin/goodsCate/edit?id=<%=list[i]._id%>”> 修改 </a> <a class=”delete” href=”/admin/delete?model=GoodsCate&id=<%=list[i]._id%>”> 删除 </a></td>
</tr>
<%for(var j=0;j<list[i].items.length;j++){%>
<tr>
<td>—–<%=list[i].items[j].title%></td>
<td><img class=”pic” src=”<%=list[i].items[j].cate_img%>” /></td>
<td class=”text-center”><span onclick=”app.editNum(this,’GoodsCate’,’sort’,'<%=list[i].items[j]._id%>’)”><%=list[i].items[j].sort%></span></td>
<td class=”text-center”>
<%if(list[i].status==1){%>
<img src=”/public/admin/images/yes.gif” onclick=”app.changeStatus(this,’GoodsCate’,’status’,'<%=list[i].items[j]._id%>’)” />
<%}else{%>
<img src=”/public/admin/images/no.gif” onclick=”app.changeStatus(this,’GoodsCate’,’status’,'<%=list[i].items[j]._id%>’)” />
<%}%>
</td>
<td class=”text-center”> <a href=”/admin/goodsCate/edit?id=<%=list[i].items[j]._id%>”> 修改 </a> <a class=”delete” href=”/admin/delete?model=GoodsCate&id=<%=list[i].items[j]._id%>”> 删除 </a></td>
</tr>
<%}%>
<%}%>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
效果
红色为一级分类
蓝色为二级分类
上传图片时,生成多种分辨率的图片
安装依赖
cnpm install jimp –save
service
app/service/tools.js
resize 尺寸
quality 质量
write 名字
const Jimp = require(“jimp”); // 生成缩略图的模块
async jimpImg(target){
// 上传图片成功以后生成缩略图
Jimp.read(target, (err, lenna) => {
if (err) throw err;
lenna.resize(200, 200) // 尺寸
.quality(90) // 质量
.write(target+’_200x200’+path.extname(target)); // save
lenna.resize(400, 400) // 尺寸
.quality(90) // 质量
.write(target+’_400x400’+path.extname(target)); // save
});
}
controller
app/controller/admin/goodsCate.js 增加一行 this.service.tools.jimpImg(target);
async doAdd(){
let parts = this.ctx.multipart({autoFields: true});
let files = {};
let stream;
while ((stream = await parts()) != null) {
if (!stream.filename) {
break;
}
let fieldname = stream.fieldname; //file 表单的名字
// 上传图片的目录
let dir=await this.service.tools.getUploadFile(stream.filename);
let target = dir.uploadDir;
let writeStream = fs.createWriteStream(target);
await pump(stream, writeStream);
files=Object.assign(files,{
[fieldname]:dir.saveDir
})
this.service.tools.jimpImg(target);
}
if(parts.field.pid!=0){
parts.field.pid=this.app.mongoose.Types.ObjectId(parts.field.pid); // 调用 mongoose 里面的方法把字符串转换成 ObjectId
}
let goodsCate =new this.ctx.model.GoodsCate(Object.assign(files,parts.field));
await goodsCate.save();
await this.success(‘/admin/goodsCate’,’ 增加分类成功 ’);
}
效果
编辑
controller
app/controller/admin/goodsCate.js
view
app/view/admin/goodsCate/edit.html
<%- include ../public/page_header.html %>
<div class=”panel panel-default”>
<div class=”panel-heading”>
编辑商品分类
</div>
<div class=”panel-body”>
<div class=”table-responsive input-form”>
<form action=”/admin/goodsCate/doEdit?_csrf=<%=csrf%>” method=”post” enctype=”multipart/form-data”>
<ul class=”form_input”>
<input type=”hidden” name=”id” value=”<%=list._id%>” />
<li> <span> 分类名称:</span> <input type=”text” name=”title” class=”input” value=”<%=list.title%>”/></li>
<li> <span> 上级分类:</span>
<select name=”pid” id=”pid”>
<option value=”0″> 顶级分类 </option>
<%for(var i=0;i<cateList.length;i++){%>
<option value=”<%=cateList[i]._id%>” <%if(cateList[i]._id.toString()==list.pid){%> selected <%}%>><%=cateList[i].title%></option>
<%}%>
</select>
</li>
<li> <span> 分类图片:</span> <input type=”file” name=”cate_img”/>
<br />
<span> </span> <img class=”pic” src=”<%=list.cate_img%>” />
</li>
<li> <span> 筛选属性:</span> <input type=”text” name=”filter_attr” class=”input” value=”<%=list.filter_attr%>”/></li>
<li> <span> 跳转地址:</span> <input type=”text” name=”link” class=”input” value=”<%=list.link%>” /></li>
<li> <span> 分类模板:</span> <input type=”text” name=”template” class=”input” value=”<%=list.template%>” /><span> 空表示默认模板 </span></li>
<li> <span>Seo 标题:</span> <input type=”text” name=”sub_title” class=”input” value=”<%=list.sub_title%>”/></li>
<li> <span>Seo 关键词: </span><input type=”text” name=”keywords” class=”input” value=”<%=list.keywords%>”/></li>
<li> <span>Seo 描述:</span> <textarea name=”description” id=”description” cols=”84″ rows=”4″><%=list.description%></textarea></li>
<li> <span> 排 序:</span> <input type=”text” name=”sort” value=”<%=list.sort%>”/></li>
<li> <span> 状 态:</span> <input type=”radio” name=”status” <%if(list.status==1){%> checked <%}%> value=”1″ id=”a”/> <label for=”a”> 显示 </label> <input type=”radio” <%if(list.status==0){%> checked <%}%> name=”status” value=”0″ id=”b”/><label for=”b”> 隐藏 </label> </li>
<li>
<br/>
<button type=”submit” class=”btn btn-primary”> 提交 </button>
</li>
</ul>
</form>
</div>
</div>
</div>
</body>
</html>