特性-市场插件的国际化支持

This commit is contained in:
sunyuqiang 2023-08-01 14:01:19 +08:00
parent cc6098258c
commit a2f9e1d01c
26 changed files with 610 additions and 228 deletions

View File

@ -18,6 +18,7 @@
"markdown-it": "^12.2.0",
"nanoid": "^4.0.2",
"vue": "3.2.45",
"vue-i18n": "^9.2.2",
"vue-router": "^4.0.0-0",
"vuex": "^4.0.0-0"
},

View File

@ -10,25 +10,25 @@
<template #icon>
<AppstoreOutlined />
</template>
插件市场
{{ $t('feature.market.title') }}
</a-menu-item>
<a-menu-item key="installed">
<template #icon>
<HeartOutlined />
</template>
已安装
{{ $t('feature.installed.title') }}
</a-menu-item>
<a-menu-item key="settings">
<template #icon>
<SettingOutlined />
</template>
账户与设置
{{ $t('feature.settings.title') }}
</a-menu-item>
<a-menu-item key="dev">
<template #icon>
<BugOutlined />
</template>
开发者
{{ $t('feature.dev.title') }}
</a-menu-item>
</a-menu>
</div>
@ -37,18 +37,18 @@
</template>
<script setup lang="ts">
import { ref } from "vue";
import { useRouter } from "vue-router";
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import {
HeartOutlined,
UserOutlined,
AppstoreOutlined,
SettingOutlined,
BugOutlined
} from "@ant-design/icons-vue";
import { useStore } from "vuex";
BugOutlined,
} from '@ant-design/icons-vue';
import { useStore } from 'vuex';
const router = useRouter();
const active = ref(["market"]);
const active = ref(['market']);
const changeMenu = (key: any) => {
router.push(key);
};
@ -59,7 +59,7 @@ window.rubick.onPluginEnter(({ code }: { code: string }) => {
});
const store = useStore();
const init = () => store.dispatch("init");
const init = () => store.dispatch('init');
init();
</script>
<style lang="less" scoped>

View File

