Authored by 郭成尧

Merge branch 'feature/search' into 'release/6.5'

Feature/search



See merge request !1237
... ... @@ -27,7 +27,7 @@
"request": "launch",
"name": "Node",
"program": "${workspaceFolder}/app.js",
"console":"integratedTerminal"
"console": "integratedTerminal"
}
]
}
... ...
... ... @@ -108,7 +108,7 @@ const baseShopFav = (req, res) => {
/**
* 店铺首页
* @return int
* TODO 弃用
*/
const _shop = (req, res, shopId) => {
let isApp = req.query.app_version || req.query.appVersion || false;
... ... @@ -216,7 +216,10 @@ const shopFav = (req, res) => {
});
};
// 品类落地页
/**
* 旧版品类落地页
* TODO 已重构为 listNew 待删除
*/
const category = (req, res, next) => {
if (req.query) {
_.forEach(req.query, (perParam, index) => {
... ... @@ -400,9 +403,7 @@ const getCategoryGoods = (req, res, next) => {
/**
* 品牌店铺的入口
* @param req
* @param res
* @param next
* TODO 弃用
*/
const brand = (req, res, next) => {
let params = Object.assign({}, req.query);
... ...
... ... @@ -11,12 +11,54 @@ const headerModel = require('../../../doraemon/models/header');
const searchModel = require(`${mRoot}/search`);
const _ = require('lodash');
const helpers = global.yoho.helpers;
const stringProcess = require(`${utils}/string-process`);
const searchProcess = require(`${utils}/search-process`);
const productProcess = require(`${utils}/product-process`);
const listParamsProcess = require(`${utils}/list-params-process`);
const co = require('bluebird').coroutine;
/**
* 搜索主页
*/
exports.index = (req, res, next) => {
let title = '搜索',
uid = req.user.uid || 0;
((render) => {
if (_.get(req, 'app.locals.wap.search.removeHotSearch', false)) {
render([]);
} else {
req.ctx(searchModel).getSearchIndex({
gender: req.yoho.channel || 'boys',
uid: uid
}).then((result) => {
render(result);
}).catch(next);
}
})((result) => {
res.render('search/index', Object.assign({
module: 'product',
page: 'search-index',
pageHeader: headerModel.setNav({
navTitle: title
}),
pageFooter: true,
width750: true,
search: {
defaultTerms: (result && result.defaultTerms && result.defaultTerms.length) ?
result.defaultTerms[0].content : '',
url: helpers.urlFormat('', null, 'search'),
hotTerms: result.hotTerms,
wantTerms: result.guessTerms
}
}, searchProcess.getListSeoData()));
});
};
/**
* 搜索落地页
*/
exports.list = (req, res, next) => {
... ... @@ -27,8 +69,7 @@ exports.list = (req, res, next) => {
let title = '';
let isQueryFirstClass = false; // 标识用户搜的是不是一级品类
let isQuerySecondClass = false; // 标识用户搜的是不是二级品类
// let domain = null;
let uid = req.user.uid || 0;
let uid = req.user.uid;
if (params.shop_id) {
params.shopId = params.shop_id;
... ... @@ -76,26 +117,6 @@ exports.list = (req, res, next) => {
}
if (params.query) {
// domain = result[0];
// 跳转到品牌商品列表页
// if (domain !== null && !params.shop_id) {
// let urlPro = {
// from: 'search',
// query: params.query
// };
// if (req.query.app_type) {
// urlPro = _.assign(urlPro, {
// app_type: req.query.app_type
// });
// }
// let url = helpers.urlFormat('', urlPro, domain);
// return res.redirect(url);
// }
// 品类名称为空时跳出
if (!result[1]) {
return;
... ... @@ -141,6 +162,7 @@ exports.list = (req, res, next) => {
_noLazy: true,
module: 'product',
page: 'search-list',
localCss: true,
pageHeader: headerModel.setNav({
navTitle: title
}),
... ... @@ -157,47 +179,6 @@ exports.list = (req, res, next) => {
};
/**
* 搜索主页
*/
exports.index = (req, res, next) => {
let title = '搜索',
uid = req.user.uid || 0;
((render) => {
if (_.get(req, 'app.locals.wap.search.removeHotSearch', false)) {
render([]);
} else {
req.ctx(searchModel).getSearchIndex({
gender: req.yoho.channel || 'boys',
uid: uid
}).then((result) => {
render(result);
}).catch(next);
}
})((result) => {
res.render('search/index', Object.assign({
module: 'product',
page: 'search-index',
pageHeader: headerModel.setNav({
navTitle: title
}),
pageFooter: true,
width750: true,
search: {
defaultTerms: (result && result.defaultTerms && result.defaultTerms.length) ?
result.defaultTerms[0].content : '',
url: helpers.urlFormat('', null, 'search'),
hotTerms: result.hotTerms,
wantTerms: result.guessTerms
}
}, searchProcess.getListSeoData()));
});
};
/**
* 联动搜索
*/
exports.fuzzyDatas = (req, res, next) => {
... ... @@ -213,12 +194,6 @@ exports.fuzzyDatas = (req, res, next) => {
* ajax 商品数据请求
*/
exports.search = (req, res, next) => {
let allowOrigin = _.get(req, 'headers.origin', null) ?
req.headers.origin : req.protocol + '://' + req.headers.host;
res.setHeader('Access-Control-Allow-Origin', allowOrigin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
let params = Object.assign({}, req.query);
let uid = req.user.uid || 0;
... ... @@ -226,8 +201,13 @@ exports.search = (req, res, next) => {
params.uid = uid;
}
if (params.query) {
params.query = stringProcess.decodeURIComponent(params.query);
}
params.isApp = req.yoho.isApp;
params.limit = 24;
params.physical_channel = req.yoho.channel && searchProcess.getChannelType(req.yoho.channel);
req.ctx(searchModel).getSearchData(params).then((result) => {
... ...
... ... @@ -587,10 +587,7 @@ module.exports = class extends global.yoho.BaseModel {
/**
* 使用经典模板的店铺首页
* @param {object} req
* @param {int} shopId 店铺id
* @param {int} uid 用户id
* @param {string} isApp app版本
* TODO 弃用
*/
getShopData(req, shopId, uid, isApp) {
let shopData = {};
... ...
... ... @@ -94,109 +94,15 @@ module.exports = class extends global.yoho.BaseModel {
}
/**
* 商品搜索接口请求
* TODO 按接口拆分
* @param {[object]} params
* @return {[array]}
* 新的搜索接口处理,替换 _searchGoods
*/
_searchGoods(params) {
let method = 'app.search.li';
if (_.isArray(params.query)) {
params.query = _.join(params.query, ',');
}
// 排除基本筛选项默认值为0的对象
for (let str in params) {
if (str !== 'order' && params[str] === '0' || params[str] === null) {
delete params[str];
}
}
/* tar add 160823 店铺销售类目 */
if (params.filter_poolId) {
method = 'app.search.pool';
Object.assign(params, {
productPool: params.filter_poolId
});
delete params.filter_poolId;
}
if (params.shop_id && !params.productPool) {
method = 'app.search.li';
}
if (params.channel) {
params.yh_channel = searchProcess.getChannelType(params.channel);
delete params.channel;
}
if (params.query) {
params.query = params.query.replace(/\+/g, ',');
}
if (params.app_type && params.app_type === '1') {
params.app_type = 1;
}
params = _.assign({
limit: '24',
status: 1,
sales: 'Y',
stocknumber: 1,
attribute_not: 2
}, params);
if (params.type === 'default') {
// 筛选 默认 不传order
delete params.order;
} else if (params.order) {
params.order = searchProcess.getTypeCont(params.type || '', params.order);
} else {
params.order = 's_t_asc';
}
// 学生返币查询
if (params.coin) {
method = 'app.student.rebate';
delete params.filter_poolId;
if (params.type === 'newest') {
delete params.order;
delete params.type;
}
}
if (params.isblknew) {
method = 'app.search.newProduct';
params.app_type = 1;
if (params.type === 'default') {
params.order = 's_t_asc';
}
}
if (params.promotion_id) {
method = 'app.search.promotion';
}
if (params.students) {
method = 'app.student.discount';
}
// 个人中心优惠券立即使用 - 商品列表
if (params.coupon_id || params.coupon_code) {
method = 'app.search.coupon';
}
// 物料商品列表增加
if (params.material === 'true') {
method = 'app.search.recommendProduct';
}
_queryGoods(params) {
let paramsForApi = searchProcess.getSearchParamsWithoutMethod(params);
return this.get({
data: _.assign({
method: method
}, params),
method: 'app.search.li'
}, paramsForApi),
param: {
cache: true
}
... ... @@ -246,7 +152,7 @@ module.exports = class extends global.yoho.BaseModel {
// client_type 全部赋值为h5 过滤掉嵌入APP页面是默认的client_type
params.client_type = 'h5';
return this._searchGoods(params).then((result) => {
return this._queryGoods(params).then((result) => {
if (result && result.code === 200) {
let newList = {};
let suggestion = {};
... ... @@ -426,10 +332,8 @@ module.exports = class extends global.yoho.BaseModel {
filterDataResult = yield self.getBrandGoods(params);
} else if (params.isShopList === 'Y') { // 无店铺有店铺 ID 的商品列表
filterDataResult = yield self.getShopGoods(params);
} else if (params.isNewList === 'Y') { // 新的 SEO 友好的品类落地页
} else { // 新的 SEO 友好的品类落地页
filterDataResult = yield self.getCategoryGoods(params);
} else {
filterDataResult = yield self._searchGoods(params);
}
let filterData = _.get(filterDataResult, 'data.filter', []);
... ... @@ -443,23 +347,6 @@ module.exports = class extends global.yoho.BaseModel {
}
/**
* 获取筛选数据
* @param {[object]} params
* @return {[array]}
* TODO 这个可能不用了,待确定
*/
getFilterSearchData(params) {
return this._searchGoods(params).then((result) => {
if (result && result.code === 200) {
return result.data;
} else {
logger.error('get filter data api return code is not 200');
return [];
}
});
}
/**
* 获取所有的品类名称
**/
getClassNames() {
... ...
... ... @@ -141,20 +141,23 @@ router.get('/seckill/list', seckill.indexData);
router.post('/seckill/remind', seckill.remind); // only app; 秒杀提醒
router.get('/seckill/get-product-list', seckill.getProductList); // 秒杀列表根据活动id获取商品列表
router.get('/search/index', search.index); // 搜索主页
router.get('/search/so/:query.html', rewrite.sortParams, chanpin.keyword); // 推广落地页
router.get('/search/chanpin/:id.html', rewrite.sortParams, chanpin.keyId);
router.get('/search/chanpin/goods', chanpin.searchGoods); // 搜索的商品
router.get('/search/list', rewrite.sortParams, search.list); // 搜索落地页
router.get('/search', search.index); // 搜索主页
router.get('/search/list', search.list); // 搜索落地页
router.get('/search/filter', search.filter); // filter
router.get('/search/fuzzyDatas', search.fuzzyDatas); // fuzzyDatas
router.get('/search/search', search.search); // ajax 请求商品数据
router.get('/search/so/:query.html', rewrite.sortParams, chanpin.keyword); // 推广落地页
router.get('/search/chanpin/:id.html', rewrite.sortParams, chanpin.keyId);
router.get('/search/chanpin/goods', chanpin.searchGoods); // 搜索的商品
router.get('/search/brand/goods', search.searchBrandGoods); // 搜索品牌下的商品
router.get('/search/shop/goods', search.searchShopGoods); // 搜索店铺下的商品
router.get('/list/global(/:pathParams)?', rewrite.resolvePathParams, globalPro.list); // 全球购路由重写 全球购列表页
router.get('/index/index', rewrite.sortParams, list.category); // 旧品类首页
router.get('/list/index', rewrite.sortParams, list.category); // 兼容 PC 的链接
// router.get('/index/index', rewrite.sortParams, list.category); // 旧 品类首页
// router.get('/list/index', rewrite.sortParams, list.category); // 旧 兼容 PC 的链接
router.get('/list(/:pathParams)?', rewrite.resolvePathParams, list.listNew); // 列表新的 URL
router.get('/search/category',
cors,
... ...
... ... @@ -29,7 +29,7 @@
<div class="search-group history-search hide">
<div class="search-content-title">
<h3 class="left">最近搜索</h3>
<i id="clear-history" class="right ico-del hide"></i>
<i id="clear-history" class="iconfont right ico-del hide">&#xe64c;</i>
</div>
<div class="search-content">
<ul class="history clearfix"></ul>
... ...
... ... @@ -31,21 +31,13 @@ module.exports = () => {
case 'cdnsrclist':// CDN list 回源域名
return res.redirect(301, listParamsProcess.generatePathUrl(req.query));
case 'search': // search
if (req.path === '/') {
// 有查询关键字
if (_.keys(req.query).length) {
req.url = `/product/search/list?${querystring.stringify(req.query)}`;
return res.redirect(301, listParamsProcess.generatePathUrl(req.query, 'search/list'));
} else {
req.url = `/product/search/index?${querystring.stringify(req.query)}`;
}
return res.redirect(301, listParamsProcess.generatePathUrl(req.query, 'search'));
}
if (req.path === '/search') {
req.url = `/product/search/index?${querystring.stringify(req.query)}`;
}
break;
// 已经废弃,只是对老页面做兼容 --start
case 'sale': // sale 跳转到 m.yohobuy.com/product/sale
if (_.keys(req.query).length) {
... ...
... ... @@ -73,8 +73,8 @@ module.exports = () => {
req.url = `/product${req.url}`;
}
if (/^\/sale/.test(req.url)) {
// 匹配 life-style-sale
if (/^\/search/.test(req.url)) {
// 搜索
req.url = `/product${req.url}`;
}
... ...
... ... @@ -16,7 +16,8 @@ const loading = require('plugin/loading');
class ProductListWithFilter {
constructor(filterParams, searchUrl, extra) {
this.scrollActived = true; // 是否激活滚动加载,默认激活
this.scrollActived = extra && typeof extra.scrollActived !== 'undefined' ?
extra.scrollActived : true; // 是否激活滚动加载,默认激活
this.filterParams = filterParams;
this.searchUrl = location.protocol + '//m.yohobuy.com/' + (searchUrl || 'product/search/search');
this.filterUrl = location.protocol + '//m.yohobuy.com/product/search/filter';
... ...
/**
* 搜索商品列表页
* @author: wsl<shuiling.wang@yoho.cn>
* @date: 2016/7/21
* 商品列表页、品类落地页
*/
require('product/search/list.page.css');
... ...
/**
* 搜索商品列表页
* @author: wsl<shuiling.wang@yoho.cn>
* @date: 2016/7/21
*/
import 'product/search/list.page.css';
import Page from 'yoho-page';
import noResultHbs from 'product/search/no-result-new.hbs';
import ProductListWithFilter from './list/product-list-with-filter';
import qs from 'yoho-qs';
import SearchListExtra from './search/search-list-extra';
import 'common/footer';
require('product/search/list.page.css');
class Search extends Page {
constructor() {
super();
require('./search/list');
require('common/footer');
require('./shop/coupon');
this.selector = {
goodsContainer: $('#goods-container'),
defaultGoods: $('.default-goods')
};
this.firstScreen = this.selector.defaultGoods.children().size() > 0;
if (!this.firstScreen) {
this.selector.goodsContainer.html(noResultHbs());
}
let initParams = {
page: 2
};
if (qs) {
$.extend(initParams, qs);
}
new ProductListWithFilter(initParams, null, {
scrollActived: this.firstScreen
});
new SearchListExtra();
}
}
export default new Search();
... ...
import Page from 'yoho-page';
import Tip from 'plugin/tip';
import maybeLike from 'channel/maybe-like';
class SearchListExtra extends Page {
constructor() {
super();
this.selector = {
input: $('#search-input').find('input[name="query"]'),
clear: $('#search-input .clear-input'),
buriedpoint: $('.buriedpoint'),
search: $('#search')
};
this.selector.clear.on('click', () => {
this.selector.input.val('').trigger('input');
});
this.init();
}
init() {
maybeLike({recpose: 100101, isExecute: true});
this.inputAction();
}
/**
* 搜索输入联动
*/
inputAction() {
let $searchAssociate = $('.search-associate');
let $icon = $('.search-icon');
let $searchItems = $('.search-items');
this.selector.input.on('input', () => {
if (this.selector.input.val() === '') {
$icon.css('color', '#b2b2b2');
this.selector.clear.addClass('hide');
$searchAssociate.html('');
$searchItems.show();
$searchAssociate.hide();
} else {
$icon.css('color', '#666');
this.selector.clear.removeClass('hide');
$searchAssociate.show();
}
// 联动搜索
$.ajax({
url: '/product/search/fuzzyDatas',
data: {
keyword: this.selector.input.val()
},
dataType: 'json',
success: (data) => {
let ajaxHtml = '';
let i;
if (data.length > 0) {
for (i = 0; i < data.length; i++) {
ajaxHtml += '<li><span class="keyword">' + data[i].keyword + '</span><span class="count">' +
data[i].count + ' items<i class="iconfont">&#xe614;</i></span></li>';
}
$searchAssociate.html(ajaxHtml);
$searchItems.hide();
} else {
$searchAssociate.html('');
}
$searchAssociate.find('li').on('click', event => {
this.selector.buriedpoint.val($(event.currentTarget).find('.keyword').html());
this.selector.search.closest('form').submit();
});
},
error: () => {
Tip.show('网络断开连接了~');
}
});
});
}
}
export default SearchListExtra;
... ...
... ... @@ -43,7 +43,7 @@
.clear-input {
position: absolute;
top: 10px;
top: 4px;
right: 100px;
}
... ...
... ... @@ -171,7 +171,7 @@
width: 24px;
height: 26px;
display: inline-block;
background: url("/search/del-ico.png") no-repeat;
color: #b0b0b0;
}
.left {
... ...
... ... @@ -335,6 +335,9 @@ const getSearchParamsWithoutMethod = (params) => {
if (params.saleType) {
finalParams.saleType = params.saleType;
}
if (params.physical_channel) {
finalParams.physical_channel = params.physical_channel;
}
return finalParams;
};
... ...