mirror of
https://github.com/go-shiori/shiori.git
synced 2025-09-07 13:35:22 +08:00
Implement search on web interface
This commit is contained in:
parent
0a4850eacb
commit
529b089fd9
5 changed files with 74 additions and 9 deletions
|
@ -153,12 +153,17 @@ func apiLogin(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
|||
}
|
||||
|
||||
func apiGetBookmarks(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
// Get query parameter
|
||||
keyword := r.URL.Query().Get("keyword")
|
||||
strTags := r.URL.Query().Get("tags")
|
||||
tags := strings.Fields(strTags)
|
||||
|
||||
// Check token
|
||||
err := checkAPIToken(r)
|
||||
checkError(err)
|
||||
|
||||
// Fetch all bookmarks
|
||||
bookmarks, err := DB.GetBookmarks(false)
|
||||
bookmarks, err := DB.SearchBookmarks(keyword, tags...)
|
||||
checkError(err)
|
||||
|
||||
err = json.NewEncoder(w).Encode(&bookmarks)
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -21,13 +21,13 @@
|
|||
<a id="logo" href="/">
|
||||
<span>栞</span>shiori</a>
|
||||
<div id="search-box">
|
||||
<input type="text" name="keyword" id="input-search" placeholder="Search tags, title or content">
|
||||
<a class="button">
|
||||
<input type="text" name="keyword" v-model.trim="search.query" @keyup.enter="loadData" placeholder="Search url, tags, title or content">
|
||||
<a class="button" @click="loadData">
|
||||
<i class="fas fa-search fa-fw"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div id="header-menu" v-if="!loading">
|
||||
<a @click="loadData">
|
||||
<a @click="reloadData">
|
||||
<i class="fas fa-cloud fa-fw"></i> Reload
|
||||
</a>
|
||||
<a @click="toggleImage">
|
||||
|
@ -75,6 +75,10 @@
|
|||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="search.query !== '' && !loading" id="search-parameter">
|
||||
<a v-if="search.keyword !== ''" @click="removeSearchParam(search.keyword)">{{search.keyword}}</a>
|
||||
<a v-for="tag in search.tags" @click="removeSearchParam('#'+tag)">#{{tag}}</a>
|
||||
</div>
|
||||
<div id="grid">
|
||||
<div v-for="column in gridColumns" class="column">
|
||||
<div v-for="item in column" class="bookmark" :class="{checked: isBookmarkChecked(item.index)}" :ref="'bookmark-'+item.index">
|
||||
|
@ -142,10 +146,15 @@
|
|||
data: {
|
||||
windowWidth: 0,
|
||||
error: "",
|
||||
loading: true,
|
||||
loading: false,
|
||||
bookmarks: [],
|
||||
checkedBookmarks: [],
|
||||
showImage: true,
|
||||
search: {
|
||||
query: "",
|
||||
keyword: "",
|
||||
tags: []
|
||||
},
|
||||
inputBookmark: {
|
||||
index: -1,
|
||||
id: -1,
|
||||
|
@ -169,10 +178,40 @@
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
removeSearchParam: function (param) {
|
||||
if (this.loading) return;
|
||||
this.search.query = this.search.query.replace(param, ' ').trim().replace(/\s+/g, ' ');
|
||||
this.loadData();
|
||||
},
|
||||
reloadData: function () {
|
||||
if (this.loading) return;
|
||||
this.search.query = '';
|
||||
this.loadData();
|
||||
},
|
||||
loadData: function () {
|
||||
if (this.loading) return;
|
||||
|
||||
// Parse search query
|
||||
var rxTags = /(^|\s+)#(\S+)/g,
|
||||
tags = [];
|
||||
|
||||
while ((result = rxTags.exec(this.search.query)) !== null) {
|
||||
tags.push(result[2]);
|
||||
}
|
||||
|
||||
var keyword = this.search.query.replace(/(^|\s+)#(\S+)/g, ' ').trim().replace(/\s+/g, ' ');
|
||||
|
||||
// Fetch data
|
||||
this.error = '';
|
||||
this.loading = true;
|
||||
instance.get('/api/bookmarks')
|
||||
this.search.tags = tags;
|
||||
this.search.keyword = keyword;
|
||||
instance.get('/api/bookmarks', {
|
||||
params: {
|
||||
keyword: this.search.keyword,
|
||||
tags: this.search.tags.join(" ")
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
app.loading = false;
|
||||
app.bookmarks = response.data;
|
||||
|
@ -460,7 +499,7 @@
|
|||
}
|
||||
|
||||
return finalContent;
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'inputBookmark.url': function (newURL) {
|
||||
|
|
|
@ -263,6 +263,25 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
#search-parameter {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
padding: 0 8px;
|
||||
a {
|
||||
display: block;
|
||||
margin: 8px;
|
||||
padding: 8px;
|
||||
font-size: 0.9em;
|
||||
background-color: @fontLightColor;
|
||||
color: white;
|
||||
border-radius: 16px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: @main;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
}
|
||||
#grid {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
|
@ -299,6 +318,8 @@
|
|||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
align-items: center;
|
||||
height: auto;
|
||||
min-height: 100vh;
|
||||
a {
|
||||
color: @main;
|
||||
&:visited {
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
</div>
|
||||
<div class="input-field">
|
||||
<p>Password: </p>
|
||||
<input type="password" name="password" v-model.trim="password" placeholder="Password">
|
||||
<input type="password" name="password" v-model.trim="password" placeholder="Password" @keyup.enter="login">
|
||||
</div>
|
||||
<div class="input-field">
|
||||
<a @click="toggleRemember">
|
||||
|
|
Loading…
Add table
Reference in a new issue