forked from liweijie/education
1 line
19 KiB
JSON
1 line
19 KiB
JSON
{"remainingRequest":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\vue-loader\\lib\\index.js??vue-loader-options!C:\\Users\\Admin\\Desktop\\education\\frontend\\src\\components\\HeaderSearch\\index.vue?vue&type=script&lang=js","dependencies":[{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\src\\components\\HeaderSearch\\index.vue","mtime":1787567871502},{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\cache-loader\\dist\\cjs.js","mtime":1787309791955},{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\babel-loader\\lib\\index.js","mtime":1787309793142},{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\cache-loader\\dist\\cjs.js","mtime":1787309791955},{"path":"C:\\Users\\Admin\\Desktop\\education\\frontend\\node_modules\\vue-loader\\lib\\index.js","mtime":1787309793790}],"contextDependencies":[],"result":[{"type":"Buffer","data":"base64:Ly8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KLy8KDQppbXBvcnQgRnVzZSBmcm9tICdmdXNlLmpzL2Rpc3QvZnVzZS5taW4uanMnDQppbXBvcnQgcGF0aCBmcm9tICdwYXRoJw0KaW1wb3J0IHsgaXNIdHRwIH0gZnJvbSAnQC91dGlscy92YWxpZGF0ZScNCg0KZXhwb3J0IGRlZmF1bHQgew0KICBuYW1lOiAnSGVhZGVyU2VhcmNoJywNCiAgZGF0YSgpIHsNCiAgICByZXR1cm4gew0KICAgICAgc2VhcmNoOiAnJywNCiAgICAgIG9wdGlvbnM6IFtdLA0KICAgICAgc2VhcmNoUG9vbDogW10sDQogICAgICBhY3RpdmVJbmRleDogLTEsDQogICAgICBzaG93OiBmYWxzZSwNCiAgICAgIGZ1c2U6IHVuZGVmaW5lZA0KICAgIH0NCiAgfSwNCiAgY29tcHV0ZWQ6IHsNCiAgICByb3V0ZXMoKSB7DQogICAgICByZXR1cm4gdGhpcy4kc3RvcmUuZ2V0dGVycy5kZWZhdWx0Um91dGVzDQogICAgfQ0KICB9LA0KICB3YXRjaDogew0KICAgIHJvdXRlcygpIHsNCiAgICAgIHRoaXMuc2VhcmNoUG9vbCA9IHRoaXMuZ2VuZXJhdGVSb3V0ZXModGhpcy5yb3V0ZXMpDQogICAgfSwNCiAgICBzZWFyY2hQb29sKGxpc3QpIHsNCiAgICAgIHRoaXMuaW5pdEZ1c2UobGlzdCkNCiAgICB9DQogIH0sDQogIG1vdW50ZWQoKSB7DQogICAgdGhpcy5zZWFyY2hQb29sID0gdGhpcy5nZW5lcmF0ZVJvdXRlcyh0aGlzLnJvdXRlcykNCiAgfSwNCiAgbWV0aG9kczogew0KICAgIGNsaWNrKCkgew0KICAgICAgdGhpcy5zaG93ID0gIXRoaXMuc2hvdw0KICAgICAgaWYgKHRoaXMuc2hvdykgew0KICAgICAgICB0aGlzLm9wdGlvbnMgPSB0aGlzLnNlYXJjaFBvb2wNCiAgICAgIH0NCiAgICB9LA0KICAgIG9uRGlhbG9nT3BlbmVkKCkgew0KICAgICAgdGhpcy4kbmV4dFRpY2soKCkgPT4gew0KICAgICAgICB0aGlzLiRyZWZzLmhlYWRlclNlYXJjaFNlbGVjdFJlZiAmJiB0aGlzLiRyZWZzLmhlYWRlclNlYXJjaFNlbGVjdFJlZi5mb2N1cygpDQogICAgICB9KQ0KICAgIH0sDQogICAgY2xvc2UoKSB7DQogICAgICB0aGlzLiRyZWZzLmhlYWRlclNlYXJjaFNlbGVjdFJlZiAmJiB0aGlzLiRyZWZzLmhlYWRlclNlYXJjaFNlbGVjdFJlZi5ibHVyKCkNCiAgICAgIHRoaXMuc2VhcmNoID0gJycNCiAgICAgIHRoaXMub3B0aW9ucyA9IHRoaXMuc2VhcmNoUG9vbA0KICAgICAgdGhpcy5zaG93ID0gZmFsc2UNCiAgICAgIHRoaXMuYWN0aXZlSW5kZXggPSAtMQ0KICAgIH0sDQogICAgY2hhbmdlKHZhbCkgew0KICAgICAgY29uc3QgcCA9IHZhbC5wYXRoDQogICAgICBjb25zdCBxdWVyeSA9IHZhbC5xdWVyeQ0KICAgICAgaWYgKGlzSHR0cCh2YWwucGF0aCkpIHsNCiAgICAgICAgLy8gaHR0cChzKTovLyDot6/lvoTmlrDnqpflj6PmiZPlvIANCiAgICAgICAgY29uc3QgcGluZGV4ID0gcC5pbmRleE9mKCdodHRwJykNCiAgICAgICAgd2luZG93Lm9wZW4ocC5zdWJzdHIocGluZGV4LCBwLmxlbmd0aCksICdfYmxhbmsnKQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgaWYgKHF1ZXJ5KSB7DQogICAgICAgICAgdGhpcy4kcm91dGVyLnB1c2goeyBwYXRoOiBwLCBxdWVyeTogSlNPTi5wYXJzZShxdWVyeSkgfSkNCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICB0aGlzLiRyb3V0ZXIucHVzaChwKQ0KICAgICAgICB9DQogICAgICB9DQogICAgICB0aGlzLnNlYXJjaCA9ICcnDQogICAgICB0aGlzLm9wdGlvbnMgPSB0aGlzLnNlYXJjaFBvb2wNCiAgICAgIHRoaXMuJG5leHRUaWNrKCgpID0+IHsNCiAgICAgICAgdGhpcy5zaG93ID0gZmFsc2UNCiAgICAgIH0pDQogICAgfSwNCiAgICBpbml0RnVzZShsaXN0KSB7DQogICAgICB0aGlzLmZ1c2UgPSBuZXcgRnVzZShsaXN0LCB7DQogICAgICAgIHNob3VsZFNvcnQ6IHRydWUsDQogICAgICAgIHRocmVzaG9sZDogMC4yLA0KICAgICAgICBtaW5NYXRjaENoYXJMZW5ndGg6IDEsDQogICAgICAgIGtleXM6IFt7DQogICAgICAgICAgbmFtZTogJ3RpdGxlJywNCiAgICAgICAgICB3ZWlnaHQ6IDAuNw0KICAgICAgICB9LCB7DQogICAgICAgICAgbmFtZTogJ3BhdGgnLA0KICAgICAgICAgIHdlaWdodDogMC4zDQogICAgICAgIH1dDQogICAgICB9KQ0KICAgIH0sDQogICAgZ2VuZXJhdGVSb3V0ZXMocm91dGVzLCBiYXNlUGF0aCA9ICcvJywgcHJlZml4VGl0bGUgPSBbXSkgew0KICAgICAgbGV0IHJlcyA9IFtdDQogICAgICBmb3IgKGNvbnN0IHJvdXRlciBvZiByb3V0ZXMpIHsNCiAgICAgICAgaWYgKHJvdXRlci5oaWRkZW4pIHsgY29udGludWUgfQ0KICAgICAgICBjb25zdCBkYXRhID0gew0KICAgICAgICAgIHBhdGg6ICFpc0h0dHAocm91dGVyLnBhdGgpID8gcGF0aC5yZXNvbHZlKGJhc2VQYXRoLCByb3V0ZXIucGF0aCkgOiByb3V0ZXIucGF0aCwNCiAgICAgICAgICB0aXRsZTogWy4uLnByZWZpeFRpdGxlXSwNCiAgICAgICAgICBpY29uOiAnJw0KICAgICAgICB9DQogICAgICAgIGlmIChyb3V0ZXIubWV0YSAmJiByb3V0ZXIubWV0YS50aXRsZSkgew0KICAgICAgICAgIGRhdGEudGl0bGUgPSBbLi4uZGF0YS50aXRsZSwgcm91dGVyLm1ldGEudGl0bGVdDQogICAgICAgICAgZGF0YS5pY29uID0gcm91dGVyLm1ldGEuaWNvbg0KICAgICAgICAgIGlmIChyb3V0ZXIucmVkaXJlY3QgIT09ICdub1JlZGlyZWN0Jykgew0KICAgICAgICAgICAgcmVzLnB1c2goZGF0YSkNCiAgICAgICAgICB9DQogICAgICAgIH0NCiAgICAgICAgaWYgKHJvdXRlci5xdWVyeSkgew0KICAgICAgICAgIGRhdGEucXVlcnkgPSByb3V0ZXIucXVlcnkNCiAgICAgICAgfQ0KICAgICAgICBpZiAocm91dGVyLmNoaWxkcmVuKSB7DQogICAgICAgICAgY29uc3QgdGVtcFJvdXRlcyA9IHRoaXMuZ2VuZXJhdGVSb3V0ZXMocm91dGVyLmNoaWxkcmVuLCBkYXRhLnBhdGgsIGRhdGEudGl0bGUpDQogICAgICAgICAgaWYgKHRlbXBSb3V0ZXMubGVuZ3RoID49IDEpIHsNCiAgICAgICAgICAgIHJlcyA9IFsuLi5yZXMsIC4uLnRlbXBSb3V0ZXNdDQogICAgICAgICAgfQ0KICAgICAgICB9DQogICAgICB9DQogICAgICByZXR1cm4gcmVzDQogICAgfSwNCiAgICBxdWVyeVNlYXJjaChxdWVyeSkgew0KICAgICAgdGhpcy5hY3RpdmVJbmRleCA9IC0xDQogICAgICBpZiAocXVlcnkgIT09ICcnKSB7DQogICAgICAgIGNvbnN0IHEgPSBxdWVyeS50b0xvd2VyQ2FzZSgpDQogICAgICAgIGNvbnN0IHBhdGhNYXRjaGVzID0gdGhpcy5zZWFyY2hQb29sLmZpbHRlcihpdGVtID0+DQogICAgICAgICAgaXRlbS5wYXRoLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMocSkNCiAgICAgICAgKQ0KICAgICAgICBjb25zdCBmdXNlTWF0Y2hlcyA9IHRoaXMuZnVzZS5zZWFyY2gocXVlcnkpLm1hcChpdGVtID0+IGl0ZW0uaXRlbSkNCiAgICAgICAgY29uc3QgbWVyZ2VkID0gWy4uLnBhdGhNYXRjaGVzXQ0KICAgICAgICBmdXNlTWF0Y2hlcy5mb3JFYWNoKGl0ZW0gPT4gew0KICAgICAgICAgIGlmICghbWVyZ2VkLmZpbmQobSA9PiBtLnBhdGggPT09IGl0ZW0ucGF0aCkpIHsNCiAgICAgICAgICAgIG1lcmdlZC5wdXNoKGl0ZW0pDQogICAgICAgICAgfQ0KICAgICAgICB9KQ0KICAgICAgICB0aGlzLm9wdGlvbnMgPSBtZXJnZWQNCiAgICAgIH0gZWxzZSB7DQogICAgICAgIHRoaXMub3B0aW9ucyA9IHRoaXMuc2VhcmNoUG9vbA0KICAgICAgfQ0KICAgIH0sDQogICAgYWN0aXZlU3R5bGUoaW5kZXgpIHsNCiAgICAgIGlmIChpbmRleCAhPT0gdGhpcy5hY3RpdmVJbmRleCkgcmV0dXJuIHt9DQogICAgICByZXR1cm4gew0KICAgICAgICAnYmFja2dyb3VuZC1jb2xvcic6ICcjMDA4NzVBJywNCiAgICAgICAgJ2NvbG9yJzogJyNmZmYnDQogICAgICB9DQogICAgfSwNCiAgICBuYXZpZ2F0ZVJlc3VsdChkaXJlY3Rpb24pIHsNCiAgICAgIGlmIChkaXJlY3Rpb24gPT09ICd1cCcpIHsNCiAgICAgICAgdGhpcy5hY3RpdmVJbmRleCA9IHRoaXMuYWN0aXZlSW5kZXggPD0gMCA/IHRoaXMub3B0aW9ucy5sZW5ndGggLSAxIDogdGhpcy5hY3RpdmVJbmRleCAtIDENCiAgICAgIH0gZWxzZSBpZiAoZGlyZWN0aW9uID09PSAnZG93bicpIHsNCiAgICAgICAgdGhpcy5hY3RpdmVJbmRleCA9IHRoaXMuYWN0aXZlSW5kZXggPj0gdGhpcy5vcHRpb25zLmxlbmd0aCAtIDEgPyAwIDogdGhpcy5hY3RpdmVJbmRleCArIDENCiAgICAgIH0NCiAgICB9LA0KICAgIHNlbGVjdEFjdGl2ZVJlc3VsdCgpIHsNCiAgICAgIGlmICh0aGlzLm9wdGlvbnMubGVuZ3RoID4gMCAmJiB0aGlzLmFjdGl2ZUluZGV4ID49IDApIHsNCiAgICAgICAgdGhpcy5jaGFuZ2UodGhpcy5vcHRpb25zW3RoaXMuYWN0aXZlSW5kZXhdKQ0KICAgICAgfQ0KICAgIH0sDQogICAgaGlnaGxpZ2h0VGV4dCh0ZXh0KSB7DQogICAgICBpZiAoIXRleHQpIHJldHVybiAnJw0KICAgICAgaWYgKCF0aGlzLnNlYXJjaCkgcmV0dXJuIHRleHQNCiAgICAgIGNvbnN0IGtleXdvcmQgPSB0aGlzLmVzY2FwZVJlZ0V4cCh0aGlzLnNlYXJjaCkNCiAgICAgIGNvbnN0IHJlZyA9IG5ldyBSZWdFeHAoYCgke2tleXdvcmR9KWAsICdnaScpDQogICAgICByZXR1cm4gdGV4dC5yZXBsYWNlKHJlZywgJzxzcGFuIGNsYXNzPSJoaWdobGlnaHQiPiQxPC9zcGFuPicpDQogICAgfSwNCiAgICBlc2NhcGVSZWdFeHAoc3RyKSB7DQogICAgICByZXR1cm4gc3RyLnJlcGxhY2UoL1suKis/XiR7fSgpfFtcXVxcXS9nLCAnXFwkJicpDQogICAgfQ0KICB9DQp9DQo="},{"version":3,"sources":["index.vue"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6EA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"index.vue","sourceRoot":"src/components/HeaderSearch","sourcesContent":["<template>\r\n <div class=\"header-search\">\r\n <svg-icon class-name=\"search-icon\" icon-class=\"search\" @click.stop=\"click\" />\r\n <el-dialog\r\n :visible.sync=\"show\"\r\n width=\"600px\"\r\n @close=\"close\"\r\n @opened=\"onDialogOpened\"\r\n :show-close=\"false\"\r\n append-to-body\r\n >\r\n <el-input\r\n v-model=\"search\"\r\n ref=\"headerSearchSelectRef\"\r\n size=\"large\"\r\n @input=\"querySearch\"\r\n prefix-icon=\"el-icon-search\"\r\n placeholder=\"菜单搜索,支持标题、URL模糊查询\"\r\n clearable\r\n @keyup.enter.native=\"selectActiveResult\"\r\n @keydown.up.native=\"navigateResult('up')\"\r\n @keydown.down.native=\"navigateResult('down')\"\r\n >\r\n </el-input>\r\n\r\n <div class=\"result-count\" v-if=\"search && options.length > 0\">\r\n 找到 <strong>{{ options.length }}</strong> 个结果\r\n </div>\r\n\r\n <el-scrollbar wrap-class=\"right-scrollbar-wrapper\">\r\n <div class=\"result-wrap\">\r\n <template v-if=\"options.length > 0\">\r\n <div\r\n class=\"search-item\"\r\n v-for=\"(item, index) in options\"\r\n :key=\"item.path\"\r\n :class=\"{ 'is-active': index === activeIndex }\"\r\n :style=\"activeStyle(index)\"\r\n @mouseenter=\"activeIndex = index\"\r\n @mouseleave=\"activeIndex = -1\"\r\n >\r\n <div class=\"left\">\r\n <svg-icon class=\"menu-icon\" :icon-class=\"item.icon\" />\r\n </div>\r\n <div class=\"search-info\" @click=\"change(item)\">\r\n <div class=\"menu-title\" v-html=\"highlightText(item.title.join(' / '))\"></div>\r\n <div class=\"menu-path\" v-html=\"highlightText(item.path)\"></div>\r\n </div>\r\n <svg-icon icon-class=\"enter\" v-show=\"index === activeIndex\" />\r\n </div>\r\n </template>\r\n\r\n <div class=\"empty-state\" v-else-if=\"search && options.length === 0\">\r\n <i class=\"el-icon-search empty-icon\"></i>\r\n <p class=\"empty-text\">未找到 \"<strong>{{ search }}</strong>\" 相关菜单</p>\r\n <p class=\"empty-tip\">试试其他关键词或路径</p>\r\n </div>\r\n\r\n </div>\r\n </el-scrollbar>\r\n\r\n <div class=\"search-footer\">\r\n <span class=\"shortcut-item\">\r\n <kbd>↑</kbd><kbd>↓</kbd> 切换\r\n </span>\r\n <span class=\"shortcut-item\">\r\n <kbd>↵</kbd> 选择\r\n </span>\r\n <span class=\"shortcut-item\">\r\n <kbd>Esc</kbd> 关闭\r\n </span>\r\n </div>\r\n </el-dialog>\r\n </div>\r\n</template>\r\n\r\n<script>\r\nimport Fuse from 'fuse.js/dist/fuse.min.js'\r\nimport path from 'path'\r\nimport { isHttp } from '@/utils/validate'\r\n\r\nexport default {\r\n name: 'HeaderSearch',\r\n data() {\r\n return {\r\n search: '',\r\n options: [],\r\n searchPool: [],\r\n activeIndex: -1,\r\n show: false,\r\n fuse: undefined\r\n }\r\n },\r\n computed: {\r\n routes() {\r\n return this.$store.getters.defaultRoutes\r\n }\r\n },\r\n watch: {\r\n routes() {\r\n this.searchPool = this.generateRoutes(this.routes)\r\n },\r\n searchPool(list) {\r\n this.initFuse(list)\r\n }\r\n },\r\n mounted() {\r\n this.searchPool = this.generateRoutes(this.routes)\r\n },\r\n methods: {\r\n click() {\r\n this.show = !this.show\r\n if (this.show) {\r\n this.options = this.searchPool\r\n }\r\n },\r\n onDialogOpened() {\r\n this.$nextTick(() => {\r\n this.$refs.headerSearchSelectRef && this.$refs.headerSearchSelectRef.focus()\r\n })\r\n },\r\n close() {\r\n this.$refs.headerSearchSelectRef && this.$refs.headerSearchSelectRef.blur()\r\n this.search = ''\r\n this.options = this.searchPool\r\n this.show = false\r\n this.activeIndex = -1\r\n },\r\n change(val) {\r\n const p = val.path\r\n const query = val.query\r\n if (isHttp(val.path)) {\r\n // http(s):// 路径新窗口打开\r\n const pindex = p.indexOf('http')\r\n window.open(p.substr(pindex, p.length), '_blank')\r\n } else {\r\n if (query) {\r\n this.$router.push({ path: p, query: JSON.parse(query) })\r\n } else {\r\n this.$router.push(p)\r\n }\r\n }\r\n this.search = ''\r\n this.options = this.searchPool\r\n this.$nextTick(() => {\r\n this.show = false\r\n })\r\n },\r\n initFuse(list) {\r\n this.fuse = new Fuse(list, {\r\n shouldSort: true,\r\n threshold: 0.2,\r\n minMatchCharLength: 1,\r\n keys: [{\r\n name: 'title',\r\n weight: 0.7\r\n }, {\r\n name: 'path',\r\n weight: 0.3\r\n }]\r\n })\r\n },\r\n generateRoutes(routes, basePath = '/', prefixTitle = []) {\r\n let res = []\r\n for (const router of routes) {\r\n if (router.hidden) { continue }\r\n const data = {\r\n path: !isHttp(router.path) ? path.resolve(basePath, router.path) : router.path,\r\n title: [...prefixTitle],\r\n icon: ''\r\n }\r\n if (router.meta && router.meta.title) {\r\n data.title = [...data.title, router.meta.title]\r\n data.icon = router.meta.icon\r\n if (router.redirect !== 'noRedirect') {\r\n res.push(data)\r\n }\r\n }\r\n if (router.query) {\r\n data.query = router.query\r\n }\r\n if (router.children) {\r\n const tempRoutes = this.generateRoutes(router.children, data.path, data.title)\r\n if (tempRoutes.length >= 1) {\r\n res = [...res, ...tempRoutes]\r\n }\r\n }\r\n }\r\n return res\r\n },\r\n querySearch(query) {\r\n this.activeIndex = -1\r\n if (query !== '') {\r\n const q = query.toLowerCase()\r\n const pathMatches = this.searchPool.filter(item =>\r\n item.path.toLowerCase().includes(q)\r\n )\r\n const fuseMatches = this.fuse.search(query).map(item => item.item)\r\n const merged = [...pathMatches]\r\n fuseMatches.forEach(item => {\r\n if (!merged.find(m => m.path === item.path)) {\r\n merged.push(item)\r\n }\r\n })\r\n this.options = merged\r\n } else {\r\n this.options = this.searchPool\r\n }\r\n },\r\n activeStyle(index) {\r\n if (index !== this.activeIndex) return {}\r\n return {\r\n 'background-color': '#00875A',\r\n 'color': '#fff'\r\n }\r\n },\r\n navigateResult(direction) {\r\n if (direction === 'up') {\r\n this.activeIndex = this.activeIndex <= 0 ? this.options.length - 1 : this.activeIndex - 1\r\n } else if (direction === 'down') {\r\n this.activeIndex = this.activeIndex >= this.options.length - 1 ? 0 : this.activeIndex + 1\r\n }\r\n },\r\n selectActiveResult() {\r\n if (this.options.length > 0 && this.activeIndex >= 0) {\r\n this.change(this.options[this.activeIndex])\r\n }\r\n },\r\n highlightText(text) {\r\n if (!text) return ''\r\n if (!this.search) return text\r\n const keyword = this.escapeRegExp(this.search)\r\n const reg = new RegExp(`(${keyword})`, 'gi')\r\n return text.replace(reg, '<span class=\"highlight\">$1</span>')\r\n },\r\n escapeRegExp(str) {\r\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\r\n }\r\n }\r\n}\r\n</script>\r\n\r\n<style lang='scss' scoped>\r\n::v-deep {\r\n .el-dialog__header {\r\n padding: 6px !important;\r\n }\r\n\r\n .highlight {\r\n color: red;\r\n font-weight: 600;\r\n }\r\n\r\n .is-active .highlight {\r\n color: rgba(255, 255, 255, 0.9);\r\n font-weight: 600;\r\n }\r\n}\r\n\r\n.header-search {\r\n .search-icon {\r\n cursor: pointer;\r\n font-size: 18px;\r\n vertical-align: middle;\r\n }\r\n}\r\n\r\n.result-count {\r\n padding: 6px 16px 0;\r\n font-size: 12px;\r\n color: #aaa;\r\n\r\n strong {\r\n color: red;\r\n font-weight: 600;\r\n }\r\n}\r\n\r\n.result-wrap {\r\n height: 280px;\r\n margin: 4px 0;\r\n\r\n .search-item {\r\n display: flex;\r\n height: 48px;\r\n align-items: center;\r\n padding-right: 10px;\r\n border-radius: 4px;\r\n transition: background 0.15s;\r\n\r\n .left {\r\n width: 60px;\r\n text-align: center;\r\n flex-shrink: 0;\r\n\r\n .menu-icon {\r\n width: 18px;\r\n height: 18px;\r\n }\r\n }\r\n\r\n .search-info {\r\n padding-left: 5px;\r\n margin-top: 10px;\r\n width: 100%;\r\n display: flex;\r\n flex-direction: column;\r\n justify-content: flex-start;\r\n flex: 1;\r\n overflow: hidden;\r\n\r\n .menu-title,\r\n .menu-path {\r\n height: 20px;\r\n white-space: nowrap;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n }\r\n\r\n .menu-path {\r\n color: #ccc;\r\n font-size: 10px;\r\n }\r\n }\r\n }\r\n\r\n .search-item:hover {\r\n cursor: pointer;\r\n }\r\n\r\n .empty-state {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n height: 100%;\r\n\r\n .empty-icon {\r\n font-size: 42px;\r\n color: #e0e0e0;\r\n margin-bottom: 14px;\r\n }\r\n\r\n .empty-text {\r\n font-size: 14px;\r\n color: #999;\r\n margin: 0 0 6px;\r\n\r\n strong {\r\n color: #666;\r\n }\r\n }\r\n\r\n .empty-tip {\r\n font-size: 12px;\r\n color: #bbb;\r\n margin: 0;\r\n }\r\n }\r\n}\r\n\r\n.search-footer {\r\n display: flex;\r\n align-items: center;\r\n gap: 28px;\r\n padding: 10px 20px;\r\n border-top: 1px solid #f0f0f0;\r\n color: #999;\r\n font-size: 12px;\r\n\r\n .shortcut-item {\r\n display: flex;\r\n align-items: center;\r\n gap: 5px;\r\n }\r\n\r\n kbd {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-width: 20px;\r\n height: 20px;\r\n padding: 0 5px;\r\n border: 1px solid #ddd;\r\n border-radius: 4px;\r\n background: #f7f7f7;\r\n color: #555;\r\n font-size: 11px;\r\n font-family: inherit;\r\n line-height: 1;\r\n box-shadow: 0 1px 0 #ccc;\r\n }\r\n}\r\n</style>"]}]} |