Authored by yyq

tdk 交互

... ... @@ -2,6 +2,7 @@
const Router = require('koa-router');
const _ = require('lodash');
const md5 = require('md5');
const pager = require('../utils/pager');
let r = new Router();
... ... @@ -14,50 +15,81 @@ const TYPE_LIST = [
];
const tdk = {
_getDeaultList: async(ctx, next) => {
},
// tdk 列表
index: async(ctx, next) => {
let resData = {};
let type = ctx.query.type || 'skn',
page = parseInt(`0${ctx.query.page}`, 10) || 1,
limit = parseInt(`0${ctx.query.limit}`, 10) || 20;
limit = parseInt(`0${ctx.query.limit}`, 10) || 20,
query = ctx.query.query;
let listKey = `tdk:${type}:links`,
startId = (page - 1) * limit,
typeObj = _.find(TYPE_LIST, {'type': type}) || {};
let hmget = [];
await ctx.redis.multi([
['llen', listKey],
['lrange', listKey, startId, page * limit]
]).execAsync().then(function(res) {
let total = res[0] || 1;
if (query) {
query = _.trim(query);
resData.query = query;
resData.pager = pager(Math.floor((total - 1) / limit) + 1, ctx.query);
_.forEach(res[1], value => {
hmget.push(['hmget', `tdk:${type}:${value}`, 'key', 'title', 'keywords', 'description']);
});
});
query = type === 'url' ? md5(query) : query;
await ctx.redis.multi(hmget).execAsync().then(function(res) {
let tdkList = [];
await ctx.redis.multi([
['exists', `tdk:${type}:${query}`],
['hmget', `tdk:${type}:${query}`, 'key', 'title', 'keywords', 'description']
]).execAsync().then(function(res) {
console.log(res);
if (!res[0]) {
return;
}
_.forEach(res, value => {
tdkList.push({
id: ++startId,
resData.tdkList = [{
id: 1,
typeName: typeObj.name,
typeLt: typeObj.lt,
key: value[0],
title: value[1],
keywords: value[2],
description: value[3]
key: res[1][0],
title: res[1][1],
keywords: res[1][2],
description: res[1][3]
}];
});
} else {
await ctx.redis.multi([
['llen', listKey],
['lrange', listKey, startId, page * limit]
]).execAsync().then(function(res) {
let total = res[0] || 1;
resData.pager = pager(Math.floor((total - 1) / limit) + 1, ctx.query);
_.forEach(res[1], value => {
hmget.push(['hmget', `tdk:${type}:${value}`, 'key', 'title', 'keywords', 'description']);
});
});
if (tdkList.length) {
resData.tdkList = tdkList;
}
});
await ctx.redis.multi(hmget).execAsync().then(function(res) {
let tdkList = [];
_.forEach(res, value => {
tdkList.push({
id: ++startId,
typeName: typeObj.name,
typeLt: typeObj.lt,
key: value[0],
title: value[1],
keywords: value[2],
description: value[3]
});
});
if (tdkList.length) {
resData.tdkList = tdkList;
}
});
}
await ctx.render('action/seo_tdk', Object.assign(resData, {
title: 'TDK管理',
... ... @@ -69,37 +101,111 @@ const tdk = {
// 添加tdk
add: async(ctx, next) => {
let result = {code: 500, message: '非法参数'},
typeList = ['skn', 'article', 'shop', 'url'];
let result = {code: 500, message: '非法参数'};
// skn, article, shop, url
let type = ctx.request.body.type,
val = ctx.request.body.val,
key = ctx.request.body.key,
title = ctx.request.body.title || '',
keywords = ctx.request.body.keywords || '',
description = ctx.request.body.description || '';
// type 不合法, 返回错误
if (!_.find(typeList, type)) {
ctx.response.body = result;
if (!_.find(TYPE_LIST, ['type', type])) {
return ctx.response.body = result;
}
let hashKey = type === 'url' ? md5(key) : key;
await ctx.redis.multi([
['lpushx', `tdk:${type}:links`, hashKey],
['hmset', `tdk:${type}:${hashKey}`, 'key', key, 'title', title, 'keywords', keywords,
'description', description, 'modify_time', Date.parse(new Date()) / 1000]
]).execAsync().then(function(res) {
if (res[1]) {
if (!res[0]) {
ctx.redis.lpush(`tdk:${type}:links`, hashKey);
}
Object.assign(result, {code:200, message: 'success'});
} else {
Object.assign(result, {code:400, message: 'failed'});
}
});
ctx.response.body = result;
},
edit: async(ctx, next) => {
let result = {code: 500, message: '非法参数'};
// skn, article, shop, url
let type = ctx.request.body.type,
key = ctx.request.body.key,
title = ctx.request.body.title || '',
keywords = ctx.request.body.keywords || '',
description = ctx.request.body.description || '';
// type 不合法, 返回错误
if (!_.find(TYPE_LIST, ['type', type])) {
return ctx.response.body = result;
}
ctx.redis.multi([
["lpush", `tdk:${type}:links`, val],
["hmset", `tdk:${type}:${val}`, "title", title, "keywords", keywords, "description", description]
let hashKey = type === 'url' ? md5(key) : key;
await ctx.redis.multi([
['hmset', `tdk:${type}:${hashKey}`, 'title', title, 'keywords', keywords,
'description', description, 'modify_time', Date.parse(new Date()) / 1000]
]).execAsync().then(function(res) {
Object.assign(result, {code:200, message: 'success'})
if (res[0]) {
Object.assign(result, {code:200, message: 'success'});
} else {
Object.assign(result, {code:400, message: 'failed'});
}
});
ctx.response.body = result;
},
delete: async(ctx, next) => {
let result = {code: 500, message: '非法参数'};
let list = ctx.request.body.list;
let delArr = ['del']
let multiArr = [];
// list 不合法, 返回错误
if (!list || !list.length) {
return ctx.response.body = result;
}
_.forEach(list, value => {
let hashKey = value.type === 'url' ? md5(value.key) : value.key;
if (!_.find(TYPE_LIST, ['type', value.type])) {
return;
}
multiArr.push(['lrem', `tdk:${value.type}:links`, 1, hashKey]);
delArr.push(`tdk:${value.type}:${hashKey}`)
});
if (multiArr.length) {
multiArr.push(delArr);
await ctx.redis.multi(multiArr).execAsync().then(function(res) {
Object.assign(result, {code:200, message: 'success'});
});
}
ctx.response.body = result;
}
};
r.get('/', tdk.index);
r.get('/tdk', tdk.index);
r.post('/tdk/add', tdk.add);
r.post('/tdk/edit', tdk.edit);
r.post('/tdk/delete', tdk.delete);
module.exports = r;
... ...
... ... @@ -20,14 +20,14 @@
border-bottom-right-radius: 0;
}
.seo-tdk-page .query-form .submit-btn {
.seo-tdk-page .query-form .query-submit-btn {
height: 39px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.seo-tdk-page .query-key {
width: 220px;
width: 300px;
height: 39px;
font-size: 14px;
vertical-align: middle;
... ... @@ -47,6 +47,11 @@
margin: 0;
}
.seo-tdk-page #table-tdk th,
.seo-tdk-page #table-tdk td {
height: 36px;
}
.seo-tdk-page #pop{
width:500px;
height: 434px;
... ... @@ -58,6 +63,12 @@
margin:auto;
}
.seo-tdk-page #pop .cover-title {
position: absolute;
background: #fff;
margin-top: -34px;
}
.seo-tdk-page #pop .control-label {
width: 60px;
text-align: right;
... ... @@ -87,6 +98,10 @@
.seo-tdk-page #pop .controls {
display: inline-block;
}
.seo-tdk-page #pop .err-tip {
color: #a94442;
}
</style>
<div class="pageheader">
... ... @@ -110,24 +125,27 @@
<div class="contentpanel seo-tdk-page" style="padding-bottom:0;">
<div class="panel panel-default">
<div class="panel-body">
<label style="margin-right:20px;"><input type="checkbox" style="margin-right:5px;">全选</label>
<label style="margin-right:20px;"><input type="checkbox" id="check-all" style="margin-right:5px;">全选</label>
<a data-toggle="modal" href="#pop" class="btn btn-default" style="margin-right:10px;">增加</a>
<button class="btn btn-default" type="submit">删除</button>
<div class="input-append query-form pull-right">
<div class="btn-group">
<button class="btn dropdown-toggle" data-toggle="dropdown" data-type="{{type}}">
{{typeName}}
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
{{# typeList}}
<li><a href="?type={{type}}">{{name}}</a></li>
{{/ typeList}}
</ul>
</div>
<input class="span2 query-key" type="text">
<button class="btn submit-btn" type="button">搜索</button>
<button class="btn btn-default delete-all" type="submit">删除</button>
<div class="input-append pull-right">
<form id="query-form" action="/seo/tdk" class="query-form" method="get">
<div class="btn-group">
<button class="btn dropdown-toggle" data-toggle="dropdown">
{{typeName}}
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
{{# typeList}}
<li><a href="?type={{type}}">{{name}}</a></li>
{{/ typeList}}
</ul>
</div>
<input type="hidden" name="type" value="{{type}}">
<input class="span2 query-key" type="text" name="query" value="{{query}}">
<button class="btn query-submit-btn" type="submit">搜索</button>
</form>
</div>
</div>
</div>
... ... @@ -155,15 +173,21 @@
<td>{{keywords}}</td>
<td>{{description}}</td>
<td class="text-center">
<span>编辑</span>
<span>删除</span>
<a href="#pop" class="edit-tdk-btn" data-toggle="modal">编辑</a>
<a href="javascript:;" class="del-tdk-btn">删除</a>
</td>
</tr>
{{/each}}
{{#unless tdkList}}
<tr>
<td class="text-center" colspan="6">暂无数据</td>
</tr>
{{/unless}}
</tbody>
</table>
{{# pager}}
<div class="text-right">
{{#if pages}}
<ul class="pagination">
{{# prePage}}
<li><a href="{{url}}">上一页</a></li>
... ... @@ -175,6 +199,7 @@
<li><a href="{{url}}">下一页</a></li>
{{/ nextPage}}
</ul>
{{/if}}
</div>
{{/ pager}}
</div>
... ... @@ -182,50 +207,206 @@
<div id="pop" class="modal fade in" style="display: none;">
<div class="modal-header">
<a class="close" data-dismiss="modal">×</a>
<a class="close clear-input" data-dismiss="modal">×</a>
<h4>添加TDK</h4>
<h4 class="cover-title"></h4>
</div>
<div class="modal-body">
<ul>
<li>
<label class="control-label" for="select-type">类型:</label>
<div class="controls">
<select id="select-type">
{{# typeList}}
<option value="{{type}}">{{name}}</option>
{{/ typeList}}
</select>
</div>
</li>
<li>
<label class="control-label" for="input-key">SKN:</label>
<div class="controls">
<input type="text" id="input-key">
</div>
</li>
<li>
<label class="control-label" for="input-title">标题:</label>
<div class="controls">
<input type="text" id="input-title" class="full-w">
</div>
</li>
<li>
<label class="control-label" for="input-keywords">关键词:</label>
<div class="controls">
<input type="text" id="input-keywords" class="full-w">
</div>
</li>
<li>
<label class="control-label" for="input-description">描述:</label>
<div class="controls">
<textarea id="input-description" class="full-w"></textarea>
</div>
</li>
</ul>
<ul>
<li>
<label class="control-label" for="select-type">类型:</label>
<div class="controls">
<select id="select-type">
{{# typeList}}
<option value="{{type}}">{{name}}</option>
{{/ typeList}}
</select>
</div>
</li>
<li>
<label class="control-label key-label" for="input-key">SKN:</label>
<div class="controls">
<input type="text" id="input-key">
</div>
</li>
<li>
<label class="control-label" for="input-title">标题:</label>
<div class="controls">
<input type="text" id="input-title" class="full-w">
</div>
</li>
<li>
<label class="control-label" for="input-keywords">关键词:</label>
<div class="controls">
<input type="text" id="input-keywords" class="full-w">
</div>
</li>
<li>
<label class="control-label" for="input-description">描述:</label>
<div class="controls">
<textarea id="input-description" class="full-w"></textarea>
</div>
</li>
</ul>
</div>
<div class="modal-footer">
<a class="btn">关闭</a>
<a class="btn btn-primary">确定</a>
<span class="err-tip"></span>
<a class="btn clear-input close-pop-btn" data-dismiss="modal">关闭</a>
<a class="btn btn-primary sure-btn">确定</a>
</div>
</div>
</div>
<script>
var pop = {
ltList: {
skn: 'SKN:',
article: 'ID:',
shop: 'ShopId:',
url: 'URL:'
},
init: function() {
var that = this;
this.$base = $('#pop');
this.$popTitle = $('.cover-title', this.$base);
this.$keyLabel = $('.key-label', this.$base);
this.$type = $('#select-type', this.$base);
this.$key = $('#input-key', this.$base);
this.$title = $('#input-title', this.$base);
this.$keywords = $('#input-keywords', this.$base);
this.$description = $('#input-description', this.$base);
this.$closeBtn = $('.close-pop-btn', this.$base);
this.$errTip = $('.err-tip', this.$base);
this.$base.on('change', '#select-type', function() {
that.$keyLabel.text(that.ltList[$(this).val()] || that.ltList.skn);
}).on('click', '.clear-input', function() {
that.clearInput();
}).on('click', '.sure-btn', function() {
var data;
if (that.saving) {
return;
}
data = that.packReqData();
if (!data) {
that.$errTip.text('请填写完整tdk信息');
return;
}
that.saving = true;
$.ajax({
url: that.editInfo ? '/seo/tdk/edit' : '/seo/tdk/add',
type: 'POST',
data: data,
}).done(function(res) {
if (res.code === 200) {
history.go(0);
}
}).always(function() {
that.saving = false;
});
});
},
clearInput: function() {
this.editInfo = false;
this.$popTitle.empty();
this.$errTip.empty();
this.$type.val('skn').change();
this.$type.removeAttr('disabled')
this.$key.removeAttr('readonly');
$('input, textarea', this.$base).val('');
},
fillInput(info) {
if (info) {
this.editInfo = info;
this.$popTitle.text('编辑TDK');
this.$type.val(info.type).change().attr('disabled', true);
this.$key.attr('readonly', 'readonly').val(info.key);
this.$title.val(info.title);
this.$keywords.val(info.keywords);
this.$description.val(info.description);
}
this.$base.show();
},
packReqData() {
var data = {
type: this.$type.val(),
key: $.trim(this.$key.val()),
title: $.trim(this.$title.val()),
keywords: $.trim(this.$keywords.val()),
description: $.trim(this.$description.val())
};
var i;
if (this.editInfo) {
data.type = this.editInfo.type;
data.key = this.editInfo.key;
}
for (i in data) {
if (data.hasOwnProperty(i) && !data[i]) {
return false;
}
}
return data;
},
close() {
this.$closeBtn.trigger('click');
}
};
$(function(){
var $checkboxs = $('#table-tdk :checkbox');
var deling;
pop.init();
function delTdk(data) {
deling = true;
$.ajax({
url: '/seo/tdk/delete',
type: 'POST',
data: {list: data},
}).done(function(res) {
if (res.code === 200) {
history.go(0);
}
}).always(function() {
deling = false;
});
};
$('#check-all').click(function() {
if ($(this).attr('checked') === 'checked') {
$checkboxs.attr('checked', 'checked');
} else {
$checkboxs.removeAttr('checked');
}
});
$('.delete-all').click(function() {
var arr = [];
$('#table-tdk :checkbox:checked').each(function(){
arr.push($(this).closest('tr').data());
});
arr.length ? delTdk(arr) : false;
});
$('#table-tdk').on('click', '.edit-tdk-btn', function(e) {
pop.fillInput($(e.target).closest('tr').data());
}).on('click', '.del-tdk-btn', function(e) {
var data = $(e.target).closest('tr').data();
delTdk([data]);
});
});
</script>
... ...