@ -1,6 +1,6 @@
.left-menu {
width: 200px;
height: 100vh;
// height: 100vh;
border-right: 1px solid var(--color-border-light);
.search-container {
padding: 10px;

View File

@ -0,0 +1,19 @@
import { createI18n } from 'vue-i18n';
import messages from './langs';
const { remote } = window.require('electron');
const { perf } = remote.getGlobal('OP_CONFIG').get();
console.log(messages);
console.log(perf);
// 2. Create i18n instance with options
const i18n = createI18n({
legacy: false,
locale: perf.common.lang || 'zh-CN', // set locale
fallbackLocale: 'zh-CN', // set fallback locale
messages, // set locale messages
// If you need to specify other options, you can set other options
// ...
});
export default i18n;

View File

@ -0,0 +1,110 @@
export default {
'en-US': {
feature: {
market: {
title: 'Market',
search: 'Search Plugins',
explore: 'Explore',
efficiency: 'Efficiency',
searchTool: 'Search Tools',
imageTool: 'Image Tools',
developTool: 'Develop Tools',
systemTool: 'System Tools',
finder: {
recommended: 'Recommended',
lastUpdated: 'Last Updated',
},
install: 'Install',
},
installed: {
title: 'Installed',
tips1: 'There are no plug-ins.',
tips2: 'Go to the plugin market and choose the plugin to install!',
developer: 'Developer',
unknown: 'Unknown',
remove: 'Remove',
functionKey: 'Function Key',
detailInfo: 'Detail Info',
addToPanel: 'Click the + sign to pin the keyword to the super panel',
removeFromPanel:
'Click the - sign to remove the keyword from the super panel',
},
settings: {
title: 'Account And Setting',
account: {
accountInfo: 'Account Info',
tips1: 'rubick',
tips2:
'After the software preferences are set, please restart the software. Please go to the mini program set avatar and nickname.',
themeColor: 'Theme Color',
primaryColor: 'Primary Color',
errorColor: 'Error Color',
warningColor: 'Warning Color',
successColor: 'Success Color',
infoColor: 'Info Color',
personalized: 'Personalized',
greeting: 'Search Box Greeting',
logo: 'Logo',
replace: 'Repalce Logo',
reset: 'Reset Default',
},
basic: {
title: 'Basic',
shortcutKey: 'Shortcut Key',
showOrHiddle: 'Show Or Hiddle',
screenCapture: 'Screen Capture',
common: 'Common',
autoPaste: 'Auto Paste',
autoBoot: 'Auto Boot Up',
spaceExec: 'Space Execution',
on: 'on',
off: 'off',
theme: 'Theme',
darkMode: 'Dark Mode',
language: 'Language',
changeLang: 'Change Language',
cn: 'Simplified Chinese',
en: 'English',
},
global: {
title: 'Global Shortcut Key',
instructions: 'Instructions and examples',
tips: 'Press the shortcut key, automatically search for the corresponding keyword, when the keyword result is exactly matched, and the result is unique, it will point directly to this function.',
example: 'Example',
example1: 'Shortcut key 「Alt + W」 keyword 「Wechat」',
tips1: 'Press 「Alt + W」 to open the local Wechat app directly',
example2: 'Shortcut key 「Ctrl + Alt + A」 keyword 「screenshot」',
tips2: 'Press 「Ctrl + Alt + A」 to take a screenshot',
shortcutKey: 'Shortcut Key',
funtionKey: 'Funtion Key',
addShortcutKey: 'ADD Global Shortcut Key',
addShortcutKeyTips:
'Press the function keys (Ctrl, Shift, {optionKeyName}) first, and then press other normal keys. Or press the F1-F12 button.',
},
superPanel: {
title: 'Super Panel',
tips: 'Please select the common plug-ins that need to be added to the super panel.',
add: 'Add',
remove: 'Revome',
},
intranet: {
title: 'Intranet Deployment',
tips: "If publishing plug-ins to the public network npm does not meet your company's security requirements, rubick supports private network private sources and private plug-in libraries. If you need private network deployment, you can configure the following rules.",
npmMirror: 'npm mirror',
dbUrl: 'database url',
accessToken: 'access token',
placeholder: 'required for private network gitlab warehouse',
},
},
dev: {
title: 'Developer',
tips: 'The rubick plug-in system relies on npm management. Local debugging needs to first execute npm link in the current directory of the local plug-in.',
pluginName: 'Plugin Name',
install: 'Install',
refreshPlugins: 'Refresh Plugins',
installSuccess: '{pluginName} Install Successed!',
refreshSuccess: '{pluginName} Refresh Successed!',
},
},
},
};

View File

@ -0,0 +1,9 @@
import en from './en-US';
import cn from './zh-CN';
const langs = {
...en,
...cn,
};
export default langs;

View File

@ -0,0 +1,108 @@
export default {
'zh-CN': {
feature: {
market: {
title: '插件市场',
search: '搜索插件',
explore: '探索',
efficiency: '效率',
searchTool: '搜索工具',
imageTool: '图像',
developTool: '开发者',
systemTool: '系统',
finder: {
recommended: '推荐',
lastUpdated: '最近更新',
},
install: '安装',
},
installed: {
title: '已安装',
tips1: '暂无任何插件',
tips2: '去插件市场选择安装合适的插件吧!',
developer: '开发者',
unknown: '未知',
remove: '移除',
functionKey: '功能关键字',
detailInfo: '详情介绍',
addToPanel: '点击+号,固定关键词到超级面板',
removeFromPanel: '点击-号,从超级面板移除关键词',
},
settings: {
title: '账户和设置',
account: {
accountInfo: '账户信息',
tips1: 'rubick 用户',
tips2: '软件偏好设置完成后需重启软件,头像和昵称请前往小程序设置',
themeColor: '主题色设置',
primaryColor: '主色调',
errorColor: '错误色',
warningColor: '警告色',
successColor: '成功色',
infoColor: '提醒色',
personalized: '用户个性化设置',
greeting: '主搜索框欢迎语',
logo: '界面 logo',
replace: '点我替换',
reset: '恢复默认设置',
},
basic: {
title: '基本设置',
shortcutKey: '快捷键',
showOrHiddle: '显示/隐藏快捷键',
screenCapture: '截屏',
common: '通用',
autoPaste: '输入框自动粘贴',
autoBoot: '开机启动',
spaceExec: '空格执行',
on: '开',
off: '关',
theme: '主题',
darkMode: '暗黑模式',
language: '语言',
changeLang: '切换语言',
cn: '简体中文',
en: '英文',
},
global: {
title: '全局快捷键',
instructions: '说明及示例',
tips: '按下快捷键,自动搜索对应关键字,当关键字结果完全匹配,且结果唯一时,会直接指向该功能。',
example: '示例',
example1: '快捷键 「 Alt + W」 关键字 「 微信」',
tips1: '按下Alt + W 直接打开本地微信应用',
example2: '快捷键 「 Ctrl + Alt + A」 关键字 「 截屏」',
tips2: '按下 Ctrl + Alt + A 进行截屏',
shortcutKey: '快捷键',
funtionKey: '功能关键字',
addShortcutKey: '新增全局快捷功能',
addShortcutKeyTips:
'先按功能键Ctrl、Shift、{optionKeyName}),再按其他普通键。或按 F1-F12 单键。',
},
superPanel: {
title: '超级面板设置',
tips: '请选择需要添加到超级面板中的常用插件',
add: '添加',
remove: '移除',
},
intranet: {
title: '内网部署配置',
tips: '把插件发布到公网 npm 如果不符合您的公司安全要求rubick 支持内网私有源和私有插件库,如果您需要内网部署使用,可以自行配置以下规则。',
npmMirror: 'npm 源',
dbUrl: 'database url',
accessToken: 'access token',
placeholder: '内网gitlab仓库必填',
},
},
dev: {
title: '开发者',
tips: 'rubick 插件系统依托于 npm 管理,本地调试需要先在本地插件当前目录执行 npm link',
pluginName: '插件名称',
install: '安装',
refreshPlugins: '刷新插件',
installSuccess: '{pluginName}安装成功!',
refreshSuccess: '{pluginName}刷新成功!',
},
},
},
};

View File

@ -5,6 +5,7 @@ import router from './router';
import store from './store';
import './assets/ant-reset.less';
import 'ant-design-vue/dist/antd.variable.min.css';
import registerI18n from './languages/i18n';
const { remote } = window.require('electron');
@ -14,4 +15,4 @@ ConfigProvider.config({
theme: perf.custom || {},
});
createApp(App).use(store).use(Antd).use(router).mount('#app');
createApp(App).use(registerI18n).use(store).use(Antd).use(router).mount('#app');

View File

@ -39,7 +39,7 @@ const visible = ref(false);
const showModal = () => {
visible.value = true;
if (!imgCode.value && !userInfo.value) {
service.getScanCode({ scene }).then(res => {
service.getScanCode({ scene }).then((res) => {
imgCode.value = `data:image/png;base64,${res.dataUrl}`;
});
}
@ -66,7 +66,6 @@ watch([visible], () => {
timer = null;
}
});
</script>
<style lang="less" scoped>
@ -75,7 +74,7 @@ watch([visible], () => {
width: 100%;
overflow-x: hidden;
background: var(--color-body-bg);
height: calc(~"100vh - 46px");
height: calc(~'100vh - 46px');
:deep(.ant-result-title) {
color: var(--color-text-primary);
}

View File

@ -1,6 +1,10 @@
<template>
<div class="dev">
<a-alert style="margin-bottom: 40px;" message="rubick 插件系统依托于 npm 管理,本地调试需要先在本地插件当前目录执行 npm link" type="warning" />
<a-alert
style="margin-bottom: 40px"
:message="$t('feature.dev.tips')"
type="warning"
/>
<a-form
ref="formRef"
:model="formState"
@ -8,21 +12,27 @@
:label-col="labelCol"
:wrapper-col="wrapperCol"
>
<a-form-item label="插件名称" name="name">
<a-form-item :label="$t('feature.dev.pluginName')" name="name">
<a-input v-model:value="formState.name" />
</a-form-item>
<a-form-item :wrapper-col="{ span: 14, offset: 4 }">
<a-button :loading="loading" type="primary" @click="onSubmit">安装</a-button>
<a-button @click="refresh" style="margin-left: 10px;">刷新插件</a-button>
<a-button :loading="loading" type="primary" @click="onSubmit">
{{ $t('feature.dev.install') }}
</a-button>
<a-button @click="refresh" style="margin-left: 10px">
{{ $t('feature.dev.refreshPlugins') }}
</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { reactive, ref } from "vue";
import { message } from "ant-design-vue";
import { reactive, ref } from 'vue';
import { message } from 'ant-design-vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const formRef = ref();
const formState = reactive({
@ -31,7 +41,7 @@ const formState = reactive({
const rules = {
name: {
required: true,
message: "Please input name",
message: 'Please input name',
},
};
const onSubmit = () => {
@ -47,7 +57,7 @@ const downloadPlugin = async (pluginName) => {
name: pluginName,
isDev: true,
});
message.success(`${pluginName}安装成功!`);
message.success(t('feature.dev.installSuccess', { pluginName: pluginName }));
loading.value = false;
};
@ -56,7 +66,9 @@ const refresh = () => {
window.market.refreshPlugin({
name: formState.name,
});
message.success(`${formState.name}刷新成功!`);
message.success(
t('feature.dev.refreshSuccess', { pluginName: formState.name })
);
});
};
@ -70,7 +82,7 @@ const wrapperCol = { span: 14 };
width: 100%;
overflow-x: hidden;
background: var(--color-body-bg);
height: calc(~"100vh - 46px");
height: calc(~'100vh - 46px');
padding: 20px;
:deep(label) {
color: var(--color-text-content);

View File

@ -3,8 +3,8 @@
<div v-if="!localPlugins.length">
<a-result
status="404"
title="暂无任何插件"
sub-title="去插件市场选择安装合适的插件吧!"
:title="$t('feature.installed.tips1')"
:sub-title="$t('feature.installed.tips2')"
/>
</div>
<div class="container" v-else>
@ -33,7 +33,9 @@
<a-tag>{{ pluginDetail.version }}</a-tag>
</div>
<div class="desc">
开发者{{ `${pluginDetail.author || '未知'}` }}
{{ $t('feature.installed.developer') }}{{
`${pluginDetail.author || $t('feature.installed.unknown')}`
}}
</div>
<div class="desc">
{{ pluginDetail.description }}
@ -47,12 +49,12 @@
:loading="pluginDetail.isloading"
@click="deletePlugin(pluginDetail)"
>
移除
{{ $t('feature.installed.remove') }}
</a-button>
</div>
</div>
<a-tabs default-active-key="1">
<a-tab-pane key="1" tab="功能关键字">
<a-tab-pane key="1" :tab="$t('feature.installed.functionKey')">
<div class="feature-container">
<div
class="desc-item"
@ -80,7 +82,7 @@
<a-tooltip
v-if="!hasAdded(cmd)"
placement="topLeft"
title="点击+号,固定关键词到超级面板"
:title="$t('feature.installed.addToPanel')"
>
<PlusCircleOutlined
@click="addCmdToSuperPanel({ code: item.code, cmd })"
@ -89,7 +91,7 @@
<a-tooltip
v-else
placement="topLeft"
title="点击-号,从超级面板移除关键词"
:title="$t('feature.installed.removeFromPanel')"
>
<MinusCircleOutlined
@click="removePluginToSuperPanel(cmd)"
@ -100,7 +102,7 @@
</div>
</div>
</a-tab-pane>
<a-tab-pane key="2" tab="详情介绍">
<a-tab-pane key="2" :tab="$t('feature.installed.detailInfo')">
<div class="detail-container" v-html="readme"></div>
</a-tab-pane>
</a-tabs>

View File

@ -3,18 +3,18 @@
<PluginList
v-if="dev && !!dev.length"
@downloadSuccess="downloadSuccess"
title="开发"
:title="$t('feature.market.developTool')"
:list="dev"
/>
</div>
</template>
<script setup>
import { ref, computed, onBeforeMount } from "vue";
import request from "../../../assets/request/index";
import PluginList from "./plugin-list.vue";
import { ref, computed, onBeforeMount } from 'vue';
import request from '../../../assets/request/index';
import PluginList from './plugin-list.vue';
import { useStore } from "vuex";
import { useStore } from 'vuex';
const store = useStore();
const totalPlugins = computed(() => store.state.totalPlugins);

View File

@ -11,34 +11,31 @@
<right-circle-outlined />
</div>
</template>
<div :key="index" v-for="(banner, index) in (data.banners || [])">
<div :key="index" v-for="(banner, index) in data.banners || []">
<img @click="jumpTo(banner.link)" width="100%" :src="banner.src" />
</div>
</a-carousel>
<PluginList
v-if="recommend && !!recommend.length"
@downloadSuccess="downloadSuccess"
title="推荐"
:title="$t('feature.market.finder.recommended')"
:list="recommend"
/>
<PluginList
v-if="newList && !!newList.length"
title="最近更新"
:title="$t('feature.market.finder.lastUpdated')"
:list="newList"
/>
</div>
</template>
<script setup>
import {
LeftCircleOutlined,
RightCircleOutlined,
} from "@ant-design/icons-vue";
import { ref, computed, onBeforeMount } from "vue";
import request from "../../../assets/request/index";
import PluginList from "./plugin-list.vue";
import { LeftCircleOutlined, RightCircleOutlined } from '@ant-design/icons-vue';
import { ref, computed, onBeforeMount } from 'vue';
import request from '../../../assets/request/index';
import PluginList from './plugin-list.vue';
import { useStore } from "vuex";
import { useStore } from 'vuex';
const store = useStore();
const totalPlugins = computed(() => store.state.totalPlugins);
@ -75,7 +72,6 @@ const newList = computed(() => {
return searchInfo;
});
});
</script>
<style lang="less">

View File

@ -3,18 +3,18 @@
<PluginList
v-if="system && !!system.length"
@downloadSuccess="downloadSuccess"
title="系统插件"
:title="$t('feature.market.imageTool')"
:list="system"
/>
</div>
</template>
<script setup>
import { ref, computed, onBeforeMount } from "vue";
import request from "../../../assets/request/index";
import PluginList from "./plugin-list.vue";
import { ref, computed, onBeforeMount } from 'vue';
import request from '../../../assets/request/index';
import PluginList from './plugin-list.vue';
import { useStore } from "vuex";
import { useStore } from 'vuex';
const store = useStore();
const totalPlugins = computed(() => store.state.totalPlugins);

View File

@ -2,7 +2,10 @@
<div class="panel-item">
<h3 class="title">{{ title }}</h3>
<div class="list-item">
<a-list :grid="{ gutter: 16, column: 2 }" :data-source="list.filter(item => !!item)">
<a-list
:grid="{ gutter: 16, column: 2 }"
:data-source="list.filter((item) => !!item)"
>
<template #renderItem="{ item, index }">
<a-list-item v-if="item" @click="showDetail(item)">
<template #actions>
@ -70,7 +73,7 @@
v-show="!detail.isloading && !detail.isdownload"
/>
</template>
获取
{{ $t('feature.market.install') }}
</a-button>
</div>
</div>
@ -83,44 +86,46 @@
<script setup>
import {
CloudDownloadOutlined,
ArrowLeftOutlined
} from "@ant-design/icons-vue";
ArrowLeftOutlined,
} from '@ant-design/icons-vue';
import { defineProps, ref } from "vue";
import { useStore } from "vuex";
import { message } from "ant-design-vue";
import MarkdownIt from "markdown-it";
import request from "../../../assets/request/index";
import { defineProps, ref } from 'vue';
import { useStore } from 'vuex';
import { message } from 'ant-design-vue';
import MarkdownIt from 'markdown-it';
import request from '../../../assets/request/index';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const store = useStore();
const startDownload = name => store.dispatch("startDownload", name);
const successDownload = name => store.dispatch("successDownload", name);
const startDownload = (name) => store.dispatch('startDownload', name);
const successDownload = (name) => store.dispatch('successDownload', name);
defineProps({
list: {
type: [Array],
default: () => []
default: () => [],
},
title: String
title: String,
});
const downloadPlugin = async plugin => {
const downloadPlugin = async (plugin) => {
startDownload(plugin.name);
await window.market.downloadPlugin(plugin);
message.success(`${plugin.name}安装成功!`);
message.success(t('feature.dev.installSuccess', { pluginName: plugin.name }));
successDownload(plugin.name);
};
const visible = ref(false);
const detail = ref({});
const markdown = new MarkdownIt();
const content = ref("");
const content = ref('');
const showDetail = async item => {
const showDetail = async (item) => {
visible.value = true;
detail.value = item;
let mdContent = "暂无内容";
let mdContent = '暂无内容';
if (item.homePage) {
mdContent = await request.getPluginDetail(item.homePage);
}
@ -153,7 +158,7 @@ const showDetail = async item => {
}
}
&:after {
content: " ";
content: ' ';
display: block;
width: 100%;
height: 1px;
@ -175,7 +180,6 @@ const showDetail = async item => {
box-shadow: none !important;
.ant-drawer-content {
background: var(--color-body-bg);
}
.ant-drawer-header {
background: var(--color-body-bg);

View File

@ -3,18 +3,18 @@
<PluginList
v-if="system && !!system.length"
@downloadSuccess="downloadSuccess"
title="系统插件"
:title="$t('feature.market.systemTool')"
:list="system"
/>
</div>
</template>
<script setup>
import { ref, computed, onBeforeMount } from "vue";
import request from "../../../assets/request/index";
import PluginList from "./plugin-list.vue";
import { ref, computed, onBeforeMount } from 'vue';
import request from '../../../assets/request/index';
import PluginList from './plugin-list.vue';
import { useStore } from "vuex";
import { useStore } from 'vuex';
const store = useStore();
const totalPlugins = computed(() => store.state.totalPlugins);

View File

@ -3,18 +3,18 @@
<PluginList
v-if="tools && !!tools.length"
@downloadSuccess="downloadSuccess"
title="搜索工具"
:title="$t('feature.market.searchTool')"
:list="tools"
/>
</div>
</template>
<script setup>
import { ref, computed, onBeforeMount } from "vue";
import request from "../../../assets/request/index";
import PluginList from "./plugin-list.vue";
import { ref, computed, onBeforeMount } from 'vue';
import request from '../../../assets/request/index';
import PluginList from './plugin-list.vue';
import { useStore } from "vuex";
import { useStore } from 'vuex';
const store = useStore();
const totalPlugins = computed(() => store.state.totalPlugins);

View File

@ -3,18 +3,18 @@
<PluginList
v-if="system && !!system.length"
@downloadSuccess="downloadSuccess"
title="生产效率"
:title="$t('feature.market.efficiency')"
:list="system"
/>
</div>
</template>
<script setup>
import { ref, computed, onBeforeMount } from "vue";
import request from "../../../assets/request/index";
import PluginList from "./plugin-list.vue";
import { ref, computed, onBeforeMount } from 'vue';
import request from '../../../assets/request/index';
import PluginList from './plugin-list.vue';
import { useStore } from "vuex";
import { useStore } from 'vuex';
const store = useStore();
const totalPlugins = computed(() => store.state.totalPlugins);

View File

@ -4,7 +4,7 @@
<div class="search-container">
<a-input-search
v-model:value="searchValue"
placeholder="搜索插件"
:placeholder="$t('feature.market.search')"
style="width: 100%"
@search="onSearch"
/>
@ -15,37 +15,37 @@
<template #icon>
<StarOutlined />
</template>
探索
{{ $t('feature.market.explore') }}
</a-menu-item>
<a-menu-item key="worker">
<template #icon>
<SendOutlined style="transform: rotate(-45deg)" />
</template>
效率
{{ $t('feature.market.efficiency') }}
</a-menu-item>
<a-menu-item key="tools">
<template #icon>
<SearchOutlined />
</template>
搜索工具
{{ $t('feature.market.searchTool') }}
</a-menu-item>
<a-menu-item key="image">
<template #icon>
<FileImageOutlined />
</template>
图像
{{ $t('feature.market.imageTool') }}
</a-menu-item>
<a-menu-item key="dev">
<template #icon>
<CodeOutlined />
</template>
开发
{{ $t('feature.market.developTool') }}
</a-menu-item>
<a-menu-item key="system">
<template #icon>
<DatabaseOutlined />
</template>
系统
{{ $t('feature.market.systemTool') }}
</a-menu-item>
</a-menu>
</div>
@ -63,15 +63,15 @@ import {
FileImageOutlined,
DatabaseOutlined,
CodeOutlined,
} from "@ant-design/icons-vue";
import { reactive, toRefs, computed } from "vue";
import { useStore } from "vuex";
import Finder from "./components/finder.vue";
import System from "./components/system.vue";
import Worker from "./components/worker.vue";
import Tools from "./components/tools.vue";
import Dev from "./components/devlopment.vue";
import Image from "./components/image.vue";
} from '@ant-design/icons-vue';
import { reactive, toRefs, computed } from 'vue';
import { useStore } from 'vuex';
import Finder from './components/finder.vue';
import System from './components/system.vue';
import Worker from './components/worker.vue';
import Tools from './components/tools.vue';
import Dev from './components/devlopment.vue';
import Image from './components/image.vue';
const Components = {
finder: Finder,
@ -83,8 +83,8 @@ const Components = {
};
const state = reactive({
searchValue: "",
current: ["finder"],
searchValue: '',
current: ['finder'],
});
const store = useStore();
@ -103,7 +103,7 @@ const { searchValue, current } = toRefs(state);
width: 100%;
overflow: hidden;
background: var(--color-menu-bg);
height: calc(~"100vh - 46px");
height: calc(~'100vh - 46px');
.container {
background: var(--color-body-bg);
width: calc(~'100% - 200px');

View File

@ -4,47 +4,52 @@
<a-menu v-model:selectedKeys="currentSelect" mode="inline">
<a-menu-item key="userInfo">
<template #icon>
<UserOutlined/>
<UserOutlined />
</template>
账户信息
{{ $t('feature.settings.account.accountInfo') }}
</a-menu-item>
<a-menu-item key="normal">
<template #icon>
<ToolOutlined/>
<ToolOutlined />
</template>
基本设置
{{ $t('feature.settings.basic.title') }}
</a-menu-item>
<a-menu-item key="global">
<template #icon>
<LaptopOutlined/>
<LaptopOutlined />
</template>
全局快捷键
{{ $t('feature.settings.global.title') }}
</a-menu-item>
<a-menu-item key="superpanel">
<template #icon>
<FileAddOutlined/>
<FileAddOutlined />
</template>
超级面板设置
{{ $t('feature.settings.superPanel.title') }}
</a-menu-item>
<a-menu-item key="localhost">
<template #icon>
<DatabaseOutlined/>
<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">快捷键</div>
<div class="title">
{{ $t('feature.settings.basic.shortcutKey') }}
</div>
<div class="settings-item-li">
<div class="label">显示/隐藏快捷键</div>
<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/>
<br />
<span
style="cursor: pointer; text-decoration: underline"
@click="resetDefault('Alt')"
@ -73,7 +78,9 @@
</a-tooltip>
</div>
<div class="settings-item-li">
<div class="label">截屏</div>
<div class="label">
{{ $t('feature.settings.basic.screenCapture') }}
</div>
<a-tooltip placement="top" trigger="click">
<template #title>
<span>{{ tipText }}</span>
@ -89,52 +96,76 @@
</div>
</div>
<div class="setting-item">
<div class="title">通用</div>
<div class="title">{{ $t('feature.settings.basic.common') }}</div>
<div class="settings-item-li">
<div class="label">输入框自动粘贴</div>
<div class="label">
{{ $t('feature.settings.basic.autoPaste') }}
</div>
<a-switch
v-model:checked="common.autoPast"
checked-children=""
un-checked-children=""
: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">开机启动</div>
<div class="label">{{ $t('feature.settings.basic.autoBoot') }}</div>
<a-switch
v-model:checked="common.start"
checked-children=""
un-checked-children=""
: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">空格执行</div>
<div class="label">
{{ $t('feature.settings.basic.spaceExec') }}
</div>
<a-switch
v-model:checked="common.space"
checked-children=""
un-checked-children=""
: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">主题</div>
<div class="title">{{ $t('feature.settings.basic.theme') }}</div>
<div class="settings-item-li">
<div class="label">暗黑模式</div>
<div class="label">{{ $t('feature.settings.basic.darkMode') }}</div>
<a-switch
v-model:checked="common.darkMode"
checked-children=""
un-checked-children=""
: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="说明及示例">
<a-collapse-panel
key="1"
:header="$t('feature.settings.global.instructions')"
>
<div>
按下快捷键自动搜索对应关键字当关键字结果完全匹配且结果唯一时会直接指向该功能
{{ $t('feature.settings.global.tips') }}
</div>
<h3 style="margin-top: 10px">示例</h3>
<a-divider style="margin: 5px 0"/>
<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>
@ -150,11 +181,11 @@
</a-collapse>
<div class="feature-container">
<div class="keywords item">
<div>快捷键</div>
<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 }}或按 F1-F12 单键</span>
<span>{{ tipText }}</span>
</template>
<div
class="value"
@ -170,7 +201,7 @@
</template>
</div>
<div class="short-cut item">
<div>功能关键字</div>
<div>{{ $t('feature.settings.global.funtionKey') }}</div>
<template v-for="(item, index) in global" :key="index">
<a-input
:value="item.value"
@ -183,13 +214,12 @@
</div>
</div>
<div @click="addConfig" class="add-global">
<PlusCircleOutlined/>
新增全局快捷功能
<PlusCircleOutlined />
{{ $t('feature.settings.global.addShortcutKey') }}
</div>
</div>
<Localhost v-if="currentSelect[0] === 'localhost'"/>
<SuperPanel v-if="currentSelect[0] === 'superpanel'"/>
<UserInfo v-if="currentSelect[0] === 'userInfo'"/>
<SuperPanel v-if="currentSelect[0] === 'superpanel'" />
<Localhost v-if="currentSelect[0] === 'localhost'" />
</div>
</div>
</template>
@ -205,26 +235,24 @@ import {
UserOutlined,
} from '@ant-design/icons-vue';
import debounce from 'lodash.debounce';
import {ref, reactive, watch, toRefs, computed, toRaw} from 'vue';
import { ref, reactive, watch, toRefs, computed, onMounted, toRaw } from 'vue';
import keycodes from './keycode';
import Localhost from './localhost.vue';
import SuperPanel from './super-panel.vue';
import UserInfo from './user-info';
import { useI18n } from 'vue-i18n';
const { locale, t } = useI18n();
const {remote, ipcRenderer} = window.require('electron');
const { remote, ipcRenderer } = window.require('electron');
const examples = [
{
title: '快捷键 「 Alt + W」 关键字 「 微信」',
desc: '按下Alt + W 直接打开本地微信应用',
title: t('feature.settings.global.example1'),
desc: t('feature.settings.global.tips1'),
},
{
title: '快捷键 「 Alt + Q」 关键字 「 取色」',
desc: '按下Alt + Q 直接打开屏幕取色功能',
},
{
title: '快捷键 「 Ctrl + Alt + Q」 关键字 「 截屏」',
desc: '按下 Ctrl + Alt + Q 进行截屏',
title: t('feature.settings.global.example2'),
desc: t('feature.settings.global.tips2'),
},
];
@ -239,12 +267,14 @@ const state = reactive({
const isWindows = window?.rubick?.isWindows();
const tipText = computed(() => {
const optionKeyName = isWindows ? 'Alt' : 'Option、Command';
return `先按功能键Ctrl、Shift、${optionKeyName}),再按其他普通键。`;
return t('feature.settings.global.addShortcutKeyTips', {
optionKeyName: optionKeyName,
});
});
const currentSelect = ref(['userInfo']);
const {perf, global: defaultGlobal} = remote.getGlobal('OP_CONFIG').get();
const { perf, global: defaultGlobal } = remote.getGlobal('OP_CONFIG').get();
state.shortCut = perf.shortCut;
state.custom = perf.custom;
@ -292,8 +322,14 @@ const changeShortCut = (e, key) => {
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) {
compose = compose.substring(1);
if (
incluFuncKeys &&
e.keyCode !== 16 &&
e.keyCode !== 17 &&
e.keyCode !== 18 &&
e.keyCode !== 93
) {
state.shortCut[key] = compose;
} else {
//
@ -321,8 +357,14 @@ const changeGlobalKey = (e, index) => {
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) {
compose = compose.substring(1);
if (
incluFuncKeys &&
e.keyCode !== 16 &&
e.keyCode !== 17 &&
e.keyCode !== 18 &&
e.keyCode !== 93
) {
state.global[index].key = compose;
} else {
//
@ -366,7 +408,23 @@ const addConfig = () => {
});
};
const {shortCut, common, local, global} = toRefs(state);
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">
@ -379,12 +437,12 @@ const {shortCut, common, local, global} = toRefs(state);
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;
@ -392,20 +450,20 @@ const {shortCut, common, local, global} = toRefs(state);
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;
@ -413,11 +471,11 @@ const {shortCut, common, local, global} = toRefs(state);
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
.label {
color: var(--color-text-content);
}
.value {
width: 300px;
cursor: pointer;
@ -427,7 +485,7 @@ const {shortCut, common, local, global} = toRefs(state);
font-size: 14px;
height: 24px;
font-weight: lighter;
.ant-input {
text-align: center;
color: var(--ant-primary-color);
@ -435,7 +493,7 @@ const {shortCut, common, local, global} = toRefs(state);
font-weight: lighter;
}
}
.ant-switch {
&:not(.ant-switch-checked) {
background: var(--color-list-hover);
@ -444,23 +502,23 @@ const {shortCut, common, local, global} = toRefs(state);
}
}
}
.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;
@ -472,28 +530,28 @@ const {shortCut, common, local, global} = toRefs(state);
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;
@ -503,7 +561,7 @@ const {shortCut, common, local, global} = toRefs(state);
}
}
}
.add-global {
color: var(--ant-primary-color);
margin-top: 20px;
@ -511,21 +569,21 @@ const {shortCut, common, local, global} = toRefs(state);
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);
}

View File

@ -1,6 +1,6 @@
<template>
<a-alert
message="把插件发布到公网 npm 如果不符合您的公司安全要求rubick 支持内网私有源和私有插件库,如果您需要内网部署使用,可以自行配置以下规则。"
:message="$t('feature.settings.intranet.tips')"
type="warning"
style="margin-bottom: 20px"
/>
@ -11,21 +11,33 @@
:rules="rules"
v-bind="layout"
>
<a-form-item has-feedback label="npm 源" name="register">
<a-form-item
has-feedback
:label="$t('feature.settings.intranet.npmMirror')"
name="register"
>
<a-input
placeholder="https://registry.npm.taobao.org"
v-model:value="formState.register"
/>
</a-form-item>
<a-form-item has-feedback label="database url" name="database">
<a-form-item
has-feedback
:label="$t('feature.settings.intranet.dbUrl')"
name="database"
>
<a-input
placeholder="https://gitcode.net/rubickcenter/rubick-database/-/raw/master"
v-model:value="formState.database"
/>
</a-form-item>
<a-form-item has-feedback label="access_token" name="access_token">
<a-form-item
has-feedback
:label="$t('feature.settings.intranet.accessToken')"
name="access_token"
>
<a-input
placeholder="内网gitlab仓库必填"
:placeholder="$t('feature.settings.intranet.placeholder')"
v-model:value="formState.access_token"
/>
</a-form-item>

View File

@ -1,5 +1,5 @@
<template>
<p>请选择需要添加到超级面板中的常用插件</p>
<p>{{ $t('feature.settings.superPanel.tips') }}</p>
<div class="super-list-item panel-item">
<a-list
:grid="{ gutter: 16, column: 2 }"
@ -14,7 +14,7 @@
style="color: #7ec699"
type="text"
>
添加
{{ $t('feature.settings.superPanel.add') }}
</a-button>
<a-button
v-else
@ -22,7 +22,7 @@
style="color: #ff4ea4"
type="text"
>
移除
{{ $t('feature.settings.superPanel.remove') }}
</a-button>
</template>
<a-list-item-meta>

View File

@ -3,8 +3,8 @@
<div class="info-container">
<a-result
class="user-info-result"
:title="userInfo.name || 'rubick 用户'"
sub-title="软件偏好设置完成后需重启软件,头像和昵称请前往小程序设置"
:title="userInfo.name || $t('feature.settings.account.tips1')"
:sub-title="$t('feature.settings.account.tips2')"
>
<template #icon>
<a-avatar :size="64" v-if="!userInfo.avatar">
@ -16,74 +16,123 @@
</div>
<div class="settings-container">
<div class="setting-item">
<div class="title">主题色设置</div>
<div class="title">
{{ $t('feature.settings.account.themeColor') }}
</div>
<div class="settings-item-li">
<div class="label">主色调</div>
<div class="label">
{{ $t('feature.settings.account.primaryColor') }}
</div>
<a-input v-model:value="custom.primaryColor" class="value">
<template #prefix>
<div :style="{ background: custom.primaryColor, width: '10px', height: '10px' }"></div>
<div
:style="{
background: custom.primaryColor,
width: '10px',
height: '10px',
}"
></div>
</template>
</a-input>
</div>
<div class="settings-item-li">
<div class="label">错误色</div>
<div class="label">
{{ $t('feature.settings.account.errorColor') }}
</div>
<a-input v-model:value="custom.errorColor" class="value">
<template #prefix>
<div :style="{ background: custom.errorColor, width: '10px', height: '10px' }"></div>
<div
:style="{
background: custom.errorColor,
width: '10px',
height: '10px',
}"
></div>
</template>
</a-input>
</div>
<div class="settings-item-li">
<div class="label">警告色</div>
<div class="label">
{{ $t('feature.settings.account.warningColor') }}
</div>
<a-input v-model:value="custom.warningColor" class="value">
<template #prefix>
<div :style="{ background: custom.warningColor, width: '10px', height: '10px' }"></div>
<div
:style="{
background: custom.warningColor,
width: '10px',
height: '10px',
}"
></div>
</template>
</a-input>
</div>
<div class="settings-item-li">
<div class="label">成功色</div>
<div class="label">
{{ $t('feature.settings.account.successColor') }}
</div>
<a-input v-model:value="custom.successColor" class="value">
<template #prefix>
<div :style="{ background: custom.successColor, width: '10px', height: '10px' }"></div>
<div
:style="{
background: custom.successColor,
width: '10px',
height: '10px',
}"
></div>
</template>
</a-input>
</div>
<div class="settings-item-li">
<div class="label">提醒色</div>
<div class="label">
{{ $t('feature.settings.account.infoColor') }}
</div>
<a-input v-model:value="custom.infoColor" class="value">
<template #prefix>
<div :style="{ background: custom.infoColor, width: '10px', height: '10px' }"></div>
<div
:style="{
background: custom.infoColor,
width: '10px',
height: '10px',
}"
></div>
</template>
</a-input>
</div>
</div>
<div class="setting-item">
<div class="title">用户个性化设置</div>
<div class="title">
{{ $t('feature.settings.account.personalized') }}
</div>
<div class="settings-item-li">
<div class="label">主搜索框欢迎语</div>
<div class="label">
{{ $t('feature.settings.account.greeting') }}
</div>
<a-input v-model:value="custom.placeholder" class="value"></a-input>
</div>
<div class="settings-item-li">
<div class="label">界面 logo</div>
<div class="label">
{{ $t('feature.settings.account.logo') }}
</div>
<div class="img-container">
<img
class="custom-img"
:src="custom.logo"
/>
<a-button @click="changeLogo" size="small" type="link">点我替换</a-button>
<img class="custom-img" :src="custom.logo" />
<a-button @click="changeLogo" size="small" type="link">
{{ $t('feature.settings.account.replace') }}
</a-button>
</div>
</div>
</div>
<div class="footer-btn">
<a-button @click="reset" type="danger">恢复默认设置</a-button>
<a-button @click="reset" type="danger">
{{ $t('feature.settings.account.reset') }}
</a-button>
</div>
</div>
</div>
</template>
<script setup>
import {reactive, ref, toRefs, watch} from 'vue';
import { reactive, ref, toRefs, watch } from 'vue';
import { Modal } from 'ant-design-vue';
import { UserOutlined } from '@ant-design/icons-vue';
import debounce from 'lodash.debounce';
@ -137,7 +186,8 @@ const reset = () => {
title: '确定恢复默认设置吗?',
content: '回复后之前的设置将会被清空',
onOk() {
const defaultcustom = remote.getGlobal('OP_CONFIG').getDefaultConfig().perf.custom;
const defaultcustom = remote.getGlobal('OP_CONFIG').getDefaultConfig()
.perf.custom;
state.custom = JSON.parse(JSON.stringify(defaultcustom));
},
});
@ -175,4 +225,4 @@ const reset = () => {
border-top: 1px dashed #ddd;
padding-top: 12px;
}
</style>
</style>

View File

@ -41,7 +41,7 @@ const visible = ref(false);
const showModal = () => {
visible.value = true;
if (!imgCode.value && !userInfo.value) {
service.getScanCode({ scene }).then(res => {
service.getScanCode({ scene }).then((res) => {
imgCode.value = `data:image/png;base64,${res.dataUrl}`;
});
}
@ -68,7 +68,6 @@ watch([visible], () => {
timer = null;
}
});
</script>
<style lang="less" scoped>
@ -77,7 +76,7 @@ watch([visible], () => {
width: 100%;
overflow-x: hidden;
background: var(--color-body-bg);
height: calc(~"100vh - 46px");
height: calc(~'100vh - 46px');
:deep(.ant-result-title) {
color: var(--color-text-primary);
}

View File

@ -1,5 +1,6 @@
{
"compilerOptions": {
"allowJs": true,
"target": "esnext",
"module": "esnext",
"strict": true,

View File

@ -1,5 +1,5 @@
export default {
version: 9,
version: 11,
perf: {
custom: {
primaryColor: '#106898',
@ -22,6 +22,7 @@ export default {
hideOnBlur: true,
autoPast: false,
darkMode: false,
lang: 'zh-CN',
},
local: {
search: true,