mirror of
https://github.com/rubickCenter/rubick
synced 2025-07-18 22:05:05 +08:00
604 lines
16 KiB
Vue
604 lines
16 KiB
Vue
<template>
|
|
<div class="settings">
|
|
<div class="left-menu">
|
|
<a-menu v-model:selectedKeys="currentSelect" mode="inline">
|
|
<a-menu-item key="userInfo">
|
|
<template #icon>
|
|
<UserOutlined />
|
|
</template>
|
|
{{ $t('feature.settings.account.accountInfo') }}
|
|
</a-menu-item>
|
|
<a-menu-item key="normal">
|
|
<template #icon>
|
|
<ToolOutlined />
|
|
</template>
|
|
{{ $t('feature.settings.basic.title') }}
|
|
</a-menu-item>
|
|
<a-menu-item key="localstart">
|
|
<template #icon>
|
|
<FolderOpenOutlined />
|
|
</template>
|
|
{{ $t('feature.settings.localstart.title') }}
|
|
</a-menu-item>
|
|
<a-menu-item key="global">
|
|
<template #icon>
|
|
<LaptopOutlined />
|
|
</template>
|
|
{{ $t('feature.settings.global.title') }}
|
|
</a-menu-item>
|
|
<!-- <a-menu-item key="superpanel">-->
|
|
<!-- <template #icon>-->
|
|
<!-- <FileAddOutlined />-->
|
|
<!-- </template>-->
|
|
<!-- {{ $t('feature.settings.superPanel.title') }}-->
|
|
<!-- </a-menu-item>-->
|
|
<a-menu-item key="localhost">
|
|
<template #icon>
|
|
<DatabaseOutlined />
|
|
</template>
|
|
{{ $t('feature.settings.intranet.title') }}
|
|
</a-menu-item>
|
|
</a-menu>
|
|
</div>
|
|
<div class="settings-detail">
|
|
<UserInfo v-if="currentSelect[0] === 'userInfo'" />
|
|
<div v-if="currentSelect[0] === 'normal'">
|
|
<div class="setting-item">
|
|
<div class="title">
|
|
{{ $t('feature.settings.basic.shortcutKey') }}
|
|
</div>
|
|
<div class="settings-item-li">
|
|
<div class="label">
|
|
{{ $t('feature.settings.basic.showOrHiddle') }}
|
|
</div>
|
|
<a-tooltip placement="top" trigger="click">
|
|
<template #title>
|
|
<span>{{ tipText }}</span>
|
|
<template v-if="isWindows">
|
|
<br />
|
|
<span
|
|
style="cursor: pointer; text-decoration: underline"
|
|
@click="resetDefault('Alt')"
|
|
>
|
|
Alt+Space
|
|
</span>
|
|
<span
|
|
style="
|
|
cursor: pointer;
|
|
margin-left: 8px;
|
|
text-decoration: underline;
|
|
"
|
|
@click="resetDefault('Ctrl')"
|
|
>
|
|
Ctrl+Space
|
|
</span>
|
|
</template>
|
|
</template>
|
|
<div
|
|
class="value"
|
|
tabIndex="-1"
|
|
@keyup="(e) => changeShortCut(e, 'showAndHidden')"
|
|
>
|
|
{{ shortCut.showAndHidden }}
|
|
</div>
|
|
</a-tooltip>
|
|
</div>
|
|
<div class="settings-item-li">
|
|
<div class="label">
|
|
{{ $t('feature.settings.basic.screenCapture') }}
|
|
</div>
|
|
<a-tooltip placement="top" trigger="click">
|
|
<template #title>
|
|
<span>{{ tipText }}</span>
|
|
</template>
|
|
<div
|
|
class="value"
|
|
tabIndex="-1"
|
|
@keyup="(e) => changeShortCut(e, 'capture')"
|
|
>
|
|
{{ shortCut.capture }}
|
|
</div>
|
|
</a-tooltip>
|
|
</div>
|
|
</div>
|
|
<div class="setting-item">
|
|
<div class="title">{{ $t('feature.settings.basic.common') }}</div>
|
|
<div class="settings-item-li">
|
|
<div class="label">
|
|
{{ $t('feature.settings.basic.autoPaste') }}
|
|
</div>
|
|
<a-switch
|
|
v-model:checked="common.autoPast"
|
|
:checked-children="$t('feature.settings.basic.on')"
|
|
:un-checked-children="$t('feature.settings.basic.off')"
|
|
></a-switch>
|
|
</div>
|
|
<div class="settings-item-li">
|
|
<div class="label">{{ $t('feature.settings.basic.autoBoot') }}</div>
|
|
<a-switch
|
|
v-model:checked="common.start"
|
|
:checked-children="$t('feature.settings.basic.on')"
|
|
:un-checked-children="$t('feature.settings.basic.off')"
|
|
></a-switch>
|
|
</div>
|
|
<div class="settings-item-li">
|
|
<div class="label">
|
|
{{ $t('feature.settings.basic.spaceExec') }}
|
|
</div>
|
|
<a-switch
|
|
v-model:checked="common.space"
|
|
:checked-children="$t('feature.settings.basic.on')"
|
|
:un-checked-children="$t('feature.settings.basic.off')"
|
|
></a-switch>
|
|
</div>
|
|
</div>
|
|
<div class="setting-item">
|
|
<div class="title">{{ $t('feature.settings.basic.theme') }}</div>
|
|
<div class="settings-item-li">
|
|
<div class="label">{{ $t('feature.settings.basic.darkMode') }}</div>
|
|
<a-switch
|
|
v-model:checked="common.darkMode"
|
|
:checked-children="$t('feature.settings.basic.on')"
|
|
:un-checked-children="$t('feature.settings.basic.off')"
|
|
></a-switch>
|
|
</div>
|
|
</div>
|
|
<div class="setting-item">
|
|
<div class="title">{{ $t('feature.settings.basic.language') }}</div>
|
|
<div class="settings-item-li">
|
|
<div class="label">
|
|
{{ $t('feature.settings.basic.changeLang') }}
|
|
</div>
|
|
<a-select
|
|
v-model:value="state.common.lang"
|
|
label-in-value
|
|
style="width: 240px"
|
|
:options="options"
|
|
@change="changeLanguage"
|
|
></a-select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-if="currentSelect[0] === 'global'">
|
|
<a-collapse>
|
|
<a-collapse-panel
|
|
key="1"
|
|
:header="$t('feature.settings.global.instructions')"
|
|
>
|
|
<div>
|
|
{{ $t('feature.settings.global.tips') }}
|
|
</div>
|
|
<h3 style="margin-top: 10px">
|
|
{{ $t('feature.settings.global.example') }}
|
|
</h3>
|
|
<a-divider style="margin: 5px 0" />
|
|
<a-list item-layout="horizontal" :data-source="examples">
|
|
<template #renderItem="{ item }">
|
|
<a-list-item>
|
|
<a-list-item-meta :description="item.desc">
|
|
<template #title>
|
|
<div>{{ item.title }}</div>
|
|
</template>
|
|
</a-list-item-meta>
|
|
</a-list-item>
|
|
</template>
|
|
</a-list>
|
|
</a-collapse-panel>
|
|
</a-collapse>
|
|
<div class="feature-container">
|
|
<div class="keywords item">
|
|
<div>{{ $t('feature.settings.global.shortcutKey') }}</div>
|
|
<template :key="index" v-for="(item, index) in global">
|
|
<a-tooltip placement="top" trigger="click">
|
|
<template #title>
|
|
<span>{{ tipText }}</span>
|
|
</template>
|
|
<div
|
|
class="value"
|
|
tabIndex="2"
|
|
@keyup="(e) => changeGlobalKey(e, index)"
|
|
>
|
|
{{ item.key }}
|
|
<MinusCircleOutlined
|
|
@click.stop="deleteGlobalKey(e, index)"
|
|
/>
|
|
</div>
|
|
</a-tooltip>
|
|
</template>
|
|
</div>
|
|
<div class="short-cut item">
|
|
<div>{{ $t('feature.settings.global.funtionKey') }}</div>
|
|
<template v-for="(item, index) in global" :key="index">
|
|
<a-input
|
|
:value="item.value"
|
|
class="value"
|
|
allowClear
|
|
:disabled="!item.key"
|
|
@change="(e) => changeGlobalValue(index, e.target.value)"
|
|
/>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
<div @click="addConfig" class="add-global">
|
|
<PlusCircleOutlined />
|
|
{{ $t('feature.settings.global.addShortcutKey') }}
|
|
</div>
|
|
</div>
|
|
<SuperPanel v-if="currentSelect[0] === 'superpanel'" />
|
|
<Localhost v-if="currentSelect[0] === 'localhost'" />
|
|
<LocalStart v-if="currentSelect[0] === 'localstart'" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import {
|
|
ToolOutlined,
|
|
LaptopOutlined,
|
|
DatabaseOutlined,
|
|
MinusCircleOutlined,
|
|
PlusCircleOutlined,
|
|
UserOutlined,
|
|
FolderOpenOutlined,
|
|
} from '@ant-design/icons-vue';
|
|
import debounce from 'lodash.debounce';
|
|
import { ref, reactive, watch, toRefs, computed } from 'vue';
|
|
import keycodes from './keycode';
|
|
import Localhost from './localhost.vue';
|
|
import SuperPanel from './super-panel.vue';
|
|
import UserInfo from './user-info';
|
|
import LocalStart from './local-start';
|
|
import { useI18n } from 'vue-i18n';
|
|
import localConfig from '@/confOp';
|
|
|
|
const { locale, t } = useI18n();
|
|
|
|
const { ipcRenderer } = window.require('electron');
|
|
|
|
const examples = [
|
|
{
|
|
title: t('feature.settings.global.example1'),
|
|
desc: t('feature.settings.global.tips1'),
|
|
},
|
|
{
|
|
title: t('feature.settings.global.example2'),
|
|
desc: t('feature.settings.global.tips2'),
|
|
},
|
|
];
|
|
|
|
const state = reactive({
|
|
shortCut: {},
|
|
common: {},
|
|
local: {},
|
|
global: [],
|
|
custom: {},
|
|
});
|
|
|
|
const isWindows = window?.rubick?.isWindows();
|
|
const tipText = computed(() => {
|
|
const optionKeyName = isWindows ? 'Alt' : 'Option、Command';
|
|
return t('feature.settings.global.addShortcutKeyTips', {
|
|
optionKeyName: optionKeyName,
|
|
});
|
|
});
|
|
|
|
const currentSelect = ref(['userInfo']);
|
|
|
|
const { perf, global: defaultGlobal } = localConfig.getConfig();
|
|
|
|
state.shortCut = perf.shortCut;
|
|
state.custom = perf.custom;
|
|
state.common = perf.common;
|
|
state.local = perf.local;
|
|
state.global = defaultGlobal;
|
|
|
|
const setConfig = debounce(() => {
|
|
localConfig.setConfig(
|
|
JSON.parse(
|
|
JSON.stringify({
|
|
perf: {
|
|
shortCut: state.shortCut,
|
|
common: state.common,
|
|
local: state.local,
|
|
custom: state.custom,
|
|
},
|
|
global: state.global,
|
|
})
|
|
)
|
|
);
|
|
ipcRenderer.send('re-register');
|
|
}, 500);
|
|
|
|
watch(state, setConfig);
|
|
|
|
const changeShortCut = (e, key) => {
|
|
let compose = '';
|
|
// 添加是否包含功能键的判断
|
|
let incluFuncKeys = false;
|
|
if (e.ctrlKey && e.keyCode !== 17) {
|
|
compose += '+Ctrl';
|
|
incluFuncKeys = true;
|
|
}
|
|
if (e.shiftKey && e.keyCode !== 16) {
|
|
compose += '+Shift';
|
|
incluFuncKeys = true;
|
|
}
|
|
if (e.altKey && e.keyCode !== 18) {
|
|
compose += '+Option';
|
|
incluFuncKeys = true;
|
|
}
|
|
if (e.metaKey && e.keyCode !== 93) {
|
|
compose += '+Command';
|
|
incluFuncKeys = true;
|
|
}
|
|
compose += '+' + keycodes[e.keyCode].toUpperCase();
|
|
compose = compose.substring(1);
|
|
if (
|
|
incluFuncKeys &&
|
|
e.keyCode !== 16 &&
|
|
e.keyCode !== 17 &&
|
|
e.keyCode !== 18 &&
|
|
e.keyCode !== 93
|
|
) {
|
|
state.shortCut[key] = compose;
|
|
} else {
|
|
// 不做处理
|
|
}
|
|
};
|
|
|
|
const changeGlobalKey = (e, index) => {
|
|
let compose = '';
|
|
// 添加是否包含功能键的判断
|
|
let incluFuncKeys = false;
|
|
if (e.ctrlKey && e.keyCode !== 17) {
|
|
compose += '+Ctrl';
|
|
incluFuncKeys = true;
|
|
}
|
|
if (e.shiftKey && e.keyCode !== 16) {
|
|
compose += '+Shift';
|
|
incluFuncKeys = true;
|
|
}
|
|
if (e.altKey && e.keyCode !== 18) {
|
|
compose += '+Option';
|
|
incluFuncKeys = true;
|
|
}
|
|
if (e.metaKey && e.keyCode !== 93) {
|
|
compose += '+Command';
|
|
incluFuncKeys = true;
|
|
}
|
|
compose += '+' + keycodes[e.keyCode].toUpperCase();
|
|
compose = compose.substring(1);
|
|
if (
|
|
incluFuncKeys &&
|
|
e.keyCode !== 16 &&
|
|
e.keyCode !== 17 &&
|
|
e.keyCode !== 18 &&
|
|
e.keyCode !== 93
|
|
) {
|
|
state.global[index].key = compose;
|
|
} else {
|
|
// 不做处理
|
|
}
|
|
// f1 - f12
|
|
if (!incluFuncKeys && e.keyCode >= 112 && e.keyCode <= 123) {
|
|
compose = keycodes[e.keyCode].toUpperCase();
|
|
state.global[index].key = compose;
|
|
}
|
|
};
|
|
|
|
const resetDefault = (key) => {
|
|
switch (key) {
|
|
case 'Alt':
|
|
state.shortCut['showAndHidden'] = 'Option+SPACE';
|
|
// copyValue.value = "Option+SPACE";
|
|
break;
|
|
case 'Ctrl':
|
|
state.shortCut['showAndHidden'] = 'Ctrl+SPACE';
|
|
// copyValue.value = "Ctrl+SPACE";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
setConfig();
|
|
};
|
|
|
|
const changeGlobalValue = (index, value) => {
|
|
state.global[index].value = value;
|
|
};
|
|
|
|
const deleteGlobalKey = (e, index) => {
|
|
state.global.splice(index, 1);
|
|
// delete state.global[index];
|
|
};
|
|
|
|
const addConfig = () => {
|
|
state.global.push({
|
|
key: '',
|
|
value: '',
|
|
});
|
|
};
|
|
|
|
const { shortCut, common, local, global } = toRefs(state);
|
|
|
|
const options = ref([
|
|
{
|
|
value: 'zh-CN',
|
|
label: t('feature.settings.basic.cn'),
|
|
},
|
|
{
|
|
value: 'en-US',
|
|
label: t('feature.settings.basic.en'),
|
|
},
|
|
]);
|
|
|
|
const changeLanguage = (value) => {
|
|
state.common.lang = value.key;
|
|
locale.value = value.key;
|
|
};
|
|
</script>
|
|
|
|
<style lang="less">
|
|
@import '~@/assets/common.less';
|
|
|
|
.settings {
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
overflow-x: hidden;
|
|
background: var(--color-body-bg);
|
|
height: calc(~'100vh - 46px');
|
|
display: flex;
|
|
|
|
.ant-menu {
|
|
background: var(--color-body-bg) !important;
|
|
color: var(--color-text-content) !important;
|
|
}
|
|
|
|
.settings-detail {
|
|
padding: 20px;
|
|
box-sizing: border-box;
|
|
flex: 1;
|
|
overflow: auto;
|
|
height: 100%;
|
|
background: var(--color-body-bg);
|
|
|
|
.setting-item {
|
|
margin-bottom: 20px;
|
|
|
|
.ant-form-item {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.title {
|
|
color: var(--ant-primary-color);
|
|
font-size: 15px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.settings-item-li {
|
|
padding-left: 20px;
|
|
display: flex;
|
|
width: 100%;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 10px;
|
|
|
|
.label {
|
|
color: var(--color-text-content);
|
|
}
|
|
|
|
.value {
|
|
width: 300px;
|
|
cursor: pointer;
|
|
text-align: center;
|
|
border: 1px solid var(--color-border-light);
|
|
color: var(--ant-primary-color);
|
|
font-size: 14px;
|
|
height: 24px;
|
|
font-weight: lighter;
|
|
background: var(--color-input-hover);
|
|
.ant-input {
|
|
text-align: center;
|
|
color: var(--ant-primary-color);
|
|
font-size: 14px;
|
|
font-weight: lighter;
|
|
background: var(--color-input-hover);
|
|
}
|
|
}
|
|
|
|
.ant-switch {
|
|
&:not(.ant-switch-checked) {
|
|
background: var(--color-list-hover);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.feature-container {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-top: 10px;
|
|
font-size: 14px;
|
|
|
|
.item {
|
|
flex: 1;
|
|
color: var(--color-text-content);
|
|
}
|
|
|
|
.short-cut {
|
|
margin-left: 20px;
|
|
}
|
|
|
|
.value {
|
|
cursor: pointer;
|
|
text-align: center;
|
|
border: 1px solid var(--color-border-light);
|
|
color: var(--ant-primary-color);
|
|
font-size: 14px;
|
|
height: 24px;
|
|
font-weight: lighter;
|
|
margin-top: 10px;
|
|
position: relative;
|
|
background: var(--color-input-hover);
|
|
|
|
.ant-input {
|
|
color: var(--ant-primary-color);
|
|
font-weight: lighter;
|
|
background: none;
|
|
}
|
|
|
|
.anticon {
|
|
color: var(--color-text-desc);
|
|
}
|
|
|
|
&.ant-input-affix-wrapper {
|
|
display: flex;
|
|
}
|
|
|
|
&:hover {
|
|
.anticon {
|
|
display: block;
|
|
color: var(--color-text-content);
|
|
}
|
|
}
|
|
|
|
.anticon {
|
|
position: absolute;
|
|
display: none;
|
|
right: 4px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
}
|
|
}
|
|
}
|
|
|
|
.add-global {
|
|
color: var(--ant-primary-color);
|
|
margin-top: 20px;
|
|
width: 100%;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.ant-collapse {
|
|
background: var(--color-input-hover);
|
|
|
|
.ant-collapse-content {
|
|
background: var(--color-input-hover);
|
|
color: var(--color-text-content);
|
|
}
|
|
|
|
h3,
|
|
.ant-collapse-header,
|
|
.ant-list-item-meta-title {
|
|
color: var(--color-text-primary);
|
|
}
|
|
|
|
.ant-list-item-meta-description {
|
|
color: var(--color-text-desc);
|
|
}
|
|
}
|
|
}
|
|
</style>
|