浏览代码

Search fixes (#2746)

* GS in awesome bar, UI fixes: scroll nav,lists

* awesome bar specificity

* Add fuzzy search in awesome bar

* Add fuzzy specific boldening

* List subtype buttons

* Update global_search.py
version-14
Prateeksha Singh 8 年前
committed by Rushabh Mehta
父节点
当前提交
e9f2aadd38
共有 8 个文件被更改,包括 415 次插入171 次删除
  1. +3
    -3
      frappe/core/doctype/communication/communication.json
  2. +3
    -2
      frappe/email/doctype/contact/contact.json
  3. +24
    -6
      frappe/public/css/desk.css
  4. +204
    -101
      frappe/public/js/frappe/ui/toolbar/awesome_bar.js
  5. +2
    -0
      frappe/public/js/frappe/ui/toolbar/search.html
  6. +139
    -50
      frappe/public/js/frappe/ui/toolbar/search.js
  7. +38
    -8
      frappe/public/less/desk.less
  8. +2
    -1
      frappe/utils/global_search.py

+ 3
- 3
frappe/core/doctype/communication/communication.json 查看文件

@@ -408,7 +408,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0,
"in_global_search": 1,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Communication Type", "label": "Communication Type",
@@ -759,7 +759,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 1,
"in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Reference Name", "label": "Reference Name",
@@ -1379,7 +1379,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-02-17 16:41:05.429760",
"modified": "2017-02-21 04:57:33.141998",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Core", "module": "Core",
"name": "Communication", "name": "Communication",


+ 3
- 2
frappe/email/doctype/contact/contact.json 查看文件

@@ -81,7 +81,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 1,
"in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Last Name", "label": "Last Name",
@@ -554,7 +554,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-01-31 00:15:30.298287",
"modified": "2017-02-20 14:54:33.723052",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Email", "module": "Email",
"name": "Contact", "name": "Contact",
@@ -805,6 +805,7 @@
"quick_entry": 0, "quick_entry": 0,
"read_only": 0, "read_only": 0,
"read_only_onload": 0, "read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_order": "ASC", "sort_order": "ASC",
"track_changes": 0, "track_changes": 0,
"track_seen": 0 "track_seen": 0

+ 24
- 6
frappe/public/css/desk.css 查看文件

@@ -687,8 +687,18 @@ fieldset[disabled] .form-control {
padding: 0px; padding: 0px;
overflow-y: auto; overflow-y: auto;
} }
.search-dialog .layout-side-section {
padding-left: 15px;
.search-dialog .layout-side-section .help-link {
padding-top: 20px;
text-transform: uppercase;
}
.search-dialog .layout-side-section .nav > li > a {
padding-left: 20px;
}
.search-dialog .results-area a {
color: #5E64FF;
}
.search-dialog .results-area .single-link a {
color: #36414c;
} }
.search-dialog .module-section .back-link { .search-dialog .module-section .back-link {
margin-bottom: 20px; margin-bottom: 20px;
@@ -698,11 +708,19 @@ fieldset[disabled] .form-control {
font-family: 'Octicons'; font-family: 'Octicons';
content: '\f0a4'; content: '\f0a4';
} }
.search-dialog .dual-section .result a {
color: black;
.search-dialog .full-list .result {
border-bottom: 1px solid #d1d8dd;
margin-top: 10px;
}
.search-dialog .full-list .result .result-subtype{
float: right;
margin-left: 10px;
} }
.search-dialog .dual-section .result a b {
color: #4e6161;
.search-dialog .full-list .section-head {
margin-bottom: 25px;
}
.search-dialog .dual-section .result-subtype {
display: none;
} }
.search-dialog .result-status { .search-dialog .result-status {
margin-top: 30px; margin-top: 30px;


+ 204
- 101
frappe/public/js/frappe/ui/toolbar/awesome_bar.js 查看文件

@@ -12,17 +12,21 @@ frappe.search.AwesomeBar = Class.extend({
this.nav = new frappe.search.NavSearch(); this.nav = new frappe.search.NavSearch();
this.help = new frappe.search.HelpSearch(); this.help = new frappe.search.HelpSearch();


this.options = [];
this.global_results = [];

var awesomplete = new Awesomplete(input, { var awesomplete = new Awesomplete(input, {
minChars: 0, minChars: 0,
maxItems: 99, maxItems: 99,
autoFirst: true, autoFirst: true,
list: [], list: [],
filter: function (text, term) {
return true;
filter: function (text, term) {
return true;
}, },
data: function (item, input) { data: function (item, input) {
var label = item.label + "%%%" + item.value + "%%%" +
(item.description || "") + "%%%" + (item.index || "");
var label = item.label + "%%%" + item.value + "%%%" +
(item.description || "") + "%%%" + (item.index || "")
+ "%%%" + (item.type || "") + "%%%" + (item.prefix || "");
return { return {
label: label, label: label,
value: item.value value: item.value
@@ -31,9 +35,16 @@ frappe.search.AwesomeBar = Class.extend({
item: function(item, term) { item: function(item, term) {
var d = item; var d = item;
var parts = item.split("%%%"), var parts = item.split("%%%"),
d = { label: parts[0], value: parts[1], description: parts[2] };
d = { label: parts[0], value: parts[1], description: parts[2],
type: parts[4], prefix: parts[5]};


var html = "<span>" + __(d.label || d.value) + "</span>";
if(d.prefix) {
var html = "<span>" + __((d.prefix + ' ' + d.label)) + "</span>";
} else if(d.type) {
var html = "<span>" + __((d.label + ' ' + d.type)) + "</span>";
} else {
var html = "<span>" + __(d.label || d.value) + "</span>";
}
if(d.description && d.value!==d.description) { if(d.description && d.value!==d.description) {
html += '<br><span class="text-muted">' + __(d.description) + '</span>'; html += '<br><span class="text-muted">' + __(d.description) + '</span>';
} }
@@ -42,29 +53,37 @@ frappe.search.AwesomeBar = Class.extend({
.html('<a style="font-weight:normal"><p>' + html + '</p></a>') .html('<a style="font-weight:normal"><p>' + html + '</p></a>')
.get(0); .get(0);
}, },
sort: function(a, b) {
sort: function(a, b) {
var a_index = a.split("%%%")[3]; var a_index = a.split("%%%")[3];
var b_index = b.split("%%%")[3]; var b_index = b.split("%%%")[3];
return (a_index - b_index);
return (a_index - b_index);
} }
}); });


$input.on("input", function(e) { $input.on("input", function(e) {
var value = e.target.value;
var txt = value.trim().replace(/\s\s+/g, ' ');
var last_space = txt.lastIndexOf(' ');

if(txt && txt.length > 2) {
me.global.get_awesome_bar_options(txt.toLowerCase(), me);
}

var $this = $(this); var $this = $(this);
clearTimeout($this.data('timeout')); clearTimeout($this.data('timeout'));


$this.data('timeout', setTimeout(function(){ $this.data('timeout', setTimeout(function(){
var value = e.target.value;
var txt = strip(value);
me.options = []; me.options = [];
if(txt) {
var keywords = strip(txt.toLowerCase());
me.build_options(keywords);
if(me.options.length < 2) {
me.global.get_awesome_bar_options(keywords, me);
if(txt && txt.length > 2) {
if(last_space !== -1) {
me.set_specifics(txt.slice(0,last_space), txt.slice(last_space+1));
} }
me.options = me.options.concat(me.build_options(txt));
me.build_defaults(txt);
me.options = me.options.concat(me.global_results);
} }


me.make_calculator(txt);
me.add_recent(txt || ""); me.add_recent(txt || "");
me.add_help(); me.add_help();


@@ -112,6 +131,7 @@ frappe.search.AwesomeBar = Class.extend({
} }


if(item.onclick) { if(item.onclick) {
// frappe.new_doc(item.match, true);
item.onclick(item.match); item.onclick(item.match);
} else { } else {
var previous_hash = window.location.hash; var previous_hash = window.location.hash;
@@ -135,7 +155,8 @@ frappe.search.AwesomeBar = Class.extend({
this.options.push({ this.options.push({
label: __("Help on Search"), label: __("Help on Search"),
value: "Help on Search", value: "Help on Search",
index: 20,
index: 50,
default: "Help",
onclick: function() { onclick: function() {
var txt = '<table class="table table-bordered">\ var txt = '<table class="table table-bordered">\
<tr><td style="width: 50%">'+__("Make a new record")+'</td><td>'+ <tr><td style="width: 50%">'+__("Make a new record")+'</td><td>'+
@@ -194,7 +215,8 @@ frappe.search.AwesomeBar = Class.extend({
out.label = match[0].bold(); out.label = match[0].bold();
out.value = match[0]; out.value = match[0];
} }
out.index = 10
out.index = 29;
out.default = "Recent";
return out; return out;
}, true); }, true);
}, },
@@ -231,45 +253,106 @@ frappe.search.AwesomeBar = Class.extend({
setup_recent: function() { setup_recent: function() {
this.recent = JSON.parse(frappe.boot.user.recent || "[]") || []; this.recent = JSON.parse(frappe.boot.user.recent || "[]") || [];
}, },
is_present: function(txt, item) {
($.isArray(item)) ? _item = item[0] : _item = item;
_item = __(_item || '').toLowerCase().replace(/-/g, " ");
if(txt===_item || _item.indexOf(txt) !== -1) {
return item;

fuzzy_search: function(txt, _item, index) {
item = __(_item || '').toLowerCase().replace(/-/g, " ");

txt = txt.toLowerCase();

var ilen = item.length;
var tlen = txt.length;
var match_level1 = 0.5;
var match_level2 = 0.8;
var index = ((tlen/ilen) > match_level1) ? 24 : index;
var rendered_label = "";
var i, j, skips = 0, mismatches = 0;

if (tlen > ilen) {
return [];
}
if (item.indexOf(txt) !== -1) {
// prefer single words
index = (item.indexOf(' ') === -1) ? index-1 : index;
index = ((tlen/ilen) > match_level2) ? 21 : index;

var regEx = new RegExp("("+ txt +")", "ig");
rendered_label = _item.replace(regEx, '<b>$1</b>');

return [_item, index, rendered_label];
}
outer: for (i = 0, j = 0; i < tlen; i++) {
var t_ch = txt.charCodeAt(i);
if(mismatches !== 0) skips++;
if(skips > 3) return [];
mismatches = 0;
while (j < ilen) {
var i_ch = item.charCodeAt(j);
if (i_ch === t_ch) {
var item_char = _item.charAt(j);
if(item_char === item_char.toLowerCase()){
rendered_label += '<b>' + txt.charAt(i) + '</b>';
} else {
rendered_label += '<b>' + txt.charAt(i).toUpperCase() + '</b>';
}
j++;
continue outer;
}
mismatches++;
if(mismatches > 2) return [];
rendered_label += _item.charAt(j);
j++;
}
return [];
} }
rendered_label += _item.slice(j);
return [_item, index + 10, rendered_label];
},

set_specifics: function(txt, end_txt) {
var me = this;
var results = this.build_options(txt);
results.forEach(function(r) {
if((r.type).toLowerCase().indexOf(end_txt.toLowerCase()) === 0) {
if(r.index < 25) {
r.index = 21;
}
me.options.push(r);
}
});
},

build_defaults: function(txt) {
this.make_global_search(txt);
this.make_search_in_current(txt);
this.options = this.options.concat(this.make_search_in_list(txt));
}, },


set_global_results: function(global_results){
this.options = this.options.concat(global_results);
build_options: function(txt) {
return this.make_new_doc(txt).concat(
this.get_doctypes(txt),
this.get_reports(txt),
this.get_pages(txt),
this.get_modules(txt)
);
}, },


build_options: function(txt) {
this.options =
this.make_global_search(txt).concat(
this.make_search_in_current(txt),
this.make_calculator(txt),
this.make_new_doc(txt),
this.make_search_in_list(txt),
this.get_doctypes(txt),
this.get_reports(txt),
this.get_pages(txt),
this.get_modules(txt)
);
set_global_results: function(global_results, txt){
this.global_results = this.global_results.concat(global_results);
}, },


make_global_search: function(txt) { make_global_search: function(txt) {
var me = this; var me = this;
return [{
this.options.push({
label: __("Search for '" + txt.bold() + "'"), label: __("Search for '" + txt.bold() + "'"),
value: __("Search for '" + txt + "'"), value: __("Search for '" + txt + "'"),
match: txt, match: txt,
index: 5,
index: 22,
default: "Search",
onclick: function() { onclick: function() {
me.search.search_dialog.show(); me.search.search_dialog.show();
me.search.setup_search(txt, [me.global, me.nav, me.help]); me.search.setup_search(txt, [me.global, me.nav, me.help]);
} }
}];
});
}, },


make_search_in_current: function(txt) { make_search_in_current: function(txt) {
@@ -280,17 +363,18 @@ frappe.search.AwesomeBar = Class.extend({
var search_field = meta.title_field || "name"; var search_field = meta.title_field || "name";
var options = {}; var options = {};
options[search_field] = ["like", "%" + txt + "%"]; options[search_field] = ["like", "%" + txt + "%"];
return [{
this.options.push({
label: __('Find {0} in {1}', [txt.bold(), route[1].bold()]), label: __('Find {0} in {1}', [txt.bold(), route[1].bold()]),
value: __('Find {0} in {1}', [txt, route[1]]), value: __('Find {0} in {1}', [txt, route[1]]),
route_options: options, route_options: options,
index: 10,
index: 23,
onclick: function() { onclick: function() {
cur_list.refresh(); cur_list.refresh();
}, },
default: "Current",
match: txt match: txt
}];
} else { return []; }
});
}
}, },


make_calculator: function(txt) { make_calculator: function(txt) {
@@ -302,34 +386,37 @@ frappe.search.AwesomeBar = Class.extend({
try { try {
var val = eval(txt); var val = eval(txt);
var formatted_value = __('{0} = {1}', [txt, (val + '').bold()]); var formatted_value = __('{0} = {1}', [txt, (val + '').bold()]);
return [{
this.options.push({
label: formatted_value, label: formatted_value,
value: __('{0} = {1}', [txt, val]), value: __('{0} = {1}', [txt, val]),
match: val, match: val,
index: 10,
index: 24,
default: "Calculator",
onclick: function() { onclick: function() {
msgprint(formatted_value, "Result"); msgprint(formatted_value, "Result");
} }
}];
});
} catch(e) { } catch(e) {
// pass // pass
} }
} else { return []; }
}
}, },


make_new_doc: function(txt) {
make_search_in_list: function(txt) {
var me = this; var me = this;
var out = []; var out = [];
if(txt.split(" ")[0]==="new") {
frappe.boot.user.can_create.forEach(function (item) {
var target = me.is_present(txt.substr(4), item);
if(in_list(txt.split(" "), "in") && (txt.slice(-2) !== "in")) {
parts = txt.split(" in ");
frappe.boot.user.can_read.forEach(function (item) {
var target = me.fuzzy_search(parts[1], item, 21)[0];
if(target) { if(target) {
out.push({ out.push({
label: __("New {0}", [target.bold()]),
value: __("New {0}", [target]),
index: 10,
match: target,
onclick: function() { frappe.new_doc(target, true); }
label: __('Find {0} in {1}', [__(parts[0]).bold(), __(target).bold()]),
value: __('Find {0} in {1}', [__(parts[0]), __(target)]),
route_options: {"name": ["like", "%" + parts[0] + "%"]},
index: 21,
default: "In List",
route: ["List", target]
}); });
} }
}); });
@@ -337,20 +424,23 @@ frappe.search.AwesomeBar = Class.extend({
return out; return out;
}, },


make_search_in_list: function(txt) {
make_new_doc: function(txt) {
var me = this; var me = this;
var out = []; var out = [];
if(in_list(txt.split(" "), "in")) {
parts = txt.split(" in ");
frappe.boot.user.can_read.forEach(function (item) {
target = me.is_present(parts[1], item);
if(txt.split(" ")[0]==="new") {
frappe.boot.user.can_create.forEach(function (item) {
var result = me.fuzzy_search(txt.substr(4), item, 21);
var target = result[0];
var rendered_label = result[2];
if(target) { if(target) {
out.push({ out.push({
label: __('Find {0} in {1}', [__(parts[0]).bold(), __(target).bold()]),
value: __('Find {0} in {1}', [__(parts[0]), __(target)]),
route_options: {"name": ["like", "%" + parts[0] + "%"]},
index: 10,
route: ["List", target]
label: rendered_label,
value: __("New {0}", [target]),
index: 21,
type: "New",
prefix: "New",
match: target,
onclick: function() { frappe.new_doc(target, true); }
}); });
} }
}); });
@@ -358,36 +448,38 @@ frappe.search.AwesomeBar = Class.extend({
return out; return out;
}, },


get_doctypes: function(txt) {
get_doctypes: function(txt) {
var me = this; var me = this;
var out = []; var out = [];


var target, index;
var result, target, index, rendered_label;
var option = function(type, route) { var option = function(type, route) {
return { return {
label: __("{0} " + type, [__(target).bold()]),
label: rendered_label,
value: __(target), value: __(target),
route: route, route: route,
index: index, index: index,
match: target
match: target,
type: type
} }
}; };
frappe.boot.user.can_read.forEach(function (item) { frappe.boot.user.can_read.forEach(function (item) {
target = me.is_present(txt, item);
result = me.fuzzy_search(txt, item, 25);
target = result[0];
index = result[1];
rendered_label = result[2];
if(target) { if(target) {
var match_ratio = txt.length / item.length;
index = (match_ratio > 0.7) ? 10 : 12;

// include 'making new' option
if(in_list(frappe.boot.user.can_create, target)) {
out.push({
label: __("New {0}", [target.bold()]),
value: __("New {0}", [target]),
match: target,
index: 12,
onclick: function() { frappe.new_doc(target, true); }
});
}
// include 'making new' option (not working)
// if(in_list(frappe.boot.user.can_create, target)) {
// out.push({
// label: rendered_label,
// value: __("New {0}", [target]),
// index: index,
// type: "New",
// prefix: "New",
// onclick: function() { frappe.new_doc(target, true); }
// });
// }
if(in_list(frappe.boot.single_types, target)) { if(in_list(frappe.boot.single_types, target)) {
out.push(option("", ["Form", target, target])); out.push(option("", ["Form", target, target]));


@@ -395,7 +487,7 @@ frappe.search.AwesomeBar = Class.extend({
out.push(option("Tree", ["Tree", target])); out.push(option("Tree", ["Tree", target]));


} else { } else {
out.push(option("List", ["List", target]));
out.push(option("List", ["List", target]));
if(frappe.model.can_get_report(target)) { if(frappe.model.can_get_report(target)) {
out.push(option("Report", ["Report", target])); out.push(option("Report", ["Report", target]));
} }
@@ -414,11 +506,12 @@ frappe.search.AwesomeBar = Class.extend({
var me = this; var me = this;
var out = []; var out = [];
Object.keys(frappe.boot.user.all_reports).forEach(function(item) { Object.keys(frappe.boot.user.all_reports).forEach(function(item) {
var target = me.is_present(txt, item);
var result = me.fuzzy_search(txt, item, 26);
var target = result[0];
var index = result[1];
var rendered_label = result[2];
if(target) { if(target) {
var report = frappe.boot.user.all_reports[target]; var report = frappe.boot.user.all_reports[target];
var match_ratio = txt.length / item.length;
var index = (match_ratio > 0.7) ? 10 : 13;
var route = []; var route = [];
if(report.report_type == "Report Builder") if(report.report_type == "Report Builder")
route = ["Report", report.ref_doctype, target]; route = ["Report", report.ref_doctype, target];
@@ -426,10 +519,12 @@ frappe.search.AwesomeBar = Class.extend({
route = ["query-report", target]; route = ["query-report", target];


out.push({ out.push({
label: __("Report {0}", [__(target).bold()]),
label: rendered_label,
value: __("Report {0}" , [__(target)]), value: __("Report {0}" , [__(target)]),
match: txt, match: txt,
index: index, index: index,
type: "Report",
prefix: "Report",
route: route route: route
}); });
} }
@@ -446,16 +541,19 @@ frappe.search.AwesomeBar = Class.extend({
p.name = name; p.name = name;
}); });
Object.keys(this.pages).forEach(function(item) { Object.keys(this.pages).forEach(function(item) {
var target = me.is_present(txt, item);
var result = me.fuzzy_search(txt, item, 27);
var target = result[0];
var index = result[1];
var rendered_label = result[2];
if(target) { if(target) {
var match_ratio = txt.length / item.length;
var index = (match_ratio > 0.7) ? 10 : 14;
var page = me.pages[target]; var page = me.pages[target];
out.push({ out.push({
label: __("Open {0}", [__(target).bold()]),
label: rendered_label,
value: __("Open {0}", [__(target)]), value: __("Open {0}", [__(target)]),
match: txt, match: txt,
index: index, index: index,
type: "Page",
prefix: "Open",
route: [page.route || page.name] route: [page.route || page.name]
}); });
} }
@@ -464,10 +562,12 @@ frappe.search.AwesomeBar = Class.extend({
var target = 'Calendar'; var target = 'Calendar';
if(__('calendar').indexOf(txt.toLowerCase()) === 0) { if(__('calendar').indexOf(txt.toLowerCase()) === 0) {
out.push({ out.push({
label: __("Open {0}", [__(target).bold()]),
label: rendered_label,
value: __("Open {0}", [__(target)]), value: __("Open {0}", [__(target)]),
route: [target, 'Event'], route: [target, 'Event'],
index: 14,
index: 27,
type: "Calendar",
prefix: "Open",
match: target match: target
}); });
} }
@@ -478,17 +578,20 @@ frappe.search.AwesomeBar = Class.extend({
var me = this; var me = this;
var out = []; var out = [];
Object.keys(frappe.modules).forEach(function(item) { Object.keys(frappe.modules).forEach(function(item) {
var target = me.is_present(txt, item);
var result = me.fuzzy_search(txt, item, 28);
var target = result[0];
var index = result[1];
var rendered_label = result[2];
if(target) { if(target) {
var match_ratio = txt.length / item.length;
var index = (match_ratio > 0.7) ? 10 : 15;
var module = frappe.modules[target]; var module = frappe.modules[target];
if(module._doctype) return; if(module._doctype) return;
ret = { ret = {
label: __("Open {0}", [__(target).bold()]),
label: rendered_label,
value: __("Open {0}", [__(target)]), value: __("Open {0}", [__(target)]),
match: txt, match: txt,
index: index
index: index,
type: "Module",
prefix: "Open"
} }
if(module.link) { if(module.link) {
ret.route = [module.link]; ret.route = [module.link];


+ 2
- 0
frappe/public/js/frappe/ui/toolbar/search.html 查看文件

@@ -1,5 +1,7 @@
<div class="row"> <div class="row">
<div class="col-md-2 col-sm-2 hidden-xs layout-side-section search-sidebar"> <div class="col-md-2 col-sm-2 hidden-xs layout-side-section search-sidebar">
<ul class="module-sidebar-nav overlay-sidebar nav nav-pills nav-stacked search-sidelist">
</ul>
</div> </div>
<div class="col-md-10 col-sm-10 layout-main-section results-area"> <div class="col-md-10 col-sm-10 layout-main-section results-area">
</div> </div>


+ 139
- 50
frappe/public/js/frappe/ui/toolbar/search.js 查看文件

@@ -12,7 +12,7 @@ frappe.search.UnifiedSearch = Class.extend({
this.search_modal.addClass('search-dialog'); this.search_modal.addClass('search-dialog');


this.input = this.search_modal.find(".search-input"); this.input = this.search_modal.find(".search-input");
this.sidebar = this.search_modal.find(".search-sidebar");
this.sidelist = this.search_modal.find(".search-sidelist");
this.results_area = this.search_modal.find(".results-area"); this.results_area = this.search_modal.find(".results-area");
}, },


@@ -32,19 +32,19 @@ frappe.search.UnifiedSearch = Class.extend({
$this.data('timeout', setTimeout(function(){ $this.data('timeout', setTimeout(function(){
var keywords = me.input.val(); var keywords = me.input.val();
me.reset(); me.reset();
if(keywords.length > 1) {
if(keywords.length > 2) {
me.build_results(keywords); me.build_results(keywords);
} else { } else {
me.current_type = ''; me.current_type = '';
} }
}, 250));
}, 600));
}); });
this.build_results(keywords); this.build_results(keywords);
setTimeout(function() { me.input.select(); }, 500); setTimeout(function() { me.input.select(); }, 500);
}, },


reset: function() { reset: function() {
this.sidebar.empty();
this.sidelist.empty();
this.results_area.empty(); this.results_area.empty();
}, },


@@ -59,7 +59,9 @@ frappe.search.UnifiedSearch = Class.extend({
render_results: function(results_obj, keywords){ render_results: function(results_obj, keywords){
var me = this; var me = this;
if(this.current === 0) { this.reset() } if(this.current === 0) { this.reset() }
this.sidebar.append(results_obj.sidelist);
results_obj.sidelist.forEach(function(list_item) {
me.sidelist.append(list_item);
})
this.results_area.find('.results-status').remove(); this.results_area.find('.results-status').remove();
results_obj.sections.forEach(function(section) { results_obj.sections.forEach(function(section) {
me.summary.append(section); me.summary.append(section);
@@ -70,18 +72,35 @@ frappe.search.UnifiedSearch = Class.extend({


bind_events: function() { bind_events: function() {
var me = this; var me = this;
this.sidebar.find('.list-link').on('click', function() {
me.set_sidebar_link_action($(this));
this.results_area.on('scroll', function() {
if(me.results_area.find('.all-results-link').length !== 0) {
return;
}
var r = me.results_area.find('.module-section')[1];
me.results_area.find('.module-section').each(function() {
if(($(this).position().top < 120) && ($(this).position().top + $(this).height() > 120)) {
var types = $(this).attr('data-type').split(',');
me.sidelist.find('.list-link').removeClass('active');
types.forEach(function(type) {
me.sidelist.find('*[data-category="'+ type +'"]').addClass('active');
});
}
});
});
this.sidelist.find('.list-link').on('click', function() {
me.set_sidelist_link_action($(this));
}); });
this.results_area.find('.section-more').on('click', function() { this.results_area.find('.section-more').on('click', function() {
var type = $(this).attr('data-category'); var type = $(this).attr('data-category');
me.sidebar.find('*[data-category="'+ type +'"]').trigger('click');
me.sidelist.find('*[data-category="'+ type +'"]').trigger('click');
}); });
}, },


set_sidebar_link_action: function(link) {
this.sidebar.find(".list-link a").removeClass("disabled");
link.find('a').addClass("disabled");
set_sidelist_link_action: function(link) {
this.sidelist.find(".list-link").removeClass("active");
this.sidelist.find(".list-link i").addClass("hide");
link.addClass("active");
link.find("i").removeClass("hide");
var type = link.attr('data-category'); var type = link.attr('data-category');
this.results_area.empty().html(this.full_lists[type]); this.results_area.empty().html(this.full_lists[type]);


@@ -101,6 +120,9 @@ frappe.search.UnifiedSearch = Class.extend({


show_summary: function() { show_summary: function() {
this.current_type = ''; this.current_type = '';
this.sidelist.find(".list-link i").addClass("hide");
this.sidelist.find(".list-link").removeClass("active");
this.sidelist.find(".list-link").first().addClass("active");
this.results_area.empty().html(this.summary); this.results_area.empty().html(this.summary);
this.bind_events(); this.bind_events();
}, },
@@ -125,7 +147,7 @@ frappe.search.UnifiedSearch = Class.extend({
var no_of_results = this.results_area.find('.result').length; var no_of_results = this.results_area.find('.result').length;
var no_of_results_cue = $('<p class="results-status text-muted small">'+ var no_of_results_cue = $('<p class="results-status text-muted small">'+
no_of_results +' results found</p>'); no_of_results +' results found</p>');
this.results_area.find(".result:last").append(no_of_results_cue);
this.results_area.find(".more-results:last").append(no_of_results_cue);
} }
this.results_area.find('.more-results.last').slideDown(200, function() { this.results_area.find('.more-results.last').slideDown(200, function() {
var height = me.results_area.find('.module-body').height() - 750; var height = me.results_area.find('.module-body').height() - 750;
@@ -144,20 +166,20 @@ frappe.search.UnifiedSearch = Class.extend({
// More searches to go // More searches to go
this.search_objects[this.current].build_results_object(this, keywords); this.search_objects[this.current].build_results_object(this, keywords);
} else { } else {
// If there's only one link in sidebar, there's no summary (show its full list)
if(this.sidebar.find('.list-link').length === 1) {
// If there's only one link in sidelist, there's no summary (show its full list)
if(this.sidelist.find('.list-link').length === 1) {
this.bind_events(); this.bind_events();
this.sidebar.find('.list-link').trigger('click');
this.sidelist.find('.list-link').trigger('click');
this.results_area.find('.all-results-link').hide(); this.results_area.find('.all-results-link').hide();


} else if (this.sidebar.find('.list-link').length === 0) {
} else if (this.sidelist.find('.list-link').length === 0) {
this.results_area.html('<p class="results-status text-muted" style="text-align: center;">'+ this.results_area.html('<p class="results-status text-muted" style="text-align: center;">'+
'No results found for: '+ "'"+ keywords +"'" +'</p>'); 'No results found for: '+ "'"+ keywords +"'" +'</p>');
} else { } else {
var list_types = this.get_all_list_types(); var list_types = this.get_all_list_types();
if(list_types.indexOf(this.current_type) >= 0) { if(list_types.indexOf(this.current_type) >= 0) {
this.bind_events(); this.bind_events();
this.sidebar.find('*[data-category="'+ this.current_type +'"]').trigger('click');
this.sidelist.find('*[data-category="'+ this.current_type +'"]').trigger('click');
} else { } else {
this.show_summary(); this.show_summary();
} }
@@ -167,7 +189,7 @@ frappe.search.UnifiedSearch = Class.extend({


get_all_list_types: function() { get_all_list_types: function() {
var types = []; var types = [];
this.sidebar.find('.list-link').each(function() {
this.sidelist.find('.list-link').each(function() {
types.push($(this).attr('data-category')); types.push($(this).attr('data-category'));
}); });
return types; return types;
@@ -214,18 +236,16 @@ frappe.search.GlobalSearch = Class.extend({


make_sidelist: function() { make_sidelist: function() {
var me = this; var me = this;
var sidelist = $('<ul class="list-unstyled sidebar-menu nav-list"></ul>');
sidelist.append('<li class="h6">'+ me.search_type +'</li>');
var sidelist = [];
this.types.forEach(function(type) { this.types.forEach(function(type) {
sidelist.append(me.make_sidelist_item(type));
sidelist.push(me.make_sidelist_item(type));
}); });
sidelist.append('<li class="divider"></li>');
return sidelist; return sidelist;
}, },


make_sidelist_item: function(type) { make_sidelist_item: function(type) {
var sidelist_item = '<li class="list-link" data-search="{0}"' +
'data-category="{1}"><a>{1}</a></li>';
var sidelist_item = '<li class="strong module-sidebar-item list-link" data-search="{0}"' +
'data-category="{1}"><a><span>{1}</span><i class="fa fa-chevron-right pull-right hide"></a></li>';
return $(__(sidelist_item, [this.search_type, type])); return $(__(sidelist_item, [this.search_type, type]));
}, },


@@ -319,7 +339,7 @@ frappe.search.GlobalSearch = Class.extend({


make_section: function(type, results) { make_section: function(type, results) {
var me = this; var me = this;
var results_section = $('<div class="row module-section '+type+'-section">'+
var results_section = $('<div class="row module-section" data-type="'+type+'">'+
'<div class="col-sm-12 module-section-column">' + '<div class="col-sm-12 module-section-column">' +
'<div class="h4 section-head">'+type+'</div>' + '<div class="h4 section-head">'+type+'</div>' +
'<div class="section-body"></div>'+ '<div class="section-body"></div>'+
@@ -329,27 +349,53 @@ frappe.search.GlobalSearch = Class.extend({
results_col.append(me.make_result_item(type, result)); results_col.append(me.make_result_item(type, result));
}); });
if(results.length > this.section_length) { if(results.length > this.section_length) {
results_col.append('<a class="small section-more" data-category="'
+ type + '">More...</a>');
results_col.append('<button class="btn btn-default btn-xs text-muted section-more" data-category="'
+ type + '" style="margin-top:10px">More...</button>');

} }
return results_section; return results_section;
}, },


make_result_subtypes_property: function(results) {
var compressed_results = [];
var labels = [];
results.forEach(function(r) {
if(labels.indexOf(r.label) === -1) {
labels.push(r.label);
}
});
labels.forEach(function(l) {
var item_group = {
title: l,
subtypes: []
};
results.forEach(function(r) {
if (r.label === l) {
item_group.subtypes.push(r);
}
});
compressed_results.push(item_group);
});
return compressed_results;
},

make_full_list: function(type, results, more) { make_full_list: function(type, results, more) {
var me = this; var me = this;
var results_list = $(' <div class="module-body"><div class="row module-section '+
var results_list = $(' <div class="module-body"><div class="row module-section full-list '+
type+'-section">'+'<div class="col-sm-12 module-section-column">' + type+'-section">'+'<div class="col-sm-12 module-section-column">' +
'<div class="back-link"><a class="all-results-link small"> All Results</a></div>' + '<div class="back-link"><a class="all-results-link small"> All Results</a></div>' +
'<div class="h4 section-head">'+type+'</div>' + '<div class="h4 section-head">'+type+'</div>' +
'<div class="section-body"></div>'+
'</div></div></div>');
'<div class="section-body"></div></div></div></div>');
if(type === "Lists") {
results = this.make_result_subtypes_property(results);
}
var results_col = results_list.find('.module-section-column'); var results_col = results_list.find('.module-section-column');
results.forEach(function(result) { results.forEach(function(result) {
results_col.append(me.make_result_item(type, result)); results_col.append(me.make_result_item(type, result));
}); });
if(more) { if(more) {
results_col.append('<a class="small list-more" data-search="'+ this.search_type +'" data-category="'
+ type + '">More...</a>');
results_col.append('<button class="btn btn-default btn-xs text-muted list-more" data-search="'+
this.search_type +'" data-category="'+ type + '" style="margin-top:10px"> More...</button>');
} }
return results_list; return results_list;
}, },
@@ -386,7 +432,6 @@ frappe.search.GlobalSearch = Class.extend({
}, },


get_awesome_bar_options: function(keywords, ref) { get_awesome_bar_options: function(keywords, ref) {

var me = this; var me = this;
var doctypes = []; var doctypes = [];
var current = 0; var current = 0;
@@ -426,7 +471,7 @@ frappe.search.GlobalSearch = Class.extend({
if(current < doctypes.length) { if(current < doctypes.length) {
get_results(); get_results();
} else { } else {
ref.set_global_results(results);
ref.set_global_results(results, keywords);
} }
} }
} }
@@ -434,13 +479,13 @@ frappe.search.GlobalSearch = Class.extend({
}; };


var make_option = function(data) { var make_option = function(data) {
console.log("GS search", me.get_finds(data.content, keywords).slice(0,86) + '...');
return { return {
label: __("{0}: {1}", [__(data.doctype).bold(), data.name]), label: __("{0}: {1}", [__(data.doctype).bold(), data.name]),
value: __("{0}: {1}", [__(data.doctype), data.name]), value: __("{0}: {1}", [__(data.doctype), data.name]),
route: ["Form", data.doctype, data.name], route: ["Form", data.doctype, data.name],
match: data.doctype, match: data.doctype,
index: 5,
index: 41,
default: "Global",
description: me.get_finds(data.content, keywords).slice(0,86) + '...' description: me.get_finds(data.content, keywords).slice(0,86) + '...'
} }
}; };
@@ -455,7 +500,6 @@ frappe.search.NavSearch = frappe.search.GlobalSearch.extend({
init: function() { init: function() {
this.search_type = 'Navigation'; this.search_type = 'Navigation';
}, },

set_types: function() { set_types: function() {
var me = this; var me = this;
this.section_length = 4; this.section_length = 4;
@@ -520,9 +564,47 @@ frappe.search.NavSearch = frappe.search.GlobalSearch.extend({
}, },


make_result_item: function(type, result) { make_result_item: function(type, result) {
var link_html = '<div class="result '+ type +'-result single-link">' +
'<a href="{0}" class="module-section-link small">{1}</a>' +
'<p class="small"></p></div>';
if(!result.subtypes) {
var link_html = '<div class="result '+ type +'-result single-link">' +
'<a href="{0}" class="module-section-link small">{1}</a>' +
'<p class="small"></p></div>';
return this.make_result_link(type, result, link_html);

} else {
var result_div = $('<div class="result '+ type +'-result single-link"></div>');
var button_html = '<button class="btn btn-default btn-xs text-muted result-subtype"'+
'>{0}</button>'
result.subtypes.forEach(function(s) {
if(["Gantt", "Report", "Calendar"].indexOf(s.type) !== -1) {
var button = $(__(button_html, [s.type]));
button.on('click', function() {
if(s.route_options) {
frappe.route_options = s.route_options;
}
frappe.set_route(s.route);
return false;
});
result_div.append(button);
} else {
title_link_html = '<a href="{0}" class="module-section-link small">{1}</a>';
var link = $(__(title_link_html, ['#', result.title + ' ' + s.type]));
link.on('click', function() {
if(s.route_options) {
frappe.route_options = s.route_options;
}
frappe.set_route(s.route);
return false;
});
result_div.append(link);
}
})
result_div.append($('<p class="small"></p>'));
return result_div;
}

},

make_result_link: function(type, result, link_html) {
if(!result.onclick) { if(!result.onclick) {
var link = $(__(link_html, ['#', result.label])); var link = $(__(link_html, ['#', result.label]));
link.on('click', function() { link.on('click', function() {
@@ -545,9 +627,14 @@ frappe.search.NavSearch = frappe.search.GlobalSearch.extend({


make_dual_sections: function() { make_dual_sections: function() {
this.dual_sections = []; this.dual_sections = [];
var section_html = '<div class="row module-section dual-section"></div>';
for(var i = 0; i < this.sections.length; i++) { for(var i = 0; i < this.sections.length; i++) {
var results_section = $(section_html);
var types;
if(i+1 < this.types.length) {
types = this.types[i] + ',' + this.types[i+1];
} else {
types = this.types[i];
}
var results_section = $('<div class="row module-section dual-section" data-type="'+ types +'"></div>');
for(var j = 0; j < 2 && i < this.sections.length; j++, i++){ for(var j = 0; j < 2 && i < this.sections.length; j++, i++){
results_section.append(this.sections[i]); results_section.append(this.sections[i]);
} }
@@ -574,12 +661,15 @@ frappe.search.NavSearch = frappe.search.GlobalSearch.extend({
'<div class="h4 section-head">'+type+'</div>' + '<div class="h4 section-head">'+type+'</div>' +
'<div class="section-body"></div>'+ '<div class="section-body"></div>'+
'</div>'); '</div>');
if(type === "Lists") {
results = this.make_result_subtypes_property(results);
}
results.slice(0, this.section_length).forEach(function(result) { results.slice(0, this.section_length).forEach(function(result) {
results_column.append(me.make_result_item(type, result)); results_column.append(me.make_result_item(type, result));
}); });
if(results.length > this.section_length) { if(results.length > this.section_length) {
results_column.append('<a class="small section-more" data-category="'
+ type + '">More...</a>');
results_column.append('<button class="btn btn-default btn-xs text-muted section-more" data-category="'
+ type + '" style="margin-top:10px">More...</button>');
} }
return results_column; return results_column;
} }
@@ -597,12 +687,11 @@ frappe.search.HelpSearch = frappe.search.GlobalSearch.extend({
}, },


make_sidelist: function() { make_sidelist: function() {
var sidelist = $('<ul class="list-unstyled sidebar-menu nav-list"></ul>');
var sidelist_item = '<li class="h6 list-link" data-search="'+ this.search_type + '"' +
'data-category="'+ this.search_type + '"><a style="font-size: 11px;">'+
this.search_type +'</a></li>';
sidelist.append(sidelist_item);
sidelist.append('<li class="divider"></li>');
var sidelist = [];
var sidelist_item = '<li class="strong module-sidebar-item list-link help-link" '+
'data-search="'+ this.search_type + '" data-category="'+ this.search_type + '"><a><span>'+
this.search_type +'</span><i class="fa fa-chevron-right pull-right hide"></a></li>';
sidelist.push(sidelist_item);
return sidelist; return sidelist;
}, },




+ 38
- 8
frappe/public/less/desk.less 查看文件

@@ -563,12 +563,26 @@ textarea.form-control {
} }


.layout-side-section { .layout-side-section {
padding-left: 15px;
.help-link {
padding-top: 20px;
text-transform: uppercase;
}

.nav > li > a {
padding-left: 20px;
}
}

.results-area {
a {
color: #5E64FF;
}

.single-link a {
color: #36414c;
}
} }


// .results-area a {
// color: #5E64FF;
// }
.module-section { .module-section {
.back-link { .back-link {
margin-bottom: 20px; margin-bottom: 20px;
@@ -580,13 +594,29 @@ textarea.form-control {
content: '\f0a4'; content: '\f0a4';
} }
} }
.dual-section .result a {
color: black;

.full-list {
.result {
border-bottom: 1px solid #d1d8dd;
margin-top: 10px;

.result-subtype{
float: right;
margin-left: 10px;
}
}

.section-head {
margin-bottom: 25px;
}
} }


.dual-section .result a b{
color: #4e6161;
.dual-section {
.result-subtype{
display: none;
}
} }

.result-status { .result-status {
margin-top: 30px; margin-top: 30px;
text-align: center; text-align: center;


+ 2
- 1
frappe/utils/global_search.py 查看文件

@@ -148,4 +148,5 @@ def search_in_doctype(doctype, text, start, limit):
doctype = %s AND doctype = %s AND
match(content) against (%s IN BOOLEAN MODE) match(content) against (%s IN BOOLEAN MODE)
limit {start}, {limit}'''.format(start=start, limit=limit), (doctype, text), as_dict=True) limit {start}, {limit}'''.format(start=start, limit=limit), (doctype, text), as_dict=True)
return results

return results

正在加载...
取消
保存