feature: 新增搜索框功能,快捷键:`Ctrl + F`

release-2.0.0.2
王良 2025-02-20 09:39:31 +08:00
parent 3f5049ad91
commit 759bfe8029
9 changed files with 136 additions and 55 deletions

View File

@ -34,6 +34,7 @@
"request-progress": "^3.0.0", "request-progress": "^3.0.0",
"sass": "^1.81.0", "sass": "^1.81.0",
"sass-loader": "^16.0.3", "sass-loader": "^16.0.3",
"search-bar-vue2": "^1.0.0",
"vue": "^2.7.16", "vue": "^2.7.16",
"vue-json-editor-fix-cn": "^1.4.3", "vue-json-editor-fix-cn": "^1.4.3",
"vue-router": "^3.6.5" "vue-router": "^3.6.5"

View File

@ -298,28 +298,38 @@ function createWindow (startHideWindow, autoQuitIfError = true) {
}) })
const shortcut = (event, input) => { const shortcut = (event, input) => {
// 按 F12打开/关闭 开发者工具 if (input.key === 'F12' && input.type === 'keyUp' && !input.control && !input.shift && !input.alt && !input.meta) {
if (input.key === 'F12') { // 按 F12打开/关闭 开发者工具
// 阻止默认的按键事件行为
event.preventDefault() event.preventDefault()
// 切换开发者工具显示状态
switchDevTools() switchDevTools()
// eslint-disable-next-line style/brace-style } else if (input.key === 'F5' && input.type === 'keyUp' && !input.control && !input.shift && !input.alt && !input.meta) {
} // 按 F5刷新页面
// 按 F5刷新页面
else if (input.key === 'F5') {
// 阻止默认的按键事件行为
event.preventDefault() event.preventDefault()
// 刷新页面
win.webContents.reload() win.webContents.reload()
} else {
// 全文检索框SearchBar相关快捷键
if ((input.key === 'F' || input.key === 'f') && input.type === 'keyDown' && input.control && !input.shift && !input.alt && !input.meta) {
// 按 Ctrl + F显示或隐藏全文检索框SearchBar
event.preventDefault()
win.webContents.send('search-bar', { key: 'show-hide' })
} else if (input.key === 'Escape' && input.type === 'keyUp' && !input.control && !input.shift && !input.alt && !input.meta) {
// 按 ESC隐藏全文检索框SearchBar
event.preventDefault()
win.webContents.send('search-bar', { key: 'show-hide', hideSearchBar: true })
} else if (input.key === 'F3' && input.type === 'keyDown' && !input.control && !input.shift && !input.alt && !input.meta) {
// 按 F3全文检索框SearchBar定位到下一个
event.preventDefault()
win.webContents.send('search-bar', { key: 'next' })
} else if (input.key === 'F3' && input.type === 'keyDown' && !input.control && input.shift && !input.alt && !input.meta) {
// 按 Shift + F3全文检索框SearchBar定位到上一个
event.preventDefault()
win.webContents.send('search-bar', { key: 'previous' })
}
} }
} }
// 监听键盘事件 // 监听键盘事件
win.webContents.on('before-input-event', (event, input) => { win.webContents.on('before-input-event', (event, input) => {
if (input.type !== 'keyUp' || input.control || input.alt || input.shift || input.meta) {
return
}
win.webContents.executeJavaScript('config') win.webContents.executeJavaScript('config')
.then((value) => { .then((value) => {
console.info('window.config:', value, ', key:', input.key) console.info('window.config:', value, ', key:', input.key)

View File

@ -1,6 +1,7 @@
import antd from 'ant-design-vue' import antd from 'ant-design-vue'
import Vue from 'vue' import Vue from 'vue'
import VueRouter from 'vue-router' import VueRouter from 'vue-router'
import SearchBar from 'search-bar-vue2'
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import view from './view' import view from './view'
import App from './view/App.vue' import App from './view/App.vue'
@ -25,6 +26,7 @@ try {
Vue.config.productionTip = false Vue.config.productionTip = false
Vue.use(antd) Vue.use(antd)
Vue.use(VueRouter) Vue.use(VueRouter)
Vue.use(SearchBar)
Vue.component(DsContainer) Vue.component(DsContainer)
// 3. 创建 router 实例,然后传 `routes` 配置 // 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。 // 你还可以传别的配置参数, 不过先这么简单着吧。

View File

@ -1,4 +1,6 @@
<script> <script>
import { ipcRenderer } from 'electron'
import { SearchBar } from 'search-bar-vue2'
import createMenus from '@/view/router/menu' import createMenus from '@/view/router/menu'
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN' import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
import { colorTheme } from './composables/theme' import { colorTheme } from './composables/theme'
@ -6,6 +8,7 @@ import { colorTheme } from './composables/theme'
export default { export default {
name: 'App', name: 'App',
components: { components: {
SearchBar,
}, },
data () { data () {
return { return {
@ -13,6 +16,7 @@ export default {
info: {}, info: {},
menus: undefined, menus: undefined,
config: undefined, config: undefined,
hideSearchBar: true,
} }
}, },
computed: { computed: {
@ -37,16 +41,48 @@ export default {
this.$api.info.get().then((ret) => { this.$api.info.get().then((ret) => {
this.info = ret this.info = ret
}) })
ipcRenderer.on('search-bar', (_, message) => {
console.info(_, message)
if (message.key === 'show-hide') {
if (window.config.disableSearchBar) {
this.hideSearchBar = true
return
}
this.hideSearchBar = message.hideSearchBar != null ? message.hideSearchBar : !this.hideSearchBar
//
if (!this.hideSearchBar) {
setTimeout(() => {
try {
this.$refs.searchBar.$el.querySelector('input').focus()
} catch {
}
}, 100)
}
} else {
//
if (this.hideSearchBar) {
this.hideSearchBar = false
return
}
if (message.key === 'next' && !this.hideSearchBar) {
this.$refs.searchBar.next()
} else if (message.key === 'previous' && !this.hideSearchBar) {
this.$refs.searchBar.previous()
}
}
})
}, },
methods: { methods: {
handleClick (e) {
console.log('click', e)
},
titleClick (item) { titleClick (item) {
console.log('title click:', item) console.log('title click:', item)
}, },
menuClick (item) { menuClick (item) {
console.log('menu click:', item) console.log('menu click:', item)
window.config.disableSearchBar = false
this.$router.replace(item.path) this.$router.replace(item.path)
}, },
}, },
@ -56,6 +92,12 @@ export default {
<template> <template>
<a-config-provider :locale="locale"> <a-config-provider :locale="locale">
<div class="ds_layout" :class="themeClass"> <div class="ds_layout" :class="themeClass">
<SearchBar ref="searchBar"
:root="'#document'"
:highlightClass="'search-bar-highlight'"
:selectedClass="'selected-highlight'"
:hiden.sync="hideSearchBar"
/>
<a-layout> <a-layout>
<a-layout-sider :theme="theme"> <a-layout-sider :theme="theme">
<div class="logo" /> <div class="logo" />
@ -82,7 +124,7 @@ export default {
</a-layout-sider> </a-layout-sider>
<a-layout> <a-layout>
<!-- <a-layout-header>Header</a-layout-header> --> <!-- <a-layout-header>Header</a-layout-header> -->
<a-layout-content> <a-layout-content id="document">
<router-view /> <router-view />
</a-layout-content> </a-layout-content>
<a-layout-footer> <a-layout-footer>
@ -144,4 +186,11 @@ body {
border: 0; border: 0;
} }
} }
.search-bar-highlight {
background-color: #ef0fff;
&.selected-highlight {
background-color: #38d878;
}
}
</style> </style>

View File

@ -27,6 +27,9 @@ export function apiInit (app) {
ipcRenderer.removeAllListeners(channel) ipcRenderer.removeAllListeners(channel)
}, },
invoke, invoke,
postMessage (channel, ...args) {
ipcRenderer.postMessage(channel, ...args)
},
send, send,
async openExternal (href) { async openExternal (href) {
await shell.openExternal(href) await shell.openExternal(href)

View File

@ -170,13 +170,18 @@ export default {
}, },
async handleTabChange (key) { async handleTabChange (key) {
if (key !== '2' && key !== '3' && key !== '5' && key !== '6' && key !== '7') { if (key !== '2' && key !== '3' && key !== '5' && key !== '6' && key !== '7') {
return // JsonEditor
} window.config.disableSearchBar = false
} else {
// JsonEditor
window.config.disableSearchBar = true
this.$api.ipc.postMessage('search-bar', { key: 'show-hide', hideSearchBar: true })
// vue-json-editor // vue-json-editor
setTimeout(() => { setTimeout(() => {
window.dispatchEvent(new Event('resize')) window.dispatchEvent(new Event('resize'))
}, 10) }, 10)
}
}, },
}, },
} }
@ -271,7 +276,7 @@ export default {
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="2" tab="拦截设置"> <a-tab-pane key="2" tab="拦截设置">
<VueJsonEditor <VueJsonEditor
ref="editor" v-model="config.server.intercepts" style="height:100%" mode="code" ref="editor2" v-model="config.server.intercepts" style="height:100%" mode="code"
:show-btns="false" :expanded-on-start="true" :show-btns="false" :expanded-on-start="true"
/> />
</a-tab-pane> </a-tab-pane>
@ -284,7 +289,7 @@ export default {
<hr style="margin-bottom:15px"> <hr style="margin-bottom:15px">
<div>这里指定域名的超时时间<span class="form-help">域名配置可使用通配符或正则</span></div> <div>这里指定域名的超时时间<span class="form-help">域名配置可使用通配符或正则</span></div>
<VueJsonEditor <VueJsonEditor
ref="editor" v-model="config.server.setting.timeoutMapping" style="flex-grow:1;min-height:300px;margin-top:10px" mode="code" ref="editor3" v-model="config.server.setting.timeoutMapping" style="flex-grow:1;min-height:300px;margin-top:10px" mode="code"
:show-btns="false" :expanded-on-start="true" :show-btns="false" :expanded-on-start="true"
/> />
</div> </div>
@ -320,7 +325,7 @@ export default {
说明<code>自动兼容程序</code>会自动根据错误信息进行兼容性调整并将兼容设置保存在 <code>~/.dev-sidecar/automaticCompatibleConfig.json</code> 说明<code>自动兼容程序</code>会自动根据错误信息进行兼容性调整并将兼容设置保存在 <code>~/.dev-sidecar/automaticCompatibleConfig.json</code>
</div> </div>
<VueJsonEditor <VueJsonEditor
ref="editor" v-model="config.server.compatible" style="flex-grow:1;min-height:300px;margin-top:10px;" mode="code" ref="editor5" v-model="config.server.compatible" style="flex-grow:1;min-height:300px;margin-top:10px;" mode="code"
:show-btns="false" :expanded-on-start="true" :show-btns="false" :expanded-on-start="true"
/> />
</div> </div>
@ -332,14 +337,14 @@ export default {
<span class="form-help">域名配置可使用通配符或正则</span> <span class="form-help">域名配置可使用通配符或正则</span>
</div> </div>
<VueJsonEditor <VueJsonEditor
ref="editor" v-model="config.server.preSetIpList" style="flex-grow:1;min-height:300px;margin-top:10px;" mode="code" ref="editor6" v-model="config.server.preSetIpList" style="flex-grow:1;min-height:300px;margin-top:10px;" mode="code"
:show-btns="false" :expanded-on-start="true" :show-btns="false" :expanded-on-start="true"
/> />
</div> </div>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="7" tab="DNS服务管理"> <a-tab-pane key="7" tab="DNS服务管理">
<VueJsonEditor <VueJsonEditor
ref="editor" v-model="config.server.dns.providers" style="height:100%" mode="code" ref="editor7" v-model="config.server.dns.providers" style="height:100%" mode="code"
:show-btns="false" :expanded-on-start="true" :show-btns="false" :expanded-on-start="true"
/> />
</a-tab-pane> </a-tab-pane>

View File

@ -48,24 +48,18 @@ export default {
case 'ArrowDown': case 'ArrowDown':
case 'ArrowLeft': case 'ArrowLeft':
case 'ArrowRight': case 'ArrowRight':
return '' return '无'
//
case 'NumpadDivide': // return 'Num/'
case 'NumpadMultiply': // return 'Num*'
case 'NumpadDecimal': // return 'Num.'
case 'NumpadSubtract': // return 'Num-'
case 'NumpadAdd': // return 'Num+'
return '无'
} }
switch (event.code) { switch (event.code) {
// F1 ~ F12
case 'F1': return 'F1'
case 'F2': return 'F2'
case 'F3': return 'F3'
case 'F4': return 'F4'
case 'F5': return 'F5'
case 'F6': return 'F6'
case 'F7': return 'F7'
case 'F8': return 'F8'
case 'F9': return 'F9'
case 'F10': return 'F10'
case 'F11': return 'F11'
case 'F12': return 'F12'
// 0 ~ 9 // 0 ~ 9
case 'Digit0': return '0' case 'Digit0': return '0'
case 'Digit1': return '1' case 'Digit1': return '1'
@ -110,14 +104,6 @@ export default {
case 'Numpad8': return 'Num8' case 'Numpad8': return 'Num8'
case 'Numpad9': return 'Num9' case 'Numpad9': return 'Num9'
case 'Numpad0': return 'Num0' case 'Numpad0': return 'Num0'
//
case 'NumpadDivide': // return 'Num/'
case 'NumpadMultiply': // return 'Num*'
case 'NumpadDecimal': // return 'Num.'
case 'NumpadSubtract': // return 'Num-'
case 'NumpadAdd': // return 'Num+'
return ''
} }
// //
@ -126,7 +112,7 @@ export default {
} }
console.error(`未能识别的按键key=${event.key}, code=${event.code}, keyCode=${event.keyCode}`) console.error(`未能识别的按键key=${event.key}, code=${event.code}, keyCode=${event.keyCode}`)
return '' return ''
}, },
async disableBeforeInputEvent () { async disableBeforeInputEvent () {
clearTimeout(window.enableBeforeInputEventTimeout) clearTimeout(window.enableBeforeInputEventTimeout)
@ -169,8 +155,8 @@ export default {
shortcut += 'Meta + ' shortcut += 'Meta + '
} }
// F1~F4F6~F11F5F12DevTools // F1F2F4F6~F11F5F12DevTools
if (shortcut === '' && !key.match(/^F([1-46-9]|1[01])$/g)) { if (shortcut === '' && !key.match(/^F([1246-9]|1[01])$/g)) {
this.config.app.showHideShortcut = '无' this.config.app.showHideShortcut = '无'
return return
} }
@ -178,6 +164,10 @@ export default {
// //
shortcut += key shortcut += key
if (shortcut === 'Ctrl + F' || shortcut === 'Shift + F3') {
shortcut = '无' // ''
}
this.config.app.showHideShortcut = shortcut this.config.app.showHideShortcut = shortcut
}, },
async applyBefore () { async applyBefore () {
@ -430,7 +420,7 @@ export default {
<a-form-item label="打开窗口快捷键" :label-col="labelCol" :wrapper-col="wrapperCol"> <a-form-item label="打开窗口快捷键" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-input v-model="config.app.showHideShortcut" @change="shortcutChange" @keydown="shortcutKeyDown" @keyup="shortcutKeyUp" /> <a-input v-model="config.app.showHideShortcut" @change="shortcutChange" @keydown="shortcutKeyDown" @keyup="shortcutKeyUp" />
<div class="form-help"> <div class="form-help">
部分快捷键已被占用F5=刷新页面F12=开发者工具DevTools 部分快捷键已被占用<code>F5</code><code>F12</code><code>Ctrl+F</code><code>F3</code><code>Shift+F3</code>
</div> </div>
</a-form-item> </a-form-item>
<a-form-item label="启动时窗口状态" :label-col="labelCol" :wrapper-col="wrapperCol"> <a-form-item label="启动时窗口状态" :label-col="labelCol" :wrapper-col="wrapperCol">

View File

@ -239,4 +239,13 @@ $dark-input: #777; //输入框:背景色
border-color: #8b2929; border-color: #8b2929;
} }
} }
.search-bar {
div {
color: #000;
}
span {
color: #000;
}
}
} }

View File

@ -108,6 +108,9 @@ importers:
sass-loader: sass-loader:
specifier: ^16.0.3 specifier: ^16.0.3
version: 16.0.3(sass@1.81.0)(webpack@5.96.1) version: 16.0.3(sass@1.81.0)(webpack@5.96.1)
search-bar-vue2:
specifier: ^1.0.0
version: 1.0.0(vue@2.7.16)
vue: vue:
specifier: ^2.7.16 specifier: ^2.7.16
version: 2.7.16 version: 2.7.16
@ -6011,6 +6014,11 @@ packages:
resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==}
engines: {node: ^14.0.0 || >=16.0.0} engines: {node: ^14.0.0 || >=16.0.0}
search-bar-vue2@1.0.0:
resolution: {integrity: sha512-e65f34kiWo/ub6fOsXGC7SU1A2HWyTUhQUn3AhZfO/f002XcCOaRghkWM0sADE8/OaL5MWHZ1sxDjzq2T9vWIQ==}
peerDependencies:
vue: ^2.6.14
select-hose@2.0.0: select-hose@2.0.0:
resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==}
@ -13910,6 +13918,10 @@ snapshots:
refa: 0.12.1 refa: 0.12.1
regexp-ast-analysis: 0.7.1 regexp-ast-analysis: 0.7.1
search-bar-vue2@1.0.0(vue@2.7.16):
dependencies:
vue: 2.7.16
select-hose@2.0.0: {} select-hose@2.0.0: {}
selfsigned@2.4.1: selfsigned@2.4.1: