mirror of
https://github.com/go-shiori/shiori.git
synced 2025-09-08 22:15:58 +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) {
|
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
|
// Check token
|
||||||
err := checkAPIToken(r)
|
err := checkAPIToken(r)
|
||||||
checkError(err)
|
checkError(err)
|
||||||
|
|
||||||
// Fetch all bookmarks
|
// Fetch all bookmarks
|
||||||
bookmarks, err := DB.GetBookmarks(false)
|
bookmarks, err := DB.SearchBookmarks(keyword, tags...)
|
||||||
checkError(err)
|
checkError(err)
|
||||||
|
|
||||||
err = json.NewEncoder(w).Encode(&bookmarks)
|
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="/">
|
<a id="logo" href="/">
|
||||||
<span>栞</span>shiori</a>
|
<span>栞</span>shiori</a>
|
||||||
<div id="search-box">
|
<div id="search-box">
|
||||||
<input type="text" name="keyword" id="input-search" placeholder="Search tags, title or content">
|
<input type="text" name="keyword" v-model.trim="search.query" @keyup.enter="loadData" placeholder="Search url, tags, title or content">
|
||||||
<a class="button">
|
<a class="button" @click="loadData">
|
||||||
<i class="fas fa-search fa-fw"></i>
|
<i class="fas fa-search fa-fw"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div id="header-menu" v-if="!loading">
|
<div id="header-menu" v-if="!loading">
|
||||||
<a @click="loadData">
|
<a @click="reloadData">
|
||||||
<i class="fas fa-cloud fa-fw"></i> Reload
|
<i class="fas fa-cloud fa-fw"></i> Reload
|
||||||
</a>
|
</a>
|
||||||
<a @click="toggleImage">
|
<a @click="toggleImage">
|
||||||
|
@ -75,6 +75,10 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</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 id="grid">
|
||||||
<div v-for="column in gridColumns" class="column">
|
<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">
|
<div v-for="item in column" class="bookmark" :class="{checked: isBookmarkChecked(item.index)}" :ref="'bookmark-'+item.index">
|
||||||
|
@ -142,10 +146,15 @@
|
||||||
data: {
|
data: {
|
||||||
windowWidth: 0,
|
windowWidth: 0,
|
||||||
error: "",
|
error: "",
|
||||||
loading: true,
|
loading: false,
|
||||||
bookmarks: [],
|
bookmarks: [],
|
||||||
checkedBookmarks: [],
|
checkedBookmarks: [],
|
||||||
showImage: true,
|
showImage: true,
|
||||||
|
search: {
|
||||||
|
query: "",
|
||||||
|
keyword: "",
|
||||||
|
tags: []
|
||||||
|
},
|
||||||
inputBookmark: {
|
inputBookmark: {
|
||||||
index: -1,
|
index: -1,
|
||||||
id: -1,
|
id: -1,
|
||||||
|
@ -169,10 +178,40 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
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 () {
|
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.error = '';
|
||||||
this.loading = true;
|
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) {
|
.then(function (response) {
|
||||||
app.loading = false;
|
app.loading = false;
|
||||||
app.bookmarks = response.data;
|
app.bookmarks = response.data;
|
||||||
|
@ -460,7 +499,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
return finalContent;
|
return finalContent;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
'inputBookmark.url': function (newURL) {
|
'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 {
|
#grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row;
|
flex-flow: row;
|
||||||
|
@ -299,6 +318,8 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column nowrap;
|
flex-flow: column nowrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
height: auto;
|
||||||
|
min-height: 100vh;
|
||||||
a {
|
a {
|
||||||
color: @main;
|
color: @main;
|
||||||
&:visited {
|
&:visited {
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="input-field">
|
<div class="input-field">
|
||||||
<p>Password: </p>
|
<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>
|
||||||
<div class="input-field">
|
<div class="input-field">
|
||||||
<a @click="toggleRemember">
|
<a @click="toggleRemember">
|
||||||
|
|
Loading…
Add table
Reference in a new issue