Add custom shortcut key function

This commit is contained in:
zyronon
2023-11-06 23:39:09 +08:00
parent e1ebc5dcc2
commit 37c13da696
14 changed files with 2396 additions and 156 deletions

60
gulpfile.esm.js Normal file
View File

@@ -0,0 +1,60 @@
import { src, dest } from 'gulp';
import through from 'through2';
import * as XLSX from 'xlsx';
import * as path from 'path';
function excel2json() {
let json = {};
const stream = through.obj(function(file, encode, cb) {
if (!file.isBuffer()) {
return cb(null, file);
}
const workbook = XLSX.read(file.contents);
// const excelData = XLSX.utils.sheet_to_json(
// workbook.Sheets[workbook.SheetNames[0]],
// );
const excelData = XLSX.utils.sheet_to_json(workbook.Sheets['Sheet1']);
json = excelData.reduce((result, current) => {
let newCurrent = {};
for (var key in current) {
var letterPattern = /[a-zA-Z]+/g;
var matches = key.match(letterPattern);
if (matches) {
var string = matches[0].toLocaleLowerCase();
newCurrent[string] = current[key].replace(/@{/g, '{');
}
}
result[newCurrent.key] = {};
result[newCurrent.key]['en'] = newCurrent.en || '';
result[newCurrent.key]['zh'] = newCurrent.zh || '';
result[newCurrent.key]['id'] = newCurrent.id || '';
result[newCurrent.key]['tw'] = newCurrent.tw || '';
result[newCurrent.key]['th'] = newCurrent.th || '';
result[newCurrent.key]['ru'] = newCurrent.ru || '';
result[newCurrent.key]['vi'] = newCurrent.vi || '';
result[newCurrent.key]['es'] = newCurrent.es || '';
result[newCurrent.key]['pt'] = newCurrent.pt || '';
result[newCurrent.key]['ja'] = newCurrent.ja || '';
result[newCurrent.key]['uk'] = newCurrent.uk || '';
result[newCurrent.key]['ko'] = newCurrent.ko || '';
result[newCurrent.key]['de'] = newCurrent.de || '';
result[newCurrent.key]['fr'] = newCurrent.fr || '';
return result;
}, json);
file.contents = Buffer.from(JSON.stringify(json, null, '\t'));
file.path = path.join(file.base, 'i18n.json');
cb(null, file);
});
return stream;
}
// 将翻译好的excel写入json
function i18nwrite() {
return src(['./src/locales/i18n.xlsx'])
.pipe(excel2json())
.pipe(dest('src/locales'));
}
export { i18nwrite };

View File

@@ -12,7 +12,8 @@
"commit": "git-cz",
"prepare": "husky install",
"test": "",
"deploy": "push-dir --dir=dist --branch=gh-pages --cleanup"
"deploy": "push-dir --dir=dist --branch=gh-pages --cleanup",
"i18n:write": "gulp i18nwrite"
},
"dependencies": {
"@opentranslate/baidu": "^1.4.2",
@@ -55,7 +56,10 @@
"unplugin-auto-import": "^0.16.6",
"unplugin-vue-components": "^0.25.2",
"vite": "^4.4.5",
"vue-tsc": "^1.8.5"
"vue-tsc": "^1.8.5",
"esm": "^3.2.25",
"gulp": "^4.0.2",
"xlsx": "^0.18.5"
},
"config": {
"commitizen": {

2006
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -49,13 +49,13 @@ defineEmits<{
v-if="!isSimple"
class-name="easy"
@click="$emit('toggleSimple')"
title="标记为简单词(快捷键:`)"
title="标记为简单词"
icon="material-symbols:check-circle-outline-rounded"/>
<BaseIcon
v-else
class-name="fill"
@click="$emit('toggleSimple')"
title="取消标记简单词(快捷键:`)"
title="取消标记简单词"
icon="material-symbols:check-circle-rounded"/>
</template>

View File

@@ -200,6 +200,7 @@ defineExpose({del, showWord, hideWord, play})
}
.phonetic {
margin-top: 5rem;
font-family: $word-font-family;
}

View File

@@ -25,13 +25,19 @@ onMounted(() => {
statModalIsOpen = true
store.saveStatistics(stat)
})
const close = () => {
statModalIsOpen = false
}
emitter.on(ShortcutKey.NextChapter, close)
emitter.on(ShortcutKey.RepeatChapter, close)
emitter.on(ShortcutKey.DictationChapter, close)
})
let optionType = $ref('')
function options(emitType: 'write' | 'repeat' | 'next') {
statModalIsOpen = false
optionType = emitType
emitter.emit(EventKey[emitType])
}
@@ -41,20 +47,12 @@ const isEnd = $computed(() => {
store.currentDict.chapterIndex === store.currentDict.chapterWords.length - 1
})
function onClose() {
if (!optionType) {
options('next')
}
optionType = ''
}
</script>
<template>
<Modal
:header="false"
v-model="statModalIsOpen"
@close="onClose">
v-model="statModalIsOpen">
<div class="statistics">
<header>
<div class="title">{{ store.currentDict.name }}</div>

