vue3 + Element-plus 开发后台管理系统(27)

226 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第27天,点击查看活动详情

后台项目前端综合解决方案之通用功能开发

数据源重处理,生成 searchPool

前一篇我们明确了最终我们期望得到的数据源结构,那么接下来我们就需要重新计算数据源,生成对应的 searchPoll

创建 compositions/HeaderSearch/FuseData.js

import path from 'path'
import i18n from '@/i18n'
/**
 * 筛选出可供搜索的路由对象
 * @param routes 路由表
 * @param basePath 基础路径,默认为 /
 * @param prefixTitle
 */
export const generateRoutes = (routes, basePath = '/', prefixTitle = []) => {
  // 创建 result 数据
  let res = []
  // 循环 routes 路由
  for (const route of routes) {
    // 创建包含 path 和 title 的 item
    const data = {
      path: path.resolve(basePath, route.path),
      title: [...prefixTitle]
    }
    // 当前存在 meta 时,使用 i18n 解析国际化数据,组合成新的 title 内容
    // 动态路由不允许被搜索
    // 匹配动态路由的正则
    const re = /.*\/:.*/
    if (route.meta && route.meta.title && !re.exec(route.path)) {
      const i18ntitle = i18n.global.t(`msg.route.${route.meta.title}`)
      data.title = [...data.title, i18ntitle]
      res.push(data)
    }

    // 存在 children 时,迭代调用
    if (route.children) {
      const tempRoutes = generateRoutes(route.children, data.path, data.title)
      if (tempRoutes.length >= 1) {
        res = [...res, ...tempRoutes]
      }
    }
  }
  return res
}

headerSearch 中导入 generateRoutes

<script setup>
import { computed, ref } from 'vue'
import { generateRoutes } from './FuseData'
import Fuse from 'fuse.js'
import { filterRouters } from '@/utils/route'
import { useRouter } from 'vue-router'

// 检索数据源
const router = useRouter()
const searchPool = computed(() => {
  const filterRoutes = filterRouters(router.getRoutes())
  return generateRoutes(filterRoutes)
})
/**
 * 搜索库相关
 */
const fuse = new Fuse(searchPool.value, {})
</script>

通过 querySearch 测试搜索结果

// 搜索方法
const querySearch = query => {
  console.log(fuse.search(query))
}

渲染搜索数据

数据源处理完之后,我们就需要完成渲染搜索出的数据以及对应的跳转

1、渲染检索出的数据

<template>
  <el-option
    v-for="option in searchOptions"
    :key="option.item.path"
    :label="option.item.title.join(' > ')"
    :value="option.item"
  ></el-option>
</template>

<script setup>
// 搜索结果
const searchOptions = ref([])
// 搜索方法
const querySearch = query => {
  if (query !== '') {
    searchOptions.value = fuse.search(query)
  } else {
    searchOptions.value = []
  }
}
</script>

2、完成对应的跳转

// 选中回调
const onSelectChange = val => {
  router.push(val.path)
}

剩余问题处理

到这里我们的 headerSearch 功能基本上已经完成了,但是还存在一些小 bug,那么下边我们就来一一处理

1、在 search 打开时,点击 body 关闭 search

2、在 search 关闭时,清理 searchOptions

3、headerSearch 应具备国际化的能力

我们首先处理前两个问题

/**
 * 关闭 search 的处理事件
 */
const onClose = () => {
  headerSearchSelectRef.value.blur()
  isShow.value = false
  searchOptions.value = []
}
/**
 * 监听 search 打开,处理 close 事件
 */
watch(isShow, val => {
  if (val) {
    document.body.addEventListener('click', onClose)
  } else {
    document.body.removeEventListener('click', onClose)
  }
})

接下来处理国际化的问题,想要处理这个问题非常简单,我们只需要监听语言变化重新计算数据源初始化 fuse 即可

utils/i18n 下,新建方法 watchSwitchLang

import { watch } from 'vue'
import store from '@/store'

/**
 *
 * @param  {...any} cbs 所有的回调
 */
export function watchSwitchLang(...cbs) {
  watch(
    () => store.getters.language,
    () => {
      cbs.forEach(cb => cb(store.getters.language))
    }
  )
}

在 headerSearch 监听变化,重新赋值

<script setup>
import { watchSwitchLang } from '@/utils/i18n'

// 检索数据源
const router = useRouter()
let searchPool = computed(() => {
  const filterRoutes = filterRouters(router.getRoutes())
  return generateRoutes(filterRoutes)
})
/**
 * 搜索库相关
 */
let fuse
const initFuse = searchPool => {
  fuse = new Fuse(searchPool, {})
}
initFuse(searchPool.value)

// 处理国际化
watchSwitchLang(() => {
  searchPool = computed(() => {
    const filterRoutes = filterRouters(router.getRoutes())
    return generateRoutes(filterRoutes)
  })
  initFuse(searchPool.value)
})
</script>

headerSearch 方案总结

到这里整个 headerSearch 我们已经处理完毕了,整个 headerSearch 我们只需要把握三个核心的关键点

1、根据指定内容对所有页面进行检索

2、以 select 形式展示检索出对应的页面

3、通过检索页面可快速进入对应页面

而比较复杂的点可能有两个地方

1、模糊搜索

2、检索数据源

对于这两块,我们依赖于 fuse.js 进行了实现,简化了我们的业务处理