💩 搜索功能

💩 搜索功能
This commit is contained in:
ronger 2021-05-30 12:18:59 +08:00 committed by GitHub
commit 0be1ae5a84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 267 additions and 88 deletions

View File

@ -10,7 +10,7 @@
<span v-if="isPerfect(article.articlePerfect)" style="color: gold;" title="优选"> <span v-if="isPerfect(article.articlePerfect)" style="color: gold;" title="优选">
<font-awesome-icon :icon="['fas', 'medal']"></font-awesome-icon> <font-awesome-icon :icon="['fas', 'medal']"></font-awesome-icon>
</span> </span>
{{ article.articleTitle }} <span v-html="article.articleTitle"></span>
</h4> </h4>
</el-link> </el-link>
<el-tag <el-tag
@ -21,7 +21,7 @@
effect="plain"> effect="plain">
# {{ tag.tagTitle }} # {{ tag.tagTitle }}
</el-tag> </el-tag>
<div class="text-muted article-summary-md">{{ article.articlePreviewContent }}</div> <div class="text-muted article-summary-md" v-html="article.articlePreviewContent"></div>
<el-row class="pt-5"> <el-row class="pt-5">
<el-col :xs="3" :sm="1" :xl="1" class="mr-3"> <el-col :xs="3" :sm="1" :xl="1" class="mr-3">
<el-avatar v-if="article.articleAuthorAvatarUrl" size="medium" <el-avatar v-if="article.articleAuthorAvatarUrl" size="medium"

View File

@ -10,11 +10,11 @@
<div style="padding: 0 10px;"> <div style="padding: 0 10px;">
<h4 class="article-header-md"> <h4 class="article-header-md">
<el-link rel="nofollow" @click="onRouter('portfolio',portfolio.idPortfolio)" :underline="false"> <el-link rel="nofollow" @click="onRouter('portfolio',portfolio.idPortfolio)" :underline="false">
{{ portfolio.portfolioTitle }} <span v-html="portfolio.portfolioTitle"></span>
</el-link> </el-link>
</h4> </h4>
</div> </div>
<div class="text-muted article-summary-md">{{ portfolio.portfolioDescription }}</div> <div class="text-muted article-summary-md" v-html="portfolio.portfolioDescription"></div>
</el-card> </el-card>
</el-col> </el-col>
<el-col v-else style="margin-bottom: 20px;"> <el-col v-else style="margin-bottom: 20px;">

View File

@ -8,9 +8,9 @@
<el-col :span="21"> <el-col :span="21">
<div> <div>
<el-link rel="nofollow" @click="onRouter(user.account)" :underline="false" class="text-default"> <el-link rel="nofollow" @click="onRouter(user.account)" :underline="false" class="text-default">
{{ user.nickname }} <span v-html="user.nickname"></span>
</el-link> </el-link>
<small class="d-block text-muted">{{ user.signature }}</small> <small class="d-block text-muted" v-html="user.signature"></small>
</div> </div>
</el-col> </el-col>
</el-col> </el-col>

View File

