Authored by 王水玲

Merge branch 'feature/newSearch' of git.yoho.cn:fe/yohobuywap-node into feature/newSearch

@@ -114,6 +114,42 @@ const list = (req, res, next) => { @@ -114,6 +114,42 @@ const list = (req, res, next) => {
114 }; 114 };
115 115
116 /** 116 /**
  117 + * 搜索主页
  118 + */
  119 +const index = (req, res, next) => {
  120 + let title = '搜索';
  121 +
  122 + searchModel.getSearchIndex().then((result) => {
  123 + res.render('search/index', {
  124 + module: 'product',
  125 + page: 'search-index',
  126 + pageHeader: headerModel.setNav({
  127 + navTitle: title
  128 + }),
  129 + pageFooter: true,
  130 + search: {
  131 + defaultTerms: result[0].content || '',
  132 + url: helpers.urlFormat('', null, 'search'),
  133 + hotTerms: result
  134 + }
  135 +
  136 + });
  137 + }).catch(next);
  138 +};
  139 +
  140 +/**
  141 + * 联动搜索
  142 + */
  143 +let fuzzyDatas = (req, res, next) => {
  144 + let params = req.query.keyword;
  145 +
  146 + searchModel.getFuzzyDatas(params).then((result) => {
  147 + res.json(result);
  148 + }).catch(next);
  149 +};
  150 +
  151 +
  152 +/**
117 * ajax 商品数据请求 153 * ajax 商品数据请求
118 */ 154 */
119 const search = (req, res, next) => { 155 const search = (req, res, next) => {
@@ -145,5 +181,7 @@ let filter = (req, res, next) => { @@ -145,5 +181,7 @@ let filter = (req, res, next) => {
145 module.exports = { 181 module.exports = {
146 list, 182 list,
147 filter, 183 filter,
148 - search 184 + search,
  185 + index,
  186 + fuzzyDatas
149 }; 187 };
@@ -170,9 +170,41 @@ const getAllBrandNames = () => { @@ -170,9 +170,41 @@ const getAllBrandNames = () => {
170 }); 170 });
171 }; 171 };
172 172
  173 +/**
  174 + * 获取热门搜索
  175 + **/
  176 +const getSearchIndex = () => {
  177 + return api.get('', {
  178 + method: 'app.search.getTerms'
  179 + }).then((result) => {
  180 + if (result && result.code === 200) {
  181 + return result.data.hotTerms;
  182 + } else {
  183 + logger.error('Hot Search return code is not 200');
  184 + return {};
  185 + }
  186 + });
  187 +};
  188 +
  189 +const getFuzzyDatas = (params) => {
  190 + return api.get('', {
  191 + method: 'app.search.fuzzy',
  192 + keyword: params
  193 + }).then((result) => {
  194 + if (result && result.code === 200) {
  195 + return result.data;
  196 + } else {
  197 + logger.error('FuzzyDatas return code is not 200');
  198 + return {};
  199 + }
  200 + });
  201 +};
  202 +
173 module.exports = { 203 module.exports = {
174 getSearchData, 204 getSearchData,
175 getFilterData, 205 getFilterData,
176 getAllBrandNames, 206 getAllBrandNames,
177 - getClassNames 207 + getClassNames,
  208 + getSearchIndex,
  209 + getFuzzyDatas
178 }; 210 };
@@ -61,12 +61,18 @@ router.get('/outlet/activityinfo', outlet.activityList); @@ -61,12 +61,18 @@ router.get('/outlet/activityinfo', outlet.activityList);
61 router.get('/recommend-for-you/userCenter', recommendForYou.userCenter); 61 router.get('/recommend-for-you/userCenter', recommendForYou.userCenter);
62 router.get('/recommend-for-you/cart', recommendForYou.cart); 62 router.get('/recommend-for-you/cart', recommendForYou.cart);
63 63
  64 +// 搜索主页
  65 +router.get('/search/index', search.index);
  66 +
64 // 搜索落地页 67 // 搜索落地页
65 router.get('/search/list', search.list); 68 router.get('/search/list', search.list);
66 69
67 // filter 70 // filter
68 router.get('/search/filter', search.filter); 71 router.get('/search/filter', search.filter);
69 72
  73 +// fuzzyDatas
  74 +router.get('/search/fuzzyDatas', search.fuzzyDatas);
  75 +
