♻️ search 框输入交互优化

This commit is contained in:
muwoo 2021-12-08 15:51:54 +08:00
parent 951f21f5fa
commit 8a35e60e48
17 changed files with 253 additions and 24 deletions

View File

@ -1,6 +1,6 @@
{
"name": "rubick2",
"version": "0.1.0",
"name": "rubick",
"version": "2.0.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",

BIN
public/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
public/icon@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
public/icon@3x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -32,9 +32,9 @@ window.rubick = {
},
// 窗口交互
// hideMainWindow() {
// ipcSendSync("hideMainWindow");
// },
hideMainWindow() {
ipcSendSync("hideMainWindow");
},
showMainWindow() {
ipcSendSync("showMainWindow");
},

View File

@ -0,0 +1,60 @@
import path from "path";
import fs from "fs";
import getLocalDataFile from "./getLocalDataFile";
import { app } from "electron";
const configPath = path.join(getLocalDataFile(), "./rubick-config.json");
const defaultConfigForAnyPlatform = {
version: 1,
perf: {
shortCut: {
showAndHidden: "Option+R",
separate: "Ctrl+D",
quit: "Shift+Escape",
},
common: {
start: true,
space: true,
// 是否失焦隐藏。默认在dev环境不隐藏在打包后隐藏。
hideOnBlur: app.isPackaged,
},
local: {
search: true,
},
},
global: [],
};
global.OP_CONFIG = {
config: null,
get() {
try {
if (!global.config) {
global.config = JSON.parse(
fs.readFileSync(configPath, "utf8") ||
JSON.stringify(defaultConfigForAnyPlatform)
);
}
// 重置
if (
!global.config.version ||
global.config.version < defaultConfigForAnyPlatform.version
) {
global.config = defaultConfigForAnyPlatform;
fs.writeFileSync(
configPath,
JSON.stringify(defaultConfigForAnyPlatform)
);
}
return global.config;
} catch (e) {
global.config = defaultConfigForAnyPlatform;
return global.config;
}
},
set(key, value) {
global.config[key] = value;
fs.writeFileSync(configPath, JSON.stringify(global.config));
},
};

View File

@ -34,15 +34,17 @@ global.LOCAL_PLUGINS = {
},
addPlugin(plugin) {
let has = false;
global.LOCAL_PLUGINS.PLUGINS.some((p) => {
const currentPlugins = global.LOCAL_PLUGINS.getLocalPlugins();
currentPlugins.some((p) => {
has = p.name === plugin.name;
return has;
});
if (!has) {
global.LOCAL_PLUGINS.PLUGINS.unshift(plugin);
currentPlugins.unshift(plugin);
global.LOCAL_PLUGINS.PLUGINS = currentPlugins;
fs.writeFileSync(
configPath,
JSON.stringify(global.LOCAL_PLUGINS.PLUGINS)
JSON.stringify(currentPlugins)
);
}
},

View File

@ -49,6 +49,7 @@ export default () => {
window.removeBrowserView(view);
window.setSize(800, 60);
executeHooks("PluginOut", null);
window.webContents.executeJavaScript(`window.initRubick()`);
view = undefined;
};

View File

@ -0,0 +1,45 @@
import { globalShortcut, BrowserWindow, screen } from "electron";
export default (mainWindow: BrowserWindow): void => {
const config = global.OP_CONFIG.get();
globalShortcut.unregisterAll();
// 注册偏好快捷键
globalShortcut.register(config.perf.shortCut.showAndHidden, () => {
const { x, y } = screen.getCursorScreenPoint();
const currentDisplay = screen.getDisplayNearestPoint({ x, y });
const wx = parseInt(
String(
currentDisplay.workArea.x + currentDisplay.workArea.width / 2 - 400
)
);
const wy = parseInt(
String(
currentDisplay.workArea.y + currentDisplay.workArea.height / 2 - 200
)
);
mainWindow.setAlwaysOnTop(true);
mainWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
mainWindow.focus();
mainWindow.setVisibleOnAllWorkspaces(false, { visibleOnFullScreen: true });
mainWindow.setPosition(wx, wy);
mainWindow.show();
});
globalShortcut.register(config.perf.shortCut.separate, () => {
// todo
});
globalShortcut.register(config.perf.shortCut.quit, () => {
// mainWindow.webContents.send('init-rubick');
// mainWindow.show();
});
// 注册自定义全局快捷键
config.global.forEach((sc) => {
if (!sc.key || !sc.value) return;
globalShortcut.register(sc.key, () => {
mainWindow.webContents.send("global-short-key", sc.value);
});
});
};

75
src/main/common/tray.ts Normal file
View File

@ -0,0 +1,75 @@
import { dialog, Menu, Tray, app, shell, BrowserWindow } from "electron";
import path from "path";
import pkg from "../../../package.json";
import os from "os";
import commonConst from "@/common/utils/commonConst";
function createTray(window: BrowserWindow): Promise<Tray> {
return new Promise((resolve) => {
let icon;
if (commonConst.macOS()) {
icon = "./icon@3x.png";
} else if (commonConst.windows()) {
icon = parseInt(os.release()) < 10 ? "./icon@2x.png" : "./icon.ico";
} else {
icon = "icon@2x.png";
}
const appIcon = new Tray(path.join(__static, icon));
const contextMenu = Menu.buildFromTemplate([
{
label: "帮助文档",
click: () => {
process.nextTick(() => {
shell.openExternal("https://github.com/clouDr-f2e/rubick");
});
},
},
{
label: "意见反馈",
click: () => {
process.nextTick(() => {
shell.openExternal("https://github.com/clouDr-f2e/rubick/issues");
});
},
},
{ type: "separator" },
{
label: "显示窗口",
accelerator: "Alt+R",
click() {
window.show();
},
},
{
role: "quit",
label: "退出",
},
{
label: "重启",
click() {
app.relaunch();
app.quit();
},
},
{ type: "separator" },
{
label: "关于",
click() {
dialog.showMessageBox({
title: "拉比克",
message: "极简、插件化的现代桌面软件",
detail: `Version: ${pkg.version}\nAuthor: muwoo`,
});
},
},
]);
appIcon.on("click", () => {
appIcon.popUpContextMenu(contextMenu);
});
appIcon.setContextMenu(contextMenu);
resolve(appIcon);
});
}
export default createTray;

View File

@ -5,7 +5,11 @@ import commonConst from "../common/utils/commonConst";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import API from "./common/api";
import createTray from "./common/tray";
import registerHotKey from "./common/registerHotKey";
import "../common/utils/localPlugin";
import "../common/utils/localConfig";
class App {
private windowCreator: { init: () => void; getWindow: () => BrowserWindow };
@ -48,8 +52,8 @@ class App {
this.createWindow();
API(this.windowCreator.getWindow());
// this.init()
// createTray(this.windowCreator.getWindow())
// autoUpdate()
createTray(this.windowCreator.getWindow());
registerHotKey(this.windowCreator.getWindow());
};
if (!app.isReady()) {
app.on("ready", readyFunction);

View File

@ -9,6 +9,7 @@
@changeSelect="changeSelect"
:searchValue="searchValue"
:placeholder="placeholder"
@choosePlugin="choosePlugin"
/>
</div>
<Result
@ -56,6 +57,7 @@ getPluginInfo({
watch([searchValue], () => {
currentSelect.value = 0;
if (currentPlugin.value.name) return;
nextTick(() => {
ipcRenderer.sendSync("msg-trigger", {
type: "setExpendHeight",
@ -81,6 +83,12 @@ const openMenu = () => {
cmd: "插件市场",
});
};
const choosePlugin = () => {
const currentChoose = options.value[currentSelect.value];
console.log(currentChoose);
currentChoose.click();
};
</script>
<style lang="less">

View File

@ -7,13 +7,22 @@
@input="(e) => changeValue(e)"
@keydown.down="() => emit('changeCurrent', 1)"
@keydown.up="() => emit('changeCurrent', -1)"
@keydown="checkNeedInit"
@keydown="e => checkNeedInit(e)"
:value="searchValue"
:placeholder="placeholder || 'Hi, Rubick2'"
@keypress.enter="
(e) => targetSearch({ value: e.target.value, type: 'enter' })
"
@keypress.space="
(e) => targetSearch({ value: e.target.value, type: 'space' })
"
>
<template #suffix>
<div @click="() => emit('openMenu')" class="suffix-tool" >
<div class="rubick-logo">
<div v-if="currentPlugin && currentPlugin.logo" style="position: relative">
<img class="icon-tool" :src="currentPlugin.logo" />
</div>
<div v-else class="rubick-logo">
<img src="../assets/logo.png" />
</div>
</div>
@ -42,14 +51,31 @@ const changeValue = (e) => {
emit("onSearch", e);
};
const emit = defineEmits(["onSearch", "changeCurrent", "openMenu", "changeSelect"]);
const emit = defineEmits([
"onSearch",
"changeCurrent",
"openMenu",
"changeSelect",
"choosePlugin",
]);
const checkNeedInit = (e) => {
if (props.searchValue === "" && e.keyCode === 8) {
if (e.target.value === "" && e.keyCode === 8) {
closeTag();
}
};
const targetSearch = ({ value, type }) => {
if (props.currentPlugin.name) {
return ipcRenderer.sendSync("msg-trigger", {
type: "sendSubInputChangeEvent",
data: { text: value },
});
} else {
emit("choosePlugin");
}
};
const closeTag = () => {
emit("changeSelect", {});
ipcRenderer.send("msg-trigger", {
@ -95,7 +121,7 @@ const closeTag = () => {
border: none !important;
}
}
.rubick-logo {
.rubick-logo, .icon-tool {
width: 40px;
height: 40px;
background: #574778;
@ -107,6 +133,9 @@ const closeTag = () => {
width: 32px;
}
}
.icon-tool {
background: #fff;
}
.ant-input:focus {
border: none;
box-shadow: none;

View File

@ -3,6 +3,7 @@ import { nativeImage, remote, ipcRenderer } from "electron";
import { appSearch, PluginHandler } from "@/core";
import path from "path";
import commonConst from "@/common/utils/commonConst";
import { execSync } from "child_process";
import searchManager from "./search";
import optionsManager from "./options";
@ -43,12 +44,11 @@ const createPluginManager = (): any => {
})
),
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// document.getElementById("search").value = "";
// state.searchValue = "";
setSearchValue("");
}
if (plugin.pluginType === "app") {
execSync(plugin.action);
}
};
const { searchValue, onSearch, setSearchValue, placeholder } = searchManager();
@ -57,6 +57,7 @@ const createPluginManager = (): any => {
baseDir,
appList,
openPlugin,
currentPlugin: toRefs(state).currentPlugin,
});
// plugin operation
const getPluginInfo = async ({ pluginName, pluginPath }) => {
@ -85,6 +86,12 @@ const createPluginManager = (): any => {
remote.getGlobal("LOCAL_PLUGINS").updatePlugin(currentPlugin);
};
window.initRubick = () => {
state.currentPlugin = {};
setSearchValue("");
window.setSubInput({ placeholder: "" });
};
return {
...toRefs(state),
initPlugins,

View File

@ -12,12 +12,13 @@ function searchKeyValues(lists, value) {
});
}
const optionsManager = ({ searchValue, baseDir, appList, openPlugin }) => {
const optionsManager = ({ searchValue, baseDir, appList, openPlugin, currentPlugin }) => {
const optionsRef = ref([]);
watch(searchValue, () => search(searchValue.value));
// search Input operation
const search = throttle((value) => {
if (currentPlugin.value.name) return;
if (!value) return;
const localPlugins = remote.getGlobal("LOCAL_PLUGINS").getLocalPlugins();
let options: any = [];

View File

@ -11,10 +11,6 @@ const searchManager = () => {
const onSearch = (e) => {
const value = e.target.value;
state.searchValue = value;
ipcRenderer.sendSync("msg-trigger", {
type: "sendSubInputChangeEvent",
data: value,
});
};
const setSearchValue = (value: string) => {

View File

@ -18,4 +18,5 @@ interface Window {
setSubInputValue: ({ value }: { value: string }) => void;
removeSubInput: () => void;
updatePlugin: (plugin: any) => void;
initRubick: () => void;
}