@ -18,34 +18,21 @@
</el-col> </el-col>
</el-row> </el-row>
</el-col> </el-col>
<el-col :xs="10" :sm="6" :md="6" :xl="6" style="padding-top: 1rem;text-align: right;"> <el-col :xs="16" :sm="12" :md="12" :xl="9" style="padding-top: 1rem;">
<el-autocomplete
v-model="queryString"
size="small"
value-key="label"
:fetch-suggestions="querySearchAsync"
placeholder="搜索帖子、作品集和用户"
:trigger-on-focus="false"
@select="handleSelect"
style="width: 80%;"
popper-class="search-result-box"
>
<template slot-scope="{ item }">
<el-col>
<span class="search-result-type">
<small class="text-muted" v-html="getSearchResultType(item.type)"></small>
</span>
<span>{{ item.label }}</span>
</el-col>
</template>
<!-- <template slot="append">-->
<!-- <el-button size="small" icon="el-icon-search" @click="search"></el-button>-->
<!-- </template>-->
</el-autocomplete>
</el-col>
<el-col :xs="6" :sm="6" :md="6" :xl="3" style="padding-top: 1rem;">
<client-only> <client-only>
<el-col v-if="user" style="text-align: right;"> <el-col v-if="user" style="text-align: right;">
<el-popover
placement="bottom"
width="400"
trigger="click"
v-model="showPopover"
@show="handleShowPopover">
<el-input id="searchInput" v-model="queryString" @keyup.enter.native="querySearchAsync" placeholder="搜索文章,作品集,用户"
:autofocus="autofocus">
<el-button slot="append" icon="el-icon-search" @click="querySearchAsync"></el-button>
</el-input>
<el-button slot="reference" icon="el-icon-search" circle size="small"></el-button>
</el-popover>
<el-link rel="nofollow" :underline="false" style="padding-left: 10px;padding-right: 10px;" <el-link rel="nofollow" :underline="false" style="padding-left: 10px;padding-right: 10px;"
href="/portfolio/post">创建作品集 href="/portfolio/post">创建作品集
</el-link> </el-link>
@ -91,6 +78,17 @@
</el-link> </el-link>
</el-col> </el-col>
<el-col v-else style="text-align: right;"> <el-col v-else style="text-align: right;">
<el-popover
placement="bottom"
width="400"
trigger="click"
v-model="showPopover">
<el-input v-model="queryString" @keyup.enter.native="querySearchAsync" placeholder="搜索文章,作品集,用户"
autofocus>
<el-button slot="append" icon="el-icon-search" @click="querySearchAsync"></el-button>
</el-input>
<el-button slot="reference" icon="el-icon-search" circle size="small"></el-button>
</el-popover>
<nuxt-link to="/login"> <nuxt-link to="/login">
<el-link rel="nofollow" :underline="false" style="margin-left: 10px;">登录</el-link> <el-link rel="nofollow" :underline="false" style="margin-left: 10px;">登录</el-link>
</nuxt-link> </nuxt-link>
@ -115,8 +113,7 @@ export default {
computed: { computed: {
...mapState({ ...mapState({
activeMenu: state => state.activeMenu, activeMenu: state => state.activeMenu,
user: state => state.oauth, user: state => state.oauth
initialSearchData: state => state.search.list
}), }),
avatarURL() { avatarURL() {
let _ts = this; let _ts = this;
@ -164,7 +161,9 @@ export default {
timeout: null, timeout: null,
show: false, show: false,
notifications: [], notifications: [],
notificationNumbers: "" notificationNumbers: "",
showPopover: false,
autofocus: false
}; };
}, },
watch: { watch: {
@ -173,21 +172,17 @@ export default {
} }
}, },
methods: { methods: {
querySearchAsync(queryString, cb) { querySearchAsync() {
let initialSearchData = this.initialSearchData; this.$router.push({
let results = queryString ? initialSearchData.filter(this.createStateFilter(queryString)) : initialSearchData; path: `/search?q=${this.queryString}`
})
clearTimeout(this.timeout); this.$set(this, 'showPopover', false);
this.timeout = setTimeout(() => { this.$set(this, 'queryString', '');
cb(results);
}, 3000 * Math.random());
}, },
createStateFilter(queryString) { handleShowPopover() {
return (state) => { setTimeout(function () {
if (state && state.label) { document.getElementById("searchInput").focus()
return (state.label.toLowerCase().indexOf(queryString.toLowerCase()) > -1); }, 500);
}
};
}, },
handleSelectMenu(item) { handleSelectMenu(item) {
let _ts = this; let _ts = this;
@ -217,15 +212,6 @@ export default {
} }
} }
}, },
handleSelect(item) {
console.log(item);
let _ts = this;
if (item) {
_ts.$router.push({
path: `/${item.type}/${item.value}`
})
}
},
handleCommand(item) { handleCommand(item) {
let _ts = this; let _ts = this;
switch (item) { switch (item) {
@ -260,23 +246,6 @@ export default {
} }
}) })
} }
},
search() {
console.log(this.queryString)
},
getSearchResultType(type) {
switch (type) {
case 'article':
type = '文章';
break;
case 'portfolio':
type = '作品集';
break;
case 'user':
type = '用户';
break;
}
return type;
} }
}, },
mounted() { mounted() {

105
pages/search.vue Normal file
View File

@ -0,0 +1,105 @@
<template>
<el-row class="wrapper">
<el-col>
<el-input v-model="q" @keyup.enter.native="search" placeholder="搜索文章,作品集,用户" :clearable="true">
<el-button slot="append" icon="el-icon-search" @click="search"></el-button>
</el-input>
</el-col>
<el-col>
<el-tabs v-model="activeName">
<el-tab-pane :label="'文章 (' + getLength(articles.pagination) + ')'" name="articles">
<article-list :articles="articles" @currentChange="currentChangeArticle"/>
</el-tab-pane>
<el-tab-pane :label="'用户 (' + getLength(users.pagination) + ')'" name="users">
<user-list :users="users" @currentChange="currentChangeUser"/>
</el-tab-pane>
<el-tab-pane :label="'作品集 (' + getLength(portfolios.pagination) + ')'" name="portfolios">
<portfolio-list :portfolios="portfolios" @currentChange="currentChangePortfolio"/>
</el-tab-pane>
</el-tabs>
</el-col>
</el-row>
</template>
<script>
import {mapState} from "vuex";
import ArticleList from "../components/archive/list";
import UserList from "../components/common/user/list";
import PortfolioList from "../components/common/portfolio/list";
export default {
name: "search",
fetch({store, query, error}) {
return Promise.all([
store
.dispatch('search/fetchArticleList', {queryString: query.q})
.catch(err => error({statusCode: 404})),
store
.dispatch('search/fetchUserList', {queryString: query.q})
.catch(err => error({statusCode: 404})),
store
.dispatch('search/fetchPortfolioList', {queryString: query.q})
.catch(err => error({statusCode: 404}))
])
},
watch: {
'$route.query': function () {
let _ts = this;
let queryString = _ts.$route.query.q;
_ts.$set(_ts, "q", queryString);
this.$store.dispatch('search/fetchArticleList', {queryString: queryString})
this.$store.dispatch('search/fetchUserList', {queryString: queryString})
this.$store.dispatch('search/fetchPortfolioList', {queryString: queryString})
}
},
components: {PortfolioList, UserList, ArticleList},
computed: {
...mapState({
articles: state => state.search.articles.data,
users: state => state.search.users.data,
portfolios: state => state.search.portfolios.data
})
},
data() {
return {
q: '',
activeName: 'articles'
}
},
methods: {
search() {
let _ts = this
if (_ts.q) {
_ts.$router.push({
path: `/search?q=${_ts.q}`
})
}
},
currentChangeArticle(page) {
this.$store.dispatch("search/fetchArticleList", {queryString: this.q, page: page});
},
currentChangeUser(page) {
this.$store.dispatch("search/fetchUserList", {queryString: this.q, page: page});
},
currentChangePortfolio(page) {
this.$store.dispatch("search/fetchPortfolioList", {queryString: this.q, page: page});
},
getLength(pagination) {
if (pagination.total) {
return pagination.total;
}
return 0;
}
},
mounted() {
let _ts = this;
let queryString = _ts.$route.query.q;
_ts.$set(_ts, "q", queryString);
_ts.$store.commit('setActiveMenu', 'search')
}
}
</script>
<style scoped>
</style>

View File

@ -76,9 +76,7 @@ export const actions = {
const initFetchAppData = [ const initFetchAppData = [
// 内容数据 // 内容数据
store.dispatch('topic/fetchNavList'), store.dispatch('topic/fetchNavList'),
store.dispatch('article/fetchList'), store.dispatch('article/fetchList')
store.dispatch('article/fetchAnnouncementList'),
store.dispatch('search/fetchList'),
] ]
return Promise.all(initFetchAppData) return Promise.all(initFetchAppData)

View File

@ -1,9 +1,39 @@
export const BASE_API_PATH = '/api/console'; export const BASE_API_PATH = '/api/lucene';
const getDefaultArticleListData = () => {
return {
articles: [],
pagination: {}
}
}
const getDefaultUserListData = () => {
return {
users: [],
pagination: {}
}
}
const getDefaultPortfolioListData = () => {
return {
portfolios: [],
pagination: {}
}
}
export const state = () => { export const state = () => {
return { return {
fetching: false, fetching: false,
list: [] list: [],
articles: {
fetching: false,
data: getDefaultArticleListData()
},
users: {
fetching: false,
data: getDefaultUserListData()
},
portfolios: {
fetching: false,
data: getDefaultPortfolioListData()
}
} }
} }
@ -14,25 +44,102 @@ export const mutations = {
}, },
updateListData(state, action) { updateListData(state, action) {
state.list = action state.list = action
} },
// 数据列表
updateArticleListFetching(state, action) {
state.articles.fetching = action
},
updateArticleListData(state, action) {
state.articles.data = action
},
// 数据列表
updateUserListFetching(state, action) {
state.users.fetching = action
},
updateUserListData(state, action) {
state.users.data = action
},
// 数据列表
updatePortfolioFetching(state, action) {
state.portfolios.fetching = action
},
updatePortfolioListData(state, action) {
state.portfolios.data = action
},
} }
export const actions = { export const actions = {
// 获取文章列表 // 获取文章列表
fetchList({commit}, params = {}) { fetchArticleList({commit}, params = {}) {
// 清空已有数据 // 清空已有数据
commit('updateListData', []); commit('updateArticleListData', getDefaultArticleListData());
commit('updateListFetching', true); if (!params.queryString) {
return false;
}
commit('updateArticleListFetching', true);
return this.$axios return this.$axios
.$get(`${BASE_API_PATH}/initial-search`) .$get(`${BASE_API_PATH}/search-article`, {
params: {
q: params.queryString,
page: params.page || 1
}
})
.then(response => { .then(response => {
commit('updateListFetching', false); commit('updateArticleListFetching', false);
commit('updateListData', response); commit('updateArticleListData', response ? response : getDefaultArticleListData());
}) })
.catch(error => { .catch(error => {
console.log(error); console.log(error);
commit('updateListFetching', false); commit('updateArticleListFetching', false);
});
},
fetchUserList({commit}, params = {}) {
// 清空已有数据
commit('updateUserListData', getDefaultUserListData());
if (!params.queryString) {
return false;
}
commit('updateUserListFetching', true);
return this.$axios
.$get(`${BASE_API_PATH}/search-user`, {
params: {
q: params.queryString,
page: params.page || 1
}
})
.then(response => {
commit('updateUserListFetching', false);
commit('updateUserListData', response ? response : getDefaultUserListData());
})
.catch(error => {
console.log(error);
commit('updateUserListFetching', false);
});
},
fetchPortfolioList({commit}, params = {}) {
// 清空已有数据
commit('updatePortfolioListData', getDefaultPortfolioListData());
if (!params.queryString) {
return false;
}
commit('updatePortfolioFetching', true);
return this.$axios
.$get(`${BASE_API_PATH}/search-portfolio`, {
params: {
q: params.queryString,
page: params.page || 1
}
})
.then(response => {
commit('updatePortfolioFetching', false);
commit('updatePortfolioListData', response ? response : getDefaultPortfolioListData());
})
.catch(error => {
console.log(error);
commit('updatePortfolioFetching', false);
}); });
} }
} }