70 // ajax 请求商品数据 76 // ajax 请求商品数据
71 router.get('/search/search', search.search); 77 router.get('/search/search', search.search);
72 78
  1 +<div class="search-page yoho-page">
  2 + {{# search}}
  3 + <input type="hidden" value="{{defaultTerms}}" id="default-terms">
  4 + <div id="search-input" class="search-input">
  5 + <form id="search-form" action="{{url}}" method="get">
  6 + <i class="search-icon iconfont">&#xe60f;</i>
  7 + <input type="text" placeholder="{{#if defaultTerms}}{{defaultTerms}}{{else}}搜索商品、品牌{{/if}}" name="query" data-bp-id="search_page_input_1" class="buriedpoint" autocomplete="off">
  8 + <input type="hidden" name="from" value="search">
  9 + <i class="clear-input iconfont hide">&#xe626;</i>
  10 + <span id="search" class="search buriedpoint" type="submit" data-bp-id="search_index_one_0">搜索</span>
  11 + </form>
  12 + </div>
  13 + <ul class="search-associate"></ul>
  14 + <div class="search-items">
  15 + <div class="search-group history-search">
  16 + <div class="search-content-title">
  17 + <h3 class="left"><i class="ico-lately"></i>最近搜索</h3>
  18 + <i id="clear-history" class="right ico-del hide"></i>
  19 + </div>
  20 + <div class="search-content">
  21 + <ul class="history clearfix"></ul>
  22 + </div>
  23 + </div>
  24 + {{#if hotTerms}}
  25 + <div class="search-group hot-search">
  26 + <h3><i class="ico-hot"></i>热门搜索</h3>
  27 + <div class="search-content">
  28 + <ul class="hot clearfix">
  29 + {{# hotTerms}}
  30 + <li>
  31 + <a href='javascript:void(0);'>{{content}}</a>
  32 + </li>
  33 + {{/ hotTerms}}
  34 + </ul>
  35 + </div>
  36 + </div>
  37 + {{/if}}
  38 + </div>
  39 + {{/ search}}
  40 +</div>
  1 +/**
  2 + * 搜索主页
  3 + * @author: zxr<xiaoru.zhang@yoho.cn>
  4 + * @date: 2016/8/15
  5 + */
  6 +
  7 +require('./search/index');
  8 +require('../common/footer');
  1 +/*
  2 + * @Description: dialog
  3 + */
  4 +
  5 +var $ = require('yoho-jquery'),
  6 + Handlebars = require('yoho-handlebars'),
  7 + Hammer = require('yoho-hammer');
  8 +
  9 +var $dialogWrapper,
  10 + dialogTpl,
  11 + dialogTemplate;
  12 +
  13 +function getInstance() {
  14 + if (dialogTpl === undefined) {
  15 + dialogTpl = '<div id="dialog-wrapper" class="dialog-wrapper">' +
  16 + '<div class="dialog-box">' +
  17 + '{{# hasHeader}}' +
  18 + '{{/ hasHeader}}' +
  19 + '<div class="dialog-content">{{dialogText}}</div>' +
  20 + '{{# hasFooter}}' +
  21 + '<div class="dialog-footer">' +
  22 + '{{# leftBtnText}}' +
  23 + '<span class="dialog-left-btn tap-hightlight">{{.}}</span>' +
  24 + '{{/ leftBtnText}}' +
  25 + '{{# rightBtnText}}' +
  26 + '<span class="dialog-right-btn tap-hightlight">{{.}}</span>' +
  27 + '{{/ rightBtnText}}' +
  28 + '</div>' +
  29 + '{{/ hasFooter}}' +
  30 + '</div>' +
  31 + '</div>';
  32 +
  33 + dialogTemplate = Handlebars.compile(dialogTpl);
  34 + }
  35 + return dialogTemplate;
  36 +}
  37 +
  38 +// fullWithBtn是供详情页获取限购码使用的特殊参数
  39 +exports.showDialog = function(data, callback, callbackForLeft, fullWithBtn) {
  40 +
  41 + var dialogStr = dialogTemplate(data),
  42 + $dialogBox,
  43 + defaultHideDuraton,
  44 + dialogWrapperHammer;
  45 +
  46 + dialogTemplate = getInstance();
  47 +
  48 + $('.dialog-wrapper').remove();
  49 +
  50 + $('body').append($(dialogStr));
  51 +
  52 + $dialogBox = $('.dialog-box');
  53 + $dialogWrapper = $('.dialog-wrapper');
  54 +
  55 +
  56 + dialogWrapperHammer = new Hammer(document.getElementById('dialog-wrapper'));
  57 +
  58 + // 显示
  59 + if (data.fast) {
  60 + $dialogWrapper.css({
  61 + display: 'block'
  62 + });
  63 + } else {
  64 + $dialogWrapper.fadeIn();
  65 + }
  66 +
  67 + if (fullWithBtn) {
  68 + $('.dialog-wrapper .dialog-footer > span').css('width', '100%');
  69 + $('.dialog-wrapper .dialog-content').css({
  70 + 'padding-left': '1.85rem',
  71 + 'padding-right': '1.85rem'
  72 + });
  73 + $dialogWrapper.css('z-index', '10');
  74 + }
  75 +
  76 + $dialogBox.css({
  77 + top: '50%',
  78 + marginTop: -($dialogBox.height() / 2)
  79 + });
  80 +
  81 + // 隐藏
  82 + if (data.autoHide) {
  83 + defaultHideDuraton = 1000;
  84 + if (data.autoHide > 1) {
  85 + defaultHideDuraton = data.autoHide;
  86 + }
  87 + setTimeout(function() {
  88 + $dialogWrapper.fadeOut();
  89 + }, defaultHideDuraton);
  90 + }
  91 +
  92 + // 禁止在dialog上可以上下滚动
  93 + $dialogWrapper.on('touchmove', function() {
  94 + return false;
  95 + });
  96 +
  97 + dialogWrapperHammer.on('tap', function(event) {
  98 +
  99 + if ($(event.target).hasClass('dialog-left-btn')) {
  100 + if (typeof callbackForLeft === 'function') {
  101 + callbackForLeft();
  102 + }
  103 + $dialogWrapper.fadeOut();
  104 + } else if ($(event.target).hasClass('dialog-right-btn')) {
  105 + callback();
  106 + }
  107 +
  108 + // 防止出现点透问题
  109 + event.preventDefault();
  110 + event.srcEvent.stopPropagation();
  111 + });
  112 +};
  113 +
  114 +exports.hideDialog = function() {
  115 + $('.dialog-wrapper').remove();
  116 +};
  1 +/**
  2 + * 搜索页
  3 + * @author:zxr
  4 + * @date: 2016/8/15
  5 + */
  6 +
  7 +var $ = require('yoho-jquery'),
  8 + security = require('../../plugin/security'),
  9 + tip = require('../../plugin/tip'),
  10 + Hammer = require('yoho-hammer'),
  11 + dialog = require('../search/dialog');
  12 +
  13 +var $input = $('#search-input input');
  14 +
  15 +var $clear = $('#search-input .clear-input');
  16 +
  17 +var $form = $('#search-form');
  18 +
  19 +var $history = $('.history');
  20 +var $historySearch = $('.history-search');
  21 +var $clearHistory = $('#clear-history');
  22 +
  23 +// var $buriedpoint = $('.buriedpoint');
  24 +var $search = $('#search');
  25 +var searchUrl = $search.closest('form').attr('action');
  26 +
  27 +var writeSearch = require('./write-search');
  28 +
  29 +var ranToken = writeSearch.getRanToken();
  30 +var historyval = writeSearch.getHistoryval();
  31 +
  32 +var chHammer, cHammer;
  33 +
  34 +chHammer = new Hammer($clearHistory[0]);
  35 +
  36 +chHammer.on('tap', function() {
  37 + dialog.showDialog({
  38 + dialogText: '您确定要删除您的最近搜索吗?',
  39 + hasFooter: {
  40 + leftBtnText: '取消',
  41 + rightBtnText: '确定'
  42 + }
  43 + }, function() {
  44 +
  45 + if (localStorage) {
  46 + localStorage.removeItem(historyval);
  47 + }
  48 +
  49 + $history.html('');
  50 + $historySearch.hide();
  51 + $clearHistory.hide();
  52 +
  53 + // window.rePosFooter();
  54 +
  55 + dialog.showDialog({
  56 + dialogText: '删除成功',
  57 + autoHide: true,
  58 + fast: true
  59 + });
  60 + });
  61 +});
  62 +
  63 +// 跳到搜索页
  64 +function goSearch(query) {
  65 + // 保存搜索的内容
  66 + writeSearch.setHistoryValFun(query);
  67 + document.location.href = searchUrl + '?query=' + query;
  68 +}
  69 +
  70 +// 搜索输入联动
  71 +function inputAction() {
  72 + var $searchAssociate = $('.search-associate');
  73 + var $icon = $('.search-icon');
  74 + var $searchItems = $('.search-items');
  75 +
  76 + $input.on('input', function() {
  77 + if ($input.val() === '') {
  78 + $icon.css('color', '#b2b2b2');
  79 + $clear.addClass('hide');
  80 + $searchAssociate.html('');
  81 + $searchItems.show();
  82 + $searchAssociate.hide();
  83 + } else {
  84 + $icon.css('color', '#666');
  85 + $clear.removeClass('hide');
  86 + $searchAssociate.show();
  87 + }
  88 +
  89 + // 联动搜索
  90 + $.ajax({
  91 + url: '/product/search/fuzzyDatas',
  92 + data: {
  93 + keyword: $input.val()
  94 + },
  95 + dataType: 'json',
  96 + success: function(data) {
  97 + var ajaxHtml = '';
  98 + var i;
  99 +
  100 + if (data.length > 0) {
  101 + console.log(data.length);
  102 + for (i = 0; i < data.length; i++) {
  103 + ajaxHtml += '<li><span class="keyword">' + data[i].keyword + '</span><span class="count">' +
  104 + data[i].count + ' items<i class="iconfont">&#xe614;</i></span></li>';
  105 + }
  106 +
  107 + $searchAssociate.html(ajaxHtml);
  108 + $searchItems.hide();
  109 + } else {
  110 + $searchAssociate.html('');
  111 + }
  112 +
  113 + $searchAssociate.find('li').on('touchend', function() {
  114 + goSearch($(this).find('.keyword').html());
  115 + });
  116 + },
  117 + error: function() {
  118 + tip.show('网络断开连接了~');
  119 + }
  120 + });
  121 + });
  122 +}
  123 +
  124 +// 热门搜索、最近搜索事件
  125 +$('.search-items .search-group').on('click', 'li', function(event) {
  126 + var query = '';
  127 +
  128 + if (event.target.nodeName === 'A') {
  129 + query = $(event.target).html();
  130 + }
  131 +
  132 + if (event.target.nodeName === 'LI') {
  133 + query = $(event.target).find('a').html();
  134 + }
  135 +
  136 + goSearch(query);
  137 +});
  138 +
  139 +inputAction();
  140 +
  141 +cHammer = new Hammer($clear[0]);
  142 +cHammer.on('tap', function() {
  143 + $input.val('').trigger('input');
  144 +});
  145 +
  146 +$search.on('touchend', function() {
  147 + var $buriedpoint = $form.find('.buriedpoint');
  148 +
  149 + if ($buriedpoint.val() === '') {
  150 + $buriedpoint.val($('#default-terms').val());
  151 + }
  152 +
  153 + // 保存搜索的内容
  154 + writeSearch.setHistoryValFun($buriedpoint.val());
  155 +
  156 + if (security.hasDangerInput()) {
  157 + return false;
  158 + }
  159 +
  160 + $(this).closest('form').submit();
  161 + return false;
  162 +});
  163 +
  164 +// 初始化历史搜索的内容
  165 +(function() {
  166 + var html = '',
  167 + history,
  168 + historys, i, num = 1;
  169 +
  170 + if (localStorage) {
  171 + historys = localStorage.getItem(historyval);
  172 +
  173 + if (historys && historys.length > 0) {
  174 + historys = historys.split(ranToken);
  175 + for (i = historys.length; i > 0; i--) {
  176 + history = historys[i - 1];
  177 +
  178 + if (history === '') {
  179 + continue;
  180 + }
  181 +
  182 + if (num++ > 10) {
  183 + break;
  184 + }
  185 +
  186 + html += '<li><a href="javascript:void(0);">' + history + '</li>';
  187 + }
  188 +
  189 + $history.html(html);
  190 + if (html !== '') {
  191 + $clearHistory.removeClass('hide');
  192 + $historySearch.removeClass('hide');
  193 + }
  194 +
  195 + // window.rePosFooter();
  196 + }
  197 + }
  198 +}());
@@ -92,7 +92,7 @@ function inputAction() { @@ -92,7 +92,7 @@ function inputAction() {
92 92
93 // 联动搜索 93 // 联动搜索
94 $.ajax({ 94 $.ajax({
95 - url: '/index/search/fuzzyDatas', 95 + url: '/product/search/fuzzyDatas',
96 data: { 96 data: {
97 keyword: $input.val() 97 keyword: $input.val()
98 }, 98 },
1 @import "search"; 1 @import "search";
2 @import "list"; 2 @import "list";
  3 +@import "search-index";
  1 +.search-associate {
  2 + width: 100%;
  3 + background: #f8f8f8;
  4 + display: none;
  5 + position: absolute;
  6 + z-index: 1;
  7 +
  8 + li {
  9 + height: 84px;
  10 + line-height: 84px;
  11 + width: 100%;
  12 + padding: 0 20px;
  13 + clear: both;
  14 + margin-bottom: 5px;
  15 + background: #fff;
  16 + box-sizing: border-box;
  17 + }
  18 +
  19 + .keyword {
  20 + float: left;
  21 + font-size: 30px;
  22 + }
  23 +
  24 + .count {
  25 + float: right;
  26 + color: #b0b0b0;
  27 + font-size: 18px;
  28 +
  29 + i {
  30 + font-size: 12PX;
  31 + margin-left: 10px;
  32 + position: relative;
  33 + top: 3px;
  34 + }
  35 + }
  36 +}
  37 +
  38 +.search-brand-page {
  39 + .search-input {
  40 + position: relative;
  41 + padding: 14px 22px;
  42 + background: #f8f8f8;
  43 + form {
  44 + width: 100%;
  45 + }
  46 + .search-icon {
  47 + position: absolute;
  48 + font-size: 24px;
  49 + top: 26px;
  50 + left: 36px;
  51 + color: #b2b2b2;
  52 + }
  53 +
  54 + input {
  55 + height: 56px;
  56 + width: 378px;
  57 + border-radius: 28px;
  58 + padding: 0 52px;
  59 + font-size: 24px;
  60 + background: #fff;
  61 + border: none;
  62 + }
  63 +
  64 + .clear-input {
  65 + position: absolute;
  66 + top: 20px;
  67 + right: 145px;
  68 + font-size: 32px;
  69 + color: #666;
  70 + }
  71 +
  72 + .search {
  73 + position: absolute;
  74 + top: 18px;
  75 + right: 40px;
  76 + border: none;
  77 + background: transparent;
  78 + color: #666;
  79 + font-size: 30px;
  80 + line-height: 56px;
  81 + }
  82 + }
  83 +
  84 + .search-items {
  85 + padding: 40px 20px;
  86 +
  87 + h3 {
  88 + font-size: 24px;
  89 + margin-bottom: 20px;
  90 + color: #b8b8b8;
  91 + }
  92 +
  93 + li {
  94 + float: left;
  95 + margin-right: 20px;
  96 + margin-bottom: 20px;
  97 + max-width: 100%;
  98 + overflow: hidden;
  99 + }
  100 +
  101 + a {
  102 + display: block;
  103 + height: 68px;
  104 + line-height: 68px;
  105 + padding: 0 20px;
  106 + font-size: 28px;
  107 + background: white;
  108 + color: #b8b8b8;
  109 + overflow: hidden;
  110 + text-overflow: ellipsis;
  111 + white-space: nowrap;
  112 + border: 1px solid #b8b8b8;
  113 + border-radius: 0.2rem;
  114 + }
  115 +
  116 + .clear-history {
  117 + font-size: 28px;
  118 + }
  119 +
  120 + .clear-icon {
  121 + float: right;
  122 + color: #b8b8b8;
  123 + border: none;
  124 + background: white;
  125 + }
  126 +
  127 + span {
  128 + margin-right: 10px;
  129 + font-size: 14px;
  130 + }
  131 +
  132 + .history-search {
  133 + border-bottom: 1px solid #f3f3f3;
  134 + }
  135 +
  136 + .hot-search {
  137 + margin-top: 20px;
  138 + }
  139 +
  140 + .clearfix {
  141 + margin-left: 30px;
  142 + }
  143 +
  144 + .left-icon {
  145 + font-size: 24px;
  146 + }
  147 + }
  148 +}