mirror of https://gitee.com/xiaonuobase/snowy
【更新】优化菜单搜索功能,感谢CcSimple兄弟送来的PR
parent
887fa9d735
commit
362b538bae
|
@ -1,5 +1,4 @@
|
||||||
import { mapState, mapMutations } from 'vuex'
|
import { mapState, mapMutations } from 'vuex'
|
||||||
|
|
||||||
import hotkeys from 'hotkeys-js'
|
import hotkeys from 'hotkeys-js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -30,9 +29,7 @@ export default {
|
||||||
searchToggle: 'search/toggle',
|
searchToggle: 'search/toggle',
|
||||||
searchSet: 'search/set'
|
searchSet: 'search/set'
|
||||||
}),
|
}),
|
||||||
/**
|
// 接收点击搜索按钮
|
||||||
* 接收点击搜索按钮
|
|
||||||
*/
|
|
||||||
handleSearchClick() {
|
handleSearchClick() {
|
||||||
this.searchToggle()
|
this.searchToggle()
|
||||||
if (this.searchActive) {
|
if (this.searchActive) {
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
class="search-box"
|
class="search-box"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
allowClear
|
allowClear
|
||||||
placeholder="搜索页面(支持拼音检索)"
|
placeholder="搜索页面(支持拼音检索)"
|
||||||
size="large"
|
|
||||||
@change="querySearch"
|
@change="querySearch"
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
|
@ -15,7 +14,7 @@
|
||||||
</template>
|
</template>
|
||||||
</a-input>
|
</a-input>
|
||||||
<a-card
|
<a-card
|
||||||
:body-style="{ padding: '4px 0' }"
|
:body-style="{ padding: '0 0' }"
|
||||||
hoverable
|
hoverable
|
||||||
@mouseenter="onCardIn"
|
@mouseenter="onCardIn"
|
||||||
@mouseleave="onCardOut"
|
@mouseleave="onCardOut"
|
||||||
|
@ -23,13 +22,13 @@
|
||||||
@keypress.down="handleKeyDown"
|
@keypress.down="handleKeyDown"
|
||||||
style="margin: 10px 0"
|
style="margin: 10px 0"
|
||||||
>
|
>
|
||||||
<div ref="cardList" class="search-card beauty-scroll" style="">
|
<div ref="cardList" class="search-card beauty-scroll">
|
||||||
<a-list size="small" :data-source="resultsList">
|
<a-list size="small" :data-source="resultsList">
|
||||||
<template #renderItem="{ item, index }">
|
<template #renderItem="{ item, index }">
|
||||||
<a-list-item
|
<a-list-item
|
||||||
@click="handleSelect(item.fullPath)"
|
@click="handleSelect(item.fullPath)"
|
||||||
@mouseover="onCardItemHover(index)"
|
@mouseover="onCardItemHover(index)"
|
||||||
:class="{ active: index == cardIndex }"
|
:class="{ active: index === cardIndex }"
|
||||||
style="padding-right: 10px"
|
style="padding-right: 10px"
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
|
@ -42,7 +41,7 @@
|
||||||
<a>{{ item.name }}</a>
|
<a>{{ item.name }}</a>
|
||||||
</template>
|
</template>
|
||||||
<template #avatar>
|
<template #avatar>
|
||||||
<a-avatar style="color: black; background-color: transparent" :type="item.icon">
|
<a-avatar style="color: var(--text-color); background-color: transparent" :type="item.icon">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<component :is="item.icon" />
|
<component :is="item.icon" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -108,18 +107,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/**
|
// 过滤选项 这个方法在每次输入框的值发生变化时会触发
|
||||||
* @description 过滤选项 这个方法在每次输入框的值发生变化时会触发
|
|
||||||
*/
|
|
||||||
querySearch(e) {
|
querySearch(e) {
|
||||||
let queryString = e.target.value || ''
|
let queryString = e.target.value || ''
|
||||||
const results = queryString && this.fuse.search(queryString).map((e) => e.item)
|
const results = queryString && this.fuse.search(queryString).map((e) => e.item)
|
||||||
this.searchText = queryString
|
this.searchText = queryString
|
||||||
this.results = results
|
this.results = results
|
||||||
},
|
},
|
||||||
/**
|
// 聚焦输入框
|
||||||
* @description 聚焦输入框
|
|
||||||
*/
|
|
||||||
focus() {
|
focus() {
|
||||||
this.searchText = ''
|
this.searchText = ''
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -178,9 +173,7 @@
|
||||||
onCardItemHover(index) {
|
onCardItemHover(index) {
|
||||||
this.cardIndex = index
|
this.cardIndex = index
|
||||||
},
|
},
|
||||||
/**
|
// 接收用户在下拉菜单中选中事件
|
||||||
* @description 接收用户在下拉菜单中选中事件
|
|
||||||
*/
|
|
||||||
handleSelect(path) {
|
handleSelect(path) {
|
||||||
// 如果用户选择的就是当前页面 就直接关闭搜索面板
|
// 如果用户选择的就是当前页面 就直接关闭搜索面板
|
||||||
if (path === this.$route.path) {
|
if (path === this.$route.path) {
|
||||||
|
@ -190,26 +183,20 @@
|
||||||
this.$router.push({ path })
|
this.$router.push({ path })
|
||||||
this.handleEsc()
|
this.handleEsc()
|
||||||
},
|
},
|
||||||
/**
|
// 关闭输入框的下拉菜单
|
||||||
* @augments 关闭输入框的下拉菜单
|
|
||||||
*/
|
|
||||||
closeSuggestion() {
|
closeSuggestion() {
|
||||||
if (this.$refs.input.activated) {
|
if (this.$refs.input.activated) {
|
||||||
this.results = []
|
this.results = []
|
||||||
this.$refs.input.activated = false
|
this.$refs.input.activated = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
// 接收用户点击空白区域的关闭
|
||||||
* @augments 接收用户点击空白区域的关闭
|
|
||||||
*/
|
|
||||||
handlePanelClick(e) {
|
handlePanelClick(e) {
|
||||||
if ('INPUT' != e.target.tagName) {
|
if ('INPUT' !== e.target.tagName) {
|
||||||
this.handleEsc()
|
this.handleEsc()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
// 接收用户触发的关闭
|
||||||
* @augments 接收用户触发的关闭
|
|
||||||
*/
|
|
||||||
async handleEsc() {
|
async handleEsc() {
|
||||||
this.closeSuggestion()
|
this.closeSuggestion()
|
||||||
await this.$nextTick()
|
await this.$nextTick()
|
||||||
|
@ -221,21 +208,23 @@
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
/deep/ .ant-input {
|
/deep/ .ant-input {
|
||||||
height: 48px;
|
height: 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .ant-input:not(:first-child) {
|
/deep/ .ant-input:not(:first-child) {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .ant-input-prefix {
|
/deep/ .ant-input-prefix {
|
||||||
font-size: 24px;
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
/deep/ .ant-list-sm .ant-list-item {
|
||||||
|
padding: 4px 16px;
|
||||||
|
}
|
||||||
|
/deep/ .ant-list-item-meta {
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-box {
|
.search-box {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.beauty-scroll {
|
.beauty-scroll {
|
||||||
scrollbar-color: var(--primary-color) var(--primary-2);
|
scrollbar-color: var(--primary-color) var(--primary-2);
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
|
@ -255,31 +244,22 @@
|
||||||
background: var(--primary-3);
|
background: var(--primary-3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-card {
|
.search-card {
|
||||||
height: 220px;
|
height: 220px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
/deep/ .ant-list-item.active {
|
/deep/ .ant-list-item.active {
|
||||||
background-color: var(--primary-1);
|
background-color: var(--primary-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-tips {
|
.search-tips {
|
||||||
display: flex;
|
display: flex;
|
||||||
border-top: 1px solid #f0f0f0;
|
border-top: 1px solid var(--component-background);
|
||||||
padding-top: 6px;
|
padding-top: 10px;
|
||||||
|
|
||||||
.tips {
|
.tips {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.key {
|
.key {
|
||||||
//display: flex;
|
|
||||||
//flex-direction: row;
|
|
||||||
//align-items: center;
|
|
||||||
//justify-content: center;
|
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
|
@ -287,7 +267,6 @@
|
||||||
padding-bottom: 2px;
|
padding-bottom: 2px;
|
||||||
margin: 0px 4px;
|
margin: 0px 4px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background-color: white;
|
|
||||||
box-shadow: inset 0 -2px #cdcde6, inset 0 0 1px 1px #fff, 0 1px 2px 1px #1e235a66;
|
box-shadow: inset 0 -2px #cdcde6, inset 0 0 1px 1px #fff, 0 1px 2px 1px #1e235a66;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
.d2-panel-search-item__icon-box {
|
.d2-panel-search-item__icon-box {
|
||||||
i {
|
i {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
color: @primary-color;
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
style="overflow: hidden"
|
style="overflow: hidden"
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
dialogClass="searchModal"
|
dialogClass="searchModal"
|
||||||
:bodyStyle="{ maxHeight: '520px', overflow: 'auto', padding: '10px' }"
|
:bodyStyle="{ maxHeight: '520px', overflow: 'auto', padding: '14px' }"
|
||||||
@cancel="searchPanelClose"
|
@cancel="searchPanelClose"
|
||||||
>
|
>
|
||||||
<panel-search ref="panelSearch" @close="searchPanelClose" />
|
<panel-search ref="panelSearch" @close="searchPanelClose" />
|
||||||
|
|
|
@ -14,26 +14,15 @@ export default {
|
||||||
pool: []
|
pool: []
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
/**
|
// 切换激活状态
|
||||||
* @description 切换激活状态
|
|
||||||
* @param {Object} state state
|
|
||||||
*/
|
|
||||||
toggle(state) {
|
toggle(state) {
|
||||||
state.active = !state.active
|
state.active = !state.active
|
||||||
},
|
},
|
||||||
/**
|
// 设置激活模式
|
||||||
* @description 设置激活模式
|
|
||||||
* @param {Object} state state
|
|
||||||
* @param {Boolean} active active
|
|
||||||
*/
|
|
||||||
set(state, active) {
|
set(state, active) {
|
||||||
state.active = active
|
state.active = active
|
||||||
},
|
},
|
||||||
/**
|
// 初始化
|
||||||
* @description 初始化
|
|
||||||
* @param {Object} state state
|
|
||||||
* @param {Array} menu menu
|
|
||||||
*/
|
|
||||||
init(state, menu) {
|
init(state, menu) {
|
||||||
const pool = []
|
const pool = []
|
||||||
const getFullName = function (meta) {
|
const getFullName = function (meta) {
|
||||||
|
@ -48,10 +37,10 @@ export default {
|
||||||
}
|
}
|
||||||
const push = function (menu) {
|
const push = function (menu) {
|
||||||
menu.forEach((m) => {
|
menu.forEach((m) => {
|
||||||
if ('menu' == m.meta.type) {
|
if ('menu' === m.meta.type) {
|
||||||
if (m.children) {
|
if (m.children) {
|
||||||
push(m.children)
|
push(m.children)
|
||||||
} else {
|
} else if (m.children === null){
|
||||||
pool.push({
|
pool.push({
|
||||||
icon: m.meta.icon,
|
icon: m.meta.icon,
|
||||||
path: m.path,
|
path: m.path,
|
||||||
|
|
|
@ -183,14 +183,15 @@ a, button, input, textarea {
|
||||||
}
|
}
|
||||||
|
|
||||||
.snowy-header-right {
|
.snowy-header-right {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.snowy-header-logo {
|
.snowy-header-logo {
|
||||||
height: 49px;
|
height: 50px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
.snowy-header-logo-primary-color {
|
.snowy-header-logo-primary-color {
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright [2022] [https://www.xiaonuo.vip]
|
|
||||||
* Snowy采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
|
||||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
|
||||||
* 2.请不要删除和修改Snowy源码头部的版权声明。
|
|
||||||
* 3.本项目代码可免费商业使用,商业使用请保留源码和相关描述文件的项目出处,作者声明等。
|
|
||||||
* 4.分发源码时候,请注明软件出处 https://www.xiaonuo.vip
|
|
||||||
* 5.不可二次分发开源参与同类竞品,如有想法可联系团队xiaonuobase@qq.com商议合作。
|
|
||||||
* 6.若您的项目无法满足以上几点,需要更多功能代码,获取Snowy商业授权许可,请在官网购买授权,地址为 https://www.xiaonuo.vip
|
|
||||||
*/
|
|
||||||
import { mapState } from 'vuex'
|
|
||||||
import { DEVICE_TYPE, deviceEnquire } from '@/utils/device'
|
|
||||||
|
|
||||||
// const mixinsComputed = Vue.config.optionMergeStrategies.computed
|
|
||||||
// const mixinsMethods = Vue.config.optionMergeStrategies.methods
|
|
||||||
|
|
||||||
const mixin = {
|
|
||||||
computed: {
|
|
||||||
...mapState({
|
|
||||||
layoutMode: (state) => state.app.layout,
|
|
||||||
navTheme: (state) => state.app.theme,
|
|
||||||
primaryColor: (state) => state.app.color,
|
|
||||||
colorWeak: (state) => state.app.weak,
|
|
||||||
fixedHeader: (state) => state.app.fixedHeader,
|
|
||||||
fixSiderbar: (state) => state.app.fixSiderbar,
|
|
||||||
fixSidebar: (state) => state.app.fixSiderbar,
|
|
||||||
contentWidth: (state) => state.app.contentWidth,
|
|
||||||
autoHideHeader: (state) => state.app.autoHideHeader,
|
|
||||||
sidebarOpened: (state) => state.app.sidebar,
|
|
||||||
multiTab: (state) => state.app.multiTab
|
|
||||||
})
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
isTopMenu() {
|
|
||||||
return this.layoutMode === 'topmenu'
|
|
||||||
},
|
|
||||||
isSideMenu() {
|
|
||||||
return !this.isTopMenu()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const mixinDevice = {
|
|
||||||
computed: {
|
|
||||||
...mapState({
|
|
||||||
device: (state) => state.app.device
|
|
||||||
})
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
isMobile() {
|
|
||||||
return this.device === DEVICE_TYPE.MOBILE
|
|
||||||
},
|
|
||||||
isDesktop() {
|
|
||||||
return this.device === DEVICE_TYPE.DESKTOP
|
|
||||||
},
|
|
||||||
isTablet() {
|
|
||||||
return this.device === DEVICE_TYPE.TABLET
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const AppDeviceEnquire = {
|
|
||||||
mounted() {
|
|
||||||
const { $store } = this
|
|
||||||
deviceEnquire((deviceType) => {
|
|
||||||
switch (deviceType) {
|
|
||||||
case DEVICE_TYPE.DESKTOP:
|
|
||||||
$store.commit('TOGGLE_DEVICE', 'desktop')
|
|
||||||
$store.dispatch('setSidebar', true)
|
|
||||||
break
|
|
||||||
case DEVICE_TYPE.TABLET:
|
|
||||||
$store.commit('TOGGLE_DEVICE', 'tablet')
|
|
||||||
$store.dispatch('setSidebar', false)
|
|
||||||
break
|
|
||||||
case DEVICE_TYPE.MOBILE:
|
|
||||||
default:
|
|
||||||
$store.commit('TOGGLE_DEVICE', 'mobile')
|
|
||||||
$store.dispatch('setSidebar', true)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { mixin, AppDeviceEnquire, mixinDevice }
|
|
|
@ -1,11 +1,6 @@
|
||||||
import pinyin from 'js-pinyin'
|
import pinyin from 'js-pinyin'
|
||||||
import store from '@/store/index'
|
|
||||||
|
|
||||||
/**
|
// 中文转拼音 传入仅首字母
|
||||||
* 中文转拼音
|
|
||||||
* @param first 仅首字母
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
Object.defineProperty(String.prototype, 'toPinyin', {
|
Object.defineProperty(String.prototype, 'toPinyin', {
|
||||||
writable: false,
|
writable: false,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
|
@ -19,11 +14,7 @@ Object.defineProperty(String.prototype, 'toPinyin', {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
// 字符检索 传入检索值
|
||||||
* 字符检索
|
|
||||||
* @param input 检索值
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
Object.defineProperty(String.prototype, 'filter', {
|
Object.defineProperty(String.prototype, 'filter', {
|
||||||
writable: false,
|
writable: false,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
|
|
Loading…
Reference in New Issue