View File

@@ -11,7 +11,7 @@ import {cloneDeep} from "lodash-es";
import {DefaultShortcutKeyMap} from "@/types.ts";
import BaseButton from "@/components/BaseButton.vue";
const tabIndex = $ref(2)
const tabIndex = $ref(0)
const settingStore = useSettingStore()
//@ts-ignore
const gitLastCommitHash = ref(LATEST_COMMIT_HASH);
@@ -43,17 +43,22 @@ useEventListener('keydown', (e: KeyboardEvent) => {
// }
if (editShortcutKey) {
for (const [k, v] of Object.entries(settingStore.shortcutKeyMap)) {
if (v === shortcutKey && k !== editShortcutKey) {
settingStore.shortcutKeyMap[editShortcutKey] = DefaultShortcutKeyMap[editShortcutKey]
return ElMessage.warning('快捷键重复!')
if (shortcutKey === 'Delete') {
settingStore.shortcutKeyMap[editShortcutKey] = ''
} else {
for (const [k, v] of Object.entries(settingStore.shortcutKeyMap)) {
if (v === shortcutKey && k !== editShortcutKey) {
settingStore.shortcutKeyMap[editShortcutKey] = DefaultShortcutKeyMap[editShortcutKey]
return ElMessage.warning('快捷键重复!')
}
}
settingStore.shortcutKeyMap[editShortcutKey] = shortcutKey
}
settingStore.shortcutKeyMap[editShortcutKey] = shortcutKey
}
})
function resetShortcutKeyMap() {
editShortcutKey = ''
settingStore.shortcutKeyMap = cloneDeep(DefaultShortcutKeyMap)
ElMessage.success('恢复成功')
}
@@ -77,7 +82,7 @@ function resetShortcutKeyMap() {
<span>其他设置</span>
</div>
<div class="tab" :class="tabIndex === 2 && 'active'" @click="tabIndex = 2">
<Icon icon="icon-park-outline:setting-config" width="20" color="#0C8CE9"/>
<Icon icon="material-symbols:keyboard-outline" width="20" color="#0C8CE9"/>
<span>快捷键设置</span>
</div>
</div>
@@ -239,7 +244,7 @@ function resetShortcutKeyMap() {
<label class="item-title">字体设置(仅可调整单词练习)</label>
</div>
<div class="row">
<label class="sut-title">外语字体</label>
<label class="sub-title">外语字体</label>
<div class="wrapper">
<el-slider
:min="10"
@@ -249,7 +254,7 @@ function resetShortcutKeyMap() {
</div>
</div>
<div class="row">
<label class="sut-title">中文字体</label>
<label class="sub-title">中文字体</label>
<div class="wrapper">
<el-slider
:min="10"
@@ -264,7 +269,7 @@ function resetShortcutKeyMap() {
<label class="item-title">其他设置</label>
</div>
<div class="row">
<label class="sut-title">切换下一个单词时间</label>
<label class="sub-title">切换下一个单词时间</label>
<div class="wrapper">
<el-input-number v-model="settingStore.waitTimeForChangeWord"
:min="6"
@@ -275,22 +280,27 @@ function resetShortcutKeyMap() {
</div>
</div>
</div>
<div v-if="tabIndex === 2">
<div class="body" v-if="tabIndex === 2">
<div class="row">
<label class="item-title">功能</label>
<label class="main-title">功能</label>
<div class="wrapper">快捷键(点击可修改)</div>
</div>
<div class="row" v-for="item of Object.entries(settingStore.shortcutKeyMap)">
<label class="item-title">{{ item[0] }}</label>
<div class="wrapper" @click="editShortcutKey = item[0]">
<div class="set-key" v-if="editShortcutKey === item[0]">
<input :value="item[1]" readonly type="text" @blur="editShortcutKey = ''">
<span @click.stop="editShortcutKey = ''">直接按键盘进行设置</span>
<div class="scroll">
<div class="row" v-for="item of Object.entries(settingStore.shortcutKeyMap)">
<label class="item-title">{{ $t(item[0]) }}</label>
<div class="wrapper" @click="editShortcutKey = item[0]">
<div class="set-key" v-if="editShortcutKey === item[0]">
<input :value="item[1]?item[1]:'未设置快捷键'" readonly type="text" @blur="editShortcutKey = ''">
<span @click.stop="editShortcutKey = ''">直接按键盘进行设置</span>
</div>
<div v-else>
<div v-if="item[1]">{{ item[1] }}</div>
<span v-else>未设置快捷键</span>
</div>
</div>
<div v-else> {{ item[1] }}</div>
</div>
</div>
<div class="row">
<div class="row footer">
<label class="item-title"></label>
<div class="wrapper">
<BaseButton @click="resetShortcutKeyMap">恢复默认</BaseButton>
@@ -350,17 +360,18 @@ function resetShortcutKeyMap() {
background: var(--color-header-bg);
flex: 1;
height: 100%;
padding: 0 $space;
overflow: auto;
padding: 0 $space;
.row {
height: 50rem;
height: 40rem;
display: flex;
justify-content: space-between;
align-items: center;
gap: $space * 5;
.wrapper {
height: 30rem;
flex: 1;
display: flex;
justify-content: flex-end;
@@ -368,7 +379,7 @@ function resetShortcutKeyMap() {
span {
text-align: right;
width: 30rem;
//width: 30rem;
font-size: 12rem;
color: gray;
}
@@ -393,22 +404,39 @@ function resetShortcutKeyMap() {
}
.main-title {
font-size: 26rem;
font-size: 18rem;
font-weight: bold;
}
.item-title {
font-size: 22rem;
font-size: 16rem;
}
.sub-title {
font-size: 18rem;
font-size: 14rem;
}
}
.body {
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
}
.scroll {
flex: 1;
padding-right: 10rem;
overflow: auto;
}
.footer {
margin-bottom: 20rem;
}
.desc {
margin-bottom: 10rem;
font-size: 14rem;
font-size: 12rem;
}
.line {

View File

@@ -82,7 +82,7 @@ watch(() => store.load, n => {
</Tooltip>
<Tooltip
:title="`开关默写模式(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleShowTranslate]})`"
:title="`开关默写模式(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleDictation]})`"
>
<IconWrapper>
<Icon icon="majesticons:eye-off-line"

View File

@@ -42,7 +42,7 @@ function save() {
<template>
<div class="setting" @click.stop="null">
<Tooltip
:title="`开关释义显示(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleDictation]})`"
:title="`开关释义显示(快捷键:${settingStore.shortcutKeyMap[ShortcutKey.ToggleShowTranslate]})`"
>
<IconWrapper>
<Icon v-if="settingStore.translate" icon="mdi:translate"

View File

@@ -1,104 +0,0 @@
<script setup lang="ts">
import VolumeIcon from "@/components/VolumeIcon.vue";
import Modal from "@/components/Modal/Modal.vue";
import {$ref} from "vue/macros";
import {onMounted, onUnmounted} from "vue";
import {usePlayWordAudio} from "@/hooks/sound.ts";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import ListItem from "@/components/ListItem.vue";
import {Word} from "@/types.ts";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {cloneDeep} from "lodash-es";
let show = $ref(false)
let loading = $ref(false)
let list = $ref([])
let title = $ref('')
const playWordAudio = usePlayWordAudio()
const runtimeStore = useRuntimeStore()
onMounted(() => {
emitter.on(EventKey.openWordListModal, (val: any) => {
loading = true
show = true
list = cloneDeep(val.list)
title = val.title
setTimeout(() => {
if (val.translateLanguage === 'common') {
console.time()
let tempList = cloneDeep(val.list)
tempList.map((w: Word) => {
if (!w.trans.length) {
let res = runtimeStore.translateWordList.find(a => a.name === w.name)
if (res) w = Object.assign(w, res)
}
})
list = cloneDeep(tempList)
console.timeEnd()
}
// loading = false
}, 500)
})
})
onUnmounted(() => {
emitter.off(EventKey.openWordListModal)
})
</script>
<template>
<Modal
:title="title"
v-model="show">
<div class="all-word">
<virtual-list class="virtual-list"
:keeps="20"
data-key="name"
v-loading="loading"
:data-sources="list"
:estimate-size="85"
item-class="dict-virtual-item"
>
<template #={source}>
<ListItem
class="common-list-item"
:show-volume="true">
<div class="item-title">
<span class="word">{{ source.name }}</span>
<span class="phonetic">{{ source.usphone }}</span>
<VolumeIcon class="volume" @click="playWordAudio(source.name)"></VolumeIcon>
</div>
<div class="item-sub-title" v-if="source.trans.length">{{ source.trans.join('') }}</div>
</ListItem>
</template>
</virtual-list>
</div>
</Modal>
</template>
<style lang="scss" scoped>
@import "@/assets/css/style";
.all-word {
padding: $space;
padding-top: 0;
width: 400rem;
height: 75vh;
}
</style>
<style lang="scss">
@import "@/assets/css/variable.scss";
.virtual-list {
overflow: auto;
height: 100%;
}
.dict-virtual-item {
margin-bottom: 15rem;
}
</style>

242
src/locales/i18n.json Normal file
View File

@@ -0,0 +1,242 @@
{
"ShowWord": {
"en": "",
"zh": "显示单词",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
},
"EditArticle": {
"en": "",
"zh": "编辑文章",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
},
"Skip": {
"en": "",
"zh": "跳过",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
},
"ToggleSimple": {
"en": "",
"zh": "标记/取消简单词",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
},
"ToggleCollect": {
"en": "",
"zh": "添加/取消收藏",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
},
"NextChapter": {
"en": "",
"zh": "下一章",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
},
"RepeatChapter": {
"en": "",
"zh": "重复本章",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
},
"DictationChapter": {
"en": "",
"zh": "默写本章",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
},
"PlaySound": {
"en": "",
"zh": "播放发音",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
},
"ToggleShowTranslate": {
"en": "",
"zh": "隐藏/显示翻译",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
},
"ToggleDictation": {
"en": "",
"zh": "切换默写",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
},
"OpenSetting": {
"en": "",
"zh": "打开设置",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
},
"OpenDictDetail": {
"en": "",
"zh": "打开词典详情",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
},
"ToggleTheme": {
"en": "",
"zh": "切换主题",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
},
"ToggleConciseMode": {
"en": "",
"zh": "切换简洁模式",
"id": "",
"tw": "",
"th": "",
"ru": "",
"vi": "",
"es": "",
"pt": "",
"ja": "",
"uk": "",
"ko": "",
"de": "",
"fr": ""
}
}

BIN
src/locales/i18n.xlsx Normal file

Binary file not shown.

11
src/locales/zh-CN.ts Normal file
View File

@@ -0,0 +1,11 @@
import i18nData from './i18n.json';
const ZH = Object.entries(i18nData).reduce((result, current) => {
const [key, value] = current;
if (value.zh) {
result[key] = value.zh;
}
return result;
}, {} as any);
export default ZH;

View File

@@ -5,7 +5,16 @@ import App from './App.vue'
import {createPinia} from "pinia"
// import ElementPlus from 'element-plus'
import VirtualList from 'vue-virtual-list-v3';
import ZH from "@/locales/zh-CN.ts";
import {createI18n} from 'vue-i18n'
const i18n = createI18n({
locale: 'zh-CN',
fallbackLocale: 'zh-CN',
messages: {
'zh-CN': ZH
},
})
const pinia = createPinia()
// const app = createApp(Mobile)
const app = createApp(App)
@@ -13,5 +22,6 @@ const app = createApp(App)
// app.use(ElementPlus)
app.use(pinia)
app.use(VirtualList);
app.use(i18n)
app.mount('#app')