save
This commit is contained in:
3
components.d.ts
vendored
3
components.d.ts
vendored
@@ -16,6 +16,7 @@ declare module 'vue' {
|
||||
BatchAddArticle: typeof import('./src/components/Add/BatchAddArticle.vue')['default']
|
||||
ChapterDetail: typeof import('./src/components/ChapterDetail.vue')['default']
|
||||
ChapterList: typeof import('./src/components/ChapterList.vue')['default']
|
||||
Close: typeof import('./src/components/Close.vue')['default']
|
||||
DictList: typeof import('./src/components/DictList.vue')['default']
|
||||
DictModal: typeof import('./src/components/Toolbar/DictModal.vue')['default']
|
||||
EditAbleText: typeof import('./src/components/EditAbleText.vue')['default']
|
||||
@@ -29,6 +30,8 @@ declare module 'vue' {
|
||||
Fireworks: typeof import('./src/components/Fireworks.vue')['default']
|
||||
Footer: typeof import('./src/components/Practice/Footer.vue')['default']
|
||||
IconWrapper: typeof import('./src/components/IconWrapper.vue')['default']
|
||||
Input: typeof import('./src/components/Input.vue')['default']
|
||||
List: typeof import('./src/components/List.vue')['default']
|
||||
MiniModal: typeof import('./src/components/MiniModal.vue')['default']
|
||||
Modal: typeof import('./src/components/Modal/Modal.vue')['default']
|
||||
PopConfirm: typeof import('./src/components/PopConfirm.vue')['default']
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"dependencies": {
|
||||
"@opentranslate/baidu": "^1.4.2",
|
||||
"@opentranslate/translator": "^1.4.2",
|
||||
"@types/uuid": "^9.0.4",
|
||||
"axios": "^1.5.0",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"element-plus": "^2.3.9",
|
||||
@@ -26,6 +27,7 @@
|
||||
"pinia": "^2.1.6",
|
||||
"swiper": "^10.1.0",
|
||||
"tesseract.js": "^4.1.1",
|
||||
"uuid": "^9.0.1",
|
||||
"vue": "^3.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
15
pnpm-lock.yaml
generated
15
pnpm-lock.yaml
generated
@@ -11,6 +11,9 @@ dependencies:
|
||||
'@opentranslate/translator':
|
||||
specifier: ^1.4.2
|
||||
version: 1.4.2
|
||||
'@types/uuid':
|
||||
specifier: ^9.0.4
|
||||
version: 9.0.4
|
||||
axios:
|
||||
specifier: ^1.5.0
|
||||
version: 1.5.0
|
||||
@@ -41,6 +44,9 @@ dependencies:
|
||||
tesseract.js:
|
||||
specifier: ^4.1.1
|
||||
version: 4.1.1
|
||||
uuid:
|
||||
specifier: ^9.0.1
|
||||
version: 9.0.1
|
||||
vue:
|
||||
specifier: ^3.3.4
|
||||
version: 3.3.4
|
||||
@@ -875,6 +881,10 @@ packages:
|
||||
resolution: {integrity: sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==}
|
||||
dev: false
|
||||
|
||||
/@types/uuid@9.0.4:
|
||||
resolution: {integrity: sha512-zAuJWQflfx6dYJM62vna+Sn5aeSWhh3OB+wfUEACNcqUSc0AGc5JKl+ycL1vrH7frGTXhJchYjE1Hak8L819dA==}
|
||||
dev: false
|
||||
|
||||
/@types/web-bluetooth@0.0.16:
|
||||
resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
|
||||
dev: false
|
||||
@@ -2985,6 +2995,11 @@ packages:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
dev: true
|
||||
|
||||
/uuid@9.0.1:
|
||||
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
/v8-compile-cache-lib@3.0.1:
|
||||
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
|
||||
requiresBuild: true
|
||||
|
||||
@@ -18,7 +18,9 @@ import {useDisableEventListener} from "@/hooks/event.ts";
|
||||
import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {$ref} from "vue/macros";
|
||||
import {$computed, $ref} from "vue/macros";
|
||||
import Input from "@/components/Input.vue";
|
||||
import List from "@/components/List.vue";
|
||||
|
||||
interface IProps {
|
||||
selectIndex?: number
|
||||
@@ -31,7 +33,9 @@ const props = withDefaults(defineProps<IProps>(), {
|
||||
const base = useBaseStore()
|
||||
let article = $ref<Article>(cloneDeep(DefaultArticle))
|
||||
let selectIndex = $ref<number>(props.selectIndex)
|
||||
let selectItem = $ref<number>(props.selectIndex)
|
||||
let networkTranslateEngine = $ref('baidu')
|
||||
let searchKey = $ref('')
|
||||
let progress = $ref(0)
|
||||
const TranslateEngineOptions = [
|
||||
{value: 'baidu', label: '百度'},
|
||||
@@ -264,26 +268,43 @@ watch(() => article.useTranslateType, () => {
|
||||
}
|
||||
})
|
||||
|
||||
const list = $computed(() => {
|
||||
if (searchKey) {
|
||||
return base.currentEditDict.articles.filter((item: Article) => {
|
||||
//把搜索内容,分词之后,判断是否有这个词,比单纯遍历包含体验更好
|
||||
return searchKey.toLowerCase().split(' ').filter(v => v).some(value => {
|
||||
return item.title.toLowerCase().includes(value) || item.titleTranslate.toLowerCase().includes(value)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
return base.currentEditDict.articles
|
||||
}
|
||||
})
|
||||
|
||||
function selectArticle(index: number) {
|
||||
article = cloneDeep(base.currentEditDict.articles[index])
|
||||
selectIndex = index
|
||||
}
|
||||
|
||||
function delArticle(index: number) {
|
||||
if (index < selectIndex) {
|
||||
selectIndex--
|
||||
} else if (index === selectIndex) {
|
||||
if (selectIndex === base.currentEditDict.articles.length - 1) {
|
||||
function delArticle(item: Article) {
|
||||
let rIndex = base.currentEditDict.articles.findIndex((v: Article) => v.id === item.id)
|
||||
if (rIndex > -1) {
|
||||
if (index < selectIndex) {
|
||||
selectIndex--
|
||||
} else if (index === selectIndex) {
|
||||
if (selectIndex === base.currentEditDict.articles.length - 1) {
|
||||
selectIndex--
|
||||
}
|
||||
}
|
||||
base.currentEditDict.articles.splice(index, 1)
|
||||
if (selectIndex < 0) {
|
||||
article = cloneDeep(DefaultArticle)
|
||||
} else {
|
||||
article = cloneDeep(base.currentEditDict.articles[selectIndex])
|
||||
}
|
||||
}
|
||||
base.currentEditDict.articles.splice(index, 1)
|
||||
if (selectIndex < 0) {
|
||||
article = cloneDeep(DefaultArticle)
|
||||
} else {
|
||||
article = cloneDeep(base.currentEditDict.articles[selectIndex])
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -293,27 +314,19 @@ function delArticle(index: number) {
|
||||
<div class="dict-name">{{ base.currentEditDict.name }}</div>
|
||||
<BaseIcon title="选择其他词典/文章" icon="carbon:change-catalog"/>
|
||||
</header>
|
||||
<div class="article-list">
|
||||
<div class="item"
|
||||
:class="[
|
||||
(selectIndex === index) && 'active'
|
||||
]"
|
||||
@click="selectArticle(index)"
|
||||
v-for="(item,index) in base.currentEditDict.articles">
|
||||
<div class="left">
|
||||
<div class="name"> {{ `${index + 1}. ${item.title}` }}</div>
|
||||
<div class="translate-name"> {{ ` ${item.titleTranslate}` }}</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<BaseIcon
|
||||
@click="delArticle(index)"
|
||||
title="删除" icon="fluent:delete-24-regular"/>
|
||||
<BaseIcon
|
||||
@click="delArticle(index)"
|
||||
title="删除" icon="carbon:move"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<List
|
||||
v-model:list="list"
|
||||
v-model:searchKey="searchKey"
|
||||
:select-index="selectIndex"
|
||||
:row-key="(item:Article) => item.title"
|
||||
@del-article="delArticle"
|
||||
@select-article="selectArticle"
|
||||
>
|
||||
<template v-slot="{item,index}">
|
||||
<div class="name"> {{ `${index + 1}. ${item.title}` }}</div>
|
||||
<div class="translate-name"> {{ ` ${item.titleTranslate}` }}</div>
|
||||
</template>
|
||||
</List>
|
||||
<div class="footer">
|
||||
<BaseButton>导入</BaseButton>
|
||||
<BaseButton>导出</BaseButton>
|
||||
@@ -471,6 +484,7 @@ function delArticle(index: number) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
//opacity: 0;
|
||||
|
||||
.dict-name {
|
||||
font-size: 30rem;
|
||||
@@ -478,46 +492,12 @@ function delArticle(index: number) {
|
||||
}
|
||||
}
|
||||
|
||||
.article-list {
|
||||
flex: 1;
|
||||
width: 300rem;
|
||||
overflow: auto;
|
||||
.name {
|
||||
font-size: 18rem;
|
||||
}
|
||||
|
||||
.item {
|
||||
background: #e1e1e1;
|
||||
border-radius: 8rem;
|
||||
margin-bottom: 10rem;
|
||||
padding: 10rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transition: all .3s;
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all .3s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.right {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: var(--color-item-active);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 18rem;
|
||||
}
|
||||
|
||||
.translate-name {
|
||||
font-size: 16rem;
|
||||
}
|
||||
}
|
||||
.translate-name {
|
||||
font-size: 16rem;
|
||||
}
|
||||
|
||||
.footer {
|
||||
@@ -534,6 +514,7 @@ function delArticle(index: number) {
|
||||
display: flex;
|
||||
gap: $space;
|
||||
padding: $space;
|
||||
//opacity: 0;
|
||||
}
|
||||
|
||||
.row {
|
||||
|
||||
@@ -5,7 +5,7 @@ import IconWrapper from "@/components/IconWrapper.vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
|
||||
defineProps<{
|
||||
title: string,
|
||||
title?: string,
|
||||
icon: string,
|
||||
}>()
|
||||
|
||||
|
||||
22
src/components/Close.vue
Normal file
22
src/components/Close.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import {Icon} from "@iconify/vue";
|
||||
|
||||
defineEmits(['click'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="close"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
<Icon icon="ic:round-close"
|
||||
width="20"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.close {
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
</style>
|
||||
77
src/components/Input.vue
Normal file
77
src/components/Input.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<script setup lang="ts">
|
||||
import {$ref} from "vue/macros";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import Close from "@/components/Close.vue";
|
||||
import {useWindowClick} from "@/hooks/event.ts";
|
||||
|
||||
defineProps<{
|
||||
modelValue: string
|
||||
}>()
|
||||
|
||||
defineEmits(['update:modelValue'])
|
||||
let focus = $ref(false)
|
||||
let inputEl = $ref<HTMLDivElement>()
|
||||
|
||||
useWindowClick((e: PointerEvent) => {
|
||||
focus = inputEl.contains(e.target as any);
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="base-input"
|
||||
:class="{focus}"
|
||||
ref="inputEl"
|
||||
>
|
||||
<Icon icon="fluent:search-24-regular"
|
||||
width="20"/>
|
||||
<input type="text"
|
||||
:value="modelValue"
|
||||
@input="e=>$emit('update:modelValue',e.target.value)"
|
||||
>
|
||||
<Close @click="$emit('update:modelValue','')"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/style.scss";
|
||||
|
||||
.base-input {
|
||||
border: 1px solid var(--color-main-bg);
|
||||
border-radius: 4rem;
|
||||
padding: 3rem 5rem;
|
||||
transition: all .3s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: all .3s;
|
||||
|
||||
:deep(svg) {
|
||||
transition: all .3s;
|
||||
color: var(--color-main-bg);
|
||||
}
|
||||
|
||||
&.focus {
|
||||
border: 1px solid var(--color-main-active);
|
||||
|
||||
:deep(svg) {
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
font-family: $font-family;
|
||||
font-size: 18rem;
|
||||
outline: none;
|
||||
min-height: 20rem;
|
||||
flex: 1;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
border: none;
|
||||
|
||||
&[readonly] {
|
||||
cursor: not-allowed;
|
||||
opacity: .7;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
171
src/components/List.vue
Normal file
171
src/components/List.vue
Normal file
@@ -0,0 +1,171 @@
|
||||
<script setup lang="ts" generic="T">
|
||||
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import Input from "@/components/Input.vue";
|
||||
import {$ref} from "vue/macros";
|
||||
import {cloneDeep, throttle} from "lodash-es";
|
||||
|
||||
let dragIndex = $ref(-1)
|
||||
|
||||
interface IProps {
|
||||
list: T[]
|
||||
searchKey: string,
|
||||
selectIndex: number,
|
||||
rowKey: (item: T) => string
|
||||
}
|
||||
|
||||
const props = defineProps<IProps>()
|
||||
const emit = defineEmits<{
|
||||
selectArticle: [index: number],
|
||||
delArticle: [item: T],
|
||||
'update:searchKey': [val: string],
|
||||
'update:list': [list: T[]],
|
||||
}>()
|
||||
let draggable = $ref(false)
|
||||
|
||||
function dragstart(index: number) {
|
||||
dragIndex = index;
|
||||
}
|
||||
|
||||
const dragenter = throttle((e, index) => {
|
||||
// console.log('dragenter', 'dragIndex', dragIndex, 'index', index)
|
||||
e.preventDefault();
|
||||
// 避免源对象触发自身的dragenter事件
|
||||
if (dragIndex !== index && dragIndex !== -1) {
|
||||
const source = props.list[dragIndex];
|
||||
let temp = cloneDeep(props.list)
|
||||
temp.splice(dragIndex, 1);
|
||||
temp.splice(index, 0, source);
|
||||
emit('update:list', temp)
|
||||
// props.list = temp
|
||||
// 排序变化后目标对象的索引变成源对象的索引
|
||||
dragIndex = index;
|
||||
}
|
||||
}, 200)
|
||||
|
||||
function dragover(e, index) {
|
||||
// console.log('dragover')
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function dragend() {
|
||||
// console.log('dragend')
|
||||
draggable = false
|
||||
dragIndex = -1
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="list-wrapper">
|
||||
<div class="search">
|
||||
<Input :model-value="searchKey"
|
||||
@update:model-value="(e:string) => emit('update:searchKey',e)"
|
||||
/>
|
||||
</div>
|
||||
<transition-group
|
||||
name="drag"
|
||||
class="list"
|
||||
tag="div"
|
||||
>
|
||||
<div class="item"
|
||||
:class="[
|
||||
(props.selectIndex === index) && 'active',
|
||||
draggable && 'draggable',
|
||||
(dragIndex === index) && 'active'
|
||||
]"
|
||||
@click="emit('selectArticle',index)"
|
||||
v-for="(item,index) in props.list"
|
||||
:key="rowKey(item)"
|
||||
:draggable="draggable"
|
||||
@dragstart="dragstart(index)"
|
||||
@dragenter="dragenter($event, index)"
|
||||
@dragover="dragover($event, index)"
|
||||
@dragend="dragend()"
|
||||
>
|
||||
<div class="left">
|
||||
<slot :item="item" :index="index"></slot>
|
||||
</div>
|
||||
<div class="right">
|
||||
<BaseIcon
|
||||
@click="emit('delArticle',item)"
|
||||
title="删除" icon="fluent:delete-24-regular"/>
|
||||
<div
|
||||
@mousedown="draggable = true"
|
||||
@mouseup="draggable = false"
|
||||
>
|
||||
<BaseIcon
|
||||
icon="carbon:move"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.drag-move, /* 对移动中的元素应用的过渡 */
|
||||
.drag-enter-active,
|
||||
.drag-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.drag-enter-from,
|
||||
.drag-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
|
||||
/* 确保将离开的元素从布局流中删除
|
||||
以便能够正确地计算移动的动画。 */
|
||||
.drag-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.list-wrapper {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding-right: 5rem;
|
||||
|
||||
.search {
|
||||
margin: 10rem 0;
|
||||
width: 260rem;
|
||||
}
|
||||
|
||||
.list {
|
||||
.item {
|
||||
width: 260rem;
|
||||
box-sizing: border-box;
|
||||
background: #e1e1e1;
|
||||
border-radius: 8rem;
|
||||
margin-bottom: 10rem;
|
||||
padding: 10rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transition: all .3s;
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: all .3s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.right {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: var(--color-item-active);
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.draggable {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -25,6 +25,7 @@ export default {
|
||||
methods: {
|
||||
showPop(e) {
|
||||
if (this.disabled) return
|
||||
if (!this.title) return
|
||||
e.stopPropagation()
|
||||
let rect = e.target.getBoundingClientRect()
|
||||
this.show = true
|
||||
@@ -49,7 +50,7 @@ export default {
|
||||
<Teleport to="body">
|
||||
<Transition name="fade">
|
||||
{
|
||||
this.show && (
|
||||
this.show && this.title && (
|
||||
<div ref="tip" class="tip">
|
||||
{this.title}
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {$ref} from "vue/macros";
|
||||
|
||||
export function useWindowClick(cb: () => void) {
|
||||
export function useWindowClick(cb: (e: PointerEvent) => void) {
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.closeOther, cb)
|
||||
window.addEventListener('click', cb)
|
||||
|
||||
@@ -2,257 +2,261 @@ import {defineStore} from 'pinia'
|
||||
import {Dict, DictType, SaveDictKey, Sort, Statistics, Word} from "../types.ts"
|
||||
import {chunk, cloneDeep} from "lodash-es";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts"
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
|
||||
export interface State {
|
||||
newWordDict: Dict,
|
||||
skipWordDict: Dict,
|
||||
wrongWordDict: Dict,
|
||||
dict: Dict,
|
||||
myDicts: Dict[],
|
||||
current: {
|
||||
dictType: DictType,
|
||||
index: number,
|
||||
editIndex: number,
|
||||
repeatNumber: number,
|
||||
},
|
||||
simpleWords: string[],
|
||||
sideIsOpen: boolean,
|
||||
load: boolean
|
||||
newWordDict: Dict,
|
||||
skipWordDict: Dict,
|
||||
wrongWordDict: Dict,
|
||||
dict: Dict,
|
||||
myDicts: Dict[],
|
||||
current: {
|
||||
dictType: DictType,
|
||||
index: number,
|
||||
editIndex: number,
|
||||
repeatNumber: number,
|
||||
},
|
||||
simpleWords: string[],
|
||||
sideIsOpen: boolean,
|
||||
load: boolean
|
||||
}
|
||||
|
||||
export const useBaseStore = defineStore('base', {
|
||||
state: (): State => {
|
||||
return {
|
||||
newWordDict: {
|
||||
name: '生词本',
|
||||
sort: Sort.normal,
|
||||
type: DictType.newDict,
|
||||
originWords: [],
|
||||
articles: [],
|
||||
words: [],
|
||||
chapterWordNumber: 15,
|
||||
chapterWords: [],
|
||||
chapterIndex: 0,
|
||||
chapterWordIndex: 0,
|
||||
statistics: [],
|
||||
url: '',
|
||||
},
|
||||
skipWordDict: {
|
||||
name: '简单词',
|
||||
sort: Sort.normal,
|
||||
type: DictType.skipDict,
|
||||
originWords: [],
|
||||
articles: [],
|
||||
words: [],
|
||||
chapterWordNumber: 15,
|
||||
chapterWords: [],
|
||||
chapterIndex: 0,
|
||||
chapterWordIndex: 0,
|
||||
statistics: [],
|
||||
url: '',
|
||||
},
|
||||
wrongWordDict: {
|
||||
name: '错词本',
|
||||
sort: Sort.normal,
|
||||
type: DictType.wrongDict,
|
||||
originWords: [],
|
||||
articles: [],
|
||||
words: [],
|
||||
chapterWordNumber: 15,
|
||||
chapterWords: [],
|
||||
chapterIndex: 0,
|
||||
chapterWordIndex: 0,
|
||||
statistics: [],
|
||||
url: '',
|
||||
},
|
||||
// dict: {
|
||||
// name: '新概念英语-2',
|
||||
// sort: Sort.normal,
|
||||
// type: DictType.innerDict,
|
||||
// originWords: [],
|
||||
// articles: [],
|
||||
// words: [],
|
||||
// chapterWordNumber: 15,
|
||||
// chapterWords: [],
|
||||
// chapterIndex: 0,
|
||||
// chapterWordIndex: 0,
|
||||
// statistics: [],
|
||||
// url: '/dicts/NCE_2.json',
|
||||
// },
|
||||
dict: {
|
||||
name: '新概念英语-2',
|
||||
sort: Sort.normal,
|
||||
type: DictType.publicArticle,
|
||||
originWords: [],
|
||||
articles: [],
|
||||
words: [],
|
||||
chapterWordNumber: 15,
|
||||
chapterWords: [],
|
||||
chapterIndex: 0,
|
||||
chapterWordIndex: 0,
|
||||
statistics: [],
|
||||
url: '/articles/NCE_2.json',
|
||||
},
|
||||
myDicts: [
|
||||
{
|
||||
name: '新概念英语-2',
|
||||
sort: Sort.normal,
|
||||
type: DictType.publicArticle,
|
||||
originWords: [],
|
||||
articles: [],
|
||||
words: [],
|
||||
chapterWordNumber: 15,
|
||||
chapterWords: [],
|
||||
chapterIndex: 0,
|
||||
chapterWordIndex: 0,
|
||||
statistics: [],
|
||||
url: '/articles/NCE_2.json',
|
||||
state: (): State => {
|
||||
return {
|
||||
newWordDict: {
|
||||
name: '生词本',
|
||||
sort: Sort.normal,
|
||||
type: DictType.newDict,
|
||||
originWords: [],
|
||||
articles: [],
|
||||
words: [],
|
||||
chapterWordNumber: 15,
|
||||
chapterWords: [],
|
||||
chapterIndex: 0,
|
||||
chapterWordIndex: 0,
|
||||
statistics: [],
|
||||
url: '',
|
||||
},
|
||||
skipWordDict: {
|
||||
name: '简单词',
|
||||
sort: Sort.normal,
|
||||
type: DictType.skipDict,
|
||||
originWords: [],
|
||||
articles: [],
|
||||
words: [],
|
||||
chapterWordNumber: 15,
|
||||
chapterWords: [],
|
||||
chapterIndex: 0,
|
||||
chapterWordIndex: 0,
|
||||
statistics: [],
|
||||
url: '',
|
||||
},
|
||||
wrongWordDict: {
|
||||
name: '错词本',
|
||||
sort: Sort.normal,
|
||||
type: DictType.wrongDict,
|
||||
originWords: [],
|
||||
articles: [],
|
||||
words: [],
|
||||
chapterWordNumber: 15,
|
||||
chapterWords: [],
|
||||
chapterIndex: 0,
|
||||
chapterWordIndex: 0,
|
||||
statistics: [],
|
||||
url: '',
|
||||
},
|
||||
// dict: {
|
||||
// name: '新概念英语-2',
|
||||
// sort: Sort.normal,
|
||||
// type: DictType.innerDict,
|
||||
// originWords: [],
|
||||
// articles: [],
|
||||
// words: [],
|
||||
// chapterWordNumber: 15,
|
||||
// chapterWords: [],
|
||||
// chapterIndex: 0,
|
||||
// chapterWordIndex: 0,
|
||||
// statistics: [],
|
||||
// url: '/dicts/NCE_2.json',
|
||||
// },
|
||||
dict: {
|
||||
name: '新概念英语-2',
|
||||
sort: Sort.normal,
|
||||
type: DictType.publicArticle,
|
||||
originWords: [],
|
||||
articles: [],
|
||||
words: [],
|
||||
chapterWordNumber: 15,
|
||||
chapterWords: [],
|
||||
chapterIndex: 0,
|
||||
chapterWordIndex: 0,
|
||||
statistics: [],
|
||||
url: '/articles/NCE_2.json',
|
||||
},
|
||||
myDicts: [
|
||||
{
|
||||
name: '新概念英语-2',
|
||||
sort: Sort.normal,
|
||||
type: DictType.publicArticle,
|
||||
originWords: [],
|
||||
articles: [],
|
||||
words: [],
|
||||
chapterWordNumber: 15,
|
||||
chapterWords: [],
|
||||
chapterIndex: 0,
|
||||
chapterWordIndex: 0,
|
||||
statistics: [],
|
||||
url: '/articles/NCE_2.json',
|
||||
}
|
||||
],
|
||||
current: {
|
||||
dictType: DictType.publicArticle,
|
||||
index: 0,
|
||||
editIndex: 0,
|
||||
repeatNumber: 0,
|
||||
},
|
||||
sideIsOpen: false,
|
||||
simpleWords: [
|
||||
'a', 'an', 'of', 'and',
|
||||
'i', 'my', 'you', 'your',
|
||||
'me', 'am', 'is', 'do', 'are',
|
||||
'what', 'who', 'where', 'how', 'no', 'yes',
|
||||
'not', 'did', 'were', 'can', 'could', 'it',
|
||||
'the', 'to'
|
||||
],
|
||||
load: false
|
||||
}
|
||||
],
|
||||
current: {
|
||||
dictType: DictType.publicArticle,
|
||||
index: 0,
|
||||
editIndex: 0,
|
||||
repeatNumber: 0,
|
||||
},
|
||||
sideIsOpen: false,
|
||||
simpleWords: [
|
||||
'a', 'an', 'of', 'and',
|
||||
'i', 'my', 'you', 'your',
|
||||
'me', 'am', 'is', 'do', 'are',
|
||||
'what', 'who', 'where', 'how', 'no', 'yes',
|
||||
'not', 'did', 'were', 'can', 'could', 'it',
|
||||
'the', 'to'
|
||||
],
|
||||
load: false
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
skipWordNames: (state: State) => {
|
||||
return state.skipWordDict.originWords.map(v => v.name.toLowerCase())
|
||||
},
|
||||
skipWordNamesWithSimpleWords: (state: State) => {
|
||||
return state.skipWordDict.originWords.map(v => v.name.toLowerCase()).concat(state.simpleWords)
|
||||
getters: {
|
||||
skipWordNames: (state: State) => {
|
||||
return state.skipWordDict.originWords.map(v => v.name.toLowerCase())
|
||||
},
|
||||
skipWordNamesWithSimpleWords: (state: State) => {
|
||||
return state.skipWordDict.originWords.map(v => v.name.toLowerCase()).concat(state.simpleWords)
|
||||
},
|
||||
isArticle(state: State): boolean {
|
||||
return [
|
||||
DictType.publicArticle,
|
||||
DictType.customArticle
|
||||
].includes(state.current.dictType)
|
||||
},
|
||||
currentDict(state: State): Dict {
|
||||
switch (state.current.dictType) {
|
||||
case DictType.newDict:
|
||||
return state.newWordDict
|
||||
case DictType.skipDict:
|
||||
return state.skipWordDict
|
||||
case DictType.wrongDict:
|
||||
return state.wrongWordDict
|
||||
case DictType.publicDict:
|
||||
case DictType.publicArticle:
|
||||
case DictType.customDict:
|
||||
case DictType.customArticle:
|
||||
return this.myDicts[this.current.index]
|
||||
}
|
||||
},
|
||||
currentEditDict(): Dict {
|
||||
return this.myDicts[this.current.editIndex]
|
||||
},
|
||||
wordIndex(state: State): number {
|
||||
return this.currentDict.wordIndex
|
||||
},
|
||||
chapter(state: State): Word[] {
|
||||
return this.currentDict.chapterWords[this.currentDict.chapterIndex] ?? []
|
||||
},
|
||||
//TODO 废弃
|
||||
word(state: State): Word {
|
||||
return {trans: [], name: '', usphone: '', ukphone: '',}
|
||||
},
|
||||
dictTitle(state: State) {
|
||||
let title = this.currentDict.name
|
||||
if ([DictType.publicDict, DictType.customDict].includes(this.current.dictType)) {
|
||||
title += ` 第${this.currentDict.chapterIndex + 1}章`
|
||||
}
|
||||
return title
|
||||
}
|
||||
},
|
||||
isArticle(state: State): boolean {
|
||||
return [
|
||||
DictType.publicArticle,
|
||||
DictType.customArticle
|
||||
].includes(state.current.dictType)
|
||||
},
|
||||
currentDict(state: State): Dict {
|
||||
switch (state.current.dictType) {
|
||||
case DictType.newDict:
|
||||
return state.newWordDict
|
||||
case DictType.skipDict:
|
||||
return state.skipWordDict
|
||||
case DictType.wrongDict:
|
||||
return state.wrongWordDict
|
||||
case DictType.publicDict:
|
||||
case DictType.publicArticle:
|
||||
case DictType.customDict:
|
||||
case DictType.customArticle:
|
||||
return this.myDicts[this.current.index]
|
||||
}
|
||||
},
|
||||
currentEditDict(): Dict {
|
||||
return this.myDicts[this.current.editIndex]
|
||||
},
|
||||
wordIndex(state: State): number {
|
||||
return this.currentDict.wordIndex
|
||||
},
|
||||
chapter(state: State): Word[] {
|
||||
return this.currentDict.chapterWords[this.currentDict.chapterIndex] ?? []
|
||||
},
|
||||
//TODO 废弃
|
||||
word(state: State): Word {
|
||||
return {trans: [], name: '', usphone: '', ukphone: '',}
|
||||
},
|
||||
dictTitle(state: State) {
|
||||
let title = this.currentDict.name
|
||||
if ([DictType.publicDict, DictType.customDict].includes(this.current.dictType)) {
|
||||
title += ` 第${this.currentDict.chapterIndex + 1}章`
|
||||
}
|
||||
return title
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setState(obj: any) {
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
this[key] = value
|
||||
}
|
||||
// console.log('this/', this)
|
||||
},
|
||||
async init() {
|
||||
let configStr = localStorage.getItem(SaveDictKey)
|
||||
if (configStr) {
|
||||
let obj: State = JSON.parse(configStr)
|
||||
// this.setState(obj)
|
||||
}
|
||||
actions: {
|
||||
setState(obj: any) {
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
this[key] = value
|
||||
}
|
||||
// console.log('this/', this)
|
||||
},
|
||||
async init() {
|
||||
let configStr = localStorage.getItem(SaveDictKey)
|
||||
if (configStr) {
|
||||
let obj: State = JSON.parse(configStr)
|
||||
// this.setState(obj)
|
||||
}
|
||||
|
||||
if ([
|
||||
DictType.newDict,
|
||||
DictType.wrongDict,
|
||||
DictType.skipDict,
|
||||
].includes(this.current.dictType)) {
|
||||
if ([
|
||||
DictType.newDict,
|
||||
DictType.wrongDict,
|
||||
DictType.skipDict,
|
||||
].includes(this.current.dictType)) {
|
||||
|
||||
} else {
|
||||
if ([
|
||||
DictType.publicDict,
|
||||
DictType.customDict,
|
||||
].includes(this.current.dictType)) {
|
||||
if (!this.currentDict.originWords.length) {
|
||||
let r = await fetch(`${this.currentDict.url}`)
|
||||
r.json().then(v => {
|
||||
this.currentDict.originWords = cloneDeep(v)
|
||||
this.currentDict.words = cloneDeep(v)
|
||||
this.currentDict.chapterWords = chunk(this.dict.words, this.dict.chapterWordNumber)
|
||||
this.load = true
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ([
|
||||
DictType.publicDict,
|
||||
DictType.customDict,
|
||||
].includes(this.current.dictType)) {
|
||||
if (!this.currentDict.originWords.length) {
|
||||
let r = await fetch(`${this.currentDict.url}`)
|
||||
r.json().then(v => {
|
||||
this.currentDict.originWords = cloneDeep(v)
|
||||
this.currentDict.words = cloneDeep(v)
|
||||
this.currentDict.chapterWords = chunk(this.dict.words, this.dict.chapterWordNumber)
|
||||
this.load = true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if ([
|
||||
DictType.publicArticle,
|
||||
DictType.customArticle,
|
||||
].includes(this.current.dictType)) {
|
||||
if (!this.currentDict.articles.length) {
|
||||
let r = await fetch(`${this.currentDict.url}`)
|
||||
r.json().then(v => {
|
||||
this.currentDict.articles = cloneDeep(v)
|
||||
this.load = true
|
||||
})
|
||||
}
|
||||
if ([
|
||||
DictType.publicArticle,
|
||||
DictType.customArticle,
|
||||
].includes(this.current.dictType)) {
|
||||
if (!this.currentDict.articles.length) {
|
||||
let r = await fetch(`${this.currentDict.url}`)
|
||||
r.json().then((v: any[]) => {
|
||||
this.currentDict.articles = cloneDeep(v.map(v => {
|
||||
v.id = uuidv4()
|
||||
return v
|
||||
}))
|
||||
this.load = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
saveStatistics(statistics: Statistics) {
|
||||
if (statistics.spend > 1000 * 10) {
|
||||
this.currentDict.statistics.push(statistics)
|
||||
}
|
||||
},
|
||||
async changeDict(dict: Dict, chapterIndex: number = dict.chapterIndex, chapterWordIndex: number = dict.chapterWordNumber) {
|
||||
this.saveStatistics()
|
||||
console.log('changeDict', cloneDeep(dict), chapterIndex, chapterWordIndex)
|
||||
this.current.dictType = dict.type
|
||||
if ([DictType.newDict,
|
||||
DictType.skipDict,
|
||||
DictType.wrongDict].includes(dict.type)) {
|
||||
this[dict.type].chapterIndex = chapterIndex
|
||||
this[dict.type].chapterWordIndex = chapterWordIndex
|
||||
} else {
|
||||
this.dict = cloneDeep(dict)
|
||||
if (dict.originWords.length) {
|
||||
let r = await fetch(`/public/${this.dict.url}`)
|
||||
let v = await r.json()
|
||||
this.dict.originWords = cloneDeep(v)
|
||||
this.dict.words = cloneDeep(v)
|
||||
this.dict.chapters = chunk(this.dict.words, this.dict.chapterWordNumber)
|
||||
}
|
||||
this.dict.chapterIndex = chapterIndex
|
||||
this.dict.chapterWordIndex = chapterWordIndex
|
||||
}
|
||||
emitter.emit(EventKey.resetWord)
|
||||
}
|
||||
}
|
||||
},
|
||||
saveStatistics(statistics: Statistics) {
|
||||
if (statistics.spend > 1000 * 10) {
|
||||
this.currentDict.statistics.push(statistics)
|
||||
}
|
||||
},
|
||||
async changeDict(dict: Dict, chapterIndex: number = dict.chapterIndex, chapterWordIndex: number = dict.chapterWordNumber) {
|
||||
this.saveStatistics()
|
||||
console.log('changeDict', cloneDeep(dict), chapterIndex, chapterWordIndex)
|
||||
this.current.dictType = dict.type
|
||||
if ([DictType.newDict,
|
||||
DictType.skipDict,
|
||||
DictType.wrongDict].includes(dict.type)) {
|
||||
this[dict.type].chapterIndex = chapterIndex
|
||||
this[dict.type].chapterWordIndex = chapterWordIndex
|
||||
} else {
|
||||
this.dict = cloneDeep(dict)
|
||||
if (dict.originWords.length) {
|
||||
let r = await fetch(`/public/${this.dict.url}`)
|
||||
let v = await r.json()
|
||||
this.dict.originWords = cloneDeep(v)
|
||||
this.dict.words = cloneDeep(v)
|
||||
this.dict.chapters = chunk(this.dict.words, this.dict.chapterWordNumber)
|
||||
}
|
||||
this.dict.chapterIndex = chapterIndex
|
||||
this.dict.chapterWordIndex = chapterWordIndex
|
||||
}
|
||||
emitter.emit(EventKey.resetWord)
|
||||
}
|
||||
},
|
||||
})
|
||||
252
src/types.ts
252
src/types.ts
@@ -1,15 +1,17 @@
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
|
||||
export type Word = {
|
||||
"name": string,
|
||||
"usphone": string,
|
||||
"ukphone": string,
|
||||
"trans": string[]
|
||||
"name": string,
|
||||
"usphone": string,
|
||||
"ukphone": string,
|
||||
"trans": string[]
|
||||
}
|
||||
|
||||
export const DefaultWord: Word = {
|
||||
name: '',
|
||||
usphone: '',
|
||||
ukphone: '',
|
||||
trans: []
|
||||
name: '',
|
||||
usphone: '',
|
||||
ukphone: '',
|
||||
trans: []
|
||||
}
|
||||
|
||||
export const SaveDictKey = 'typing-word-dict'
|
||||
@@ -24,181 +26,183 @@ export type LanguageCategoryType = 'en' | 'ja' | 'de' | 'code'
|
||||
|
||||
|
||||
export type DictionaryResource = {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
category: string
|
||||
tags: string[]
|
||||
url: string
|
||||
length: number
|
||||
language: LanguageType
|
||||
languageCategory: LanguageCategoryType
|
||||
//override default pronunciation when not undefined
|
||||
defaultPronIndex?: number
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
category: string
|
||||
tags: string[]
|
||||
url: string
|
||||
length: number
|
||||
language: LanguageType
|
||||
languageCategory: LanguageCategoryType
|
||||
//override default pronunciation when not undefined
|
||||
defaultPronIndex?: number
|
||||
}
|
||||
|
||||
export type Dictionary = {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
category: string
|
||||
tags: string[]
|
||||
url: string
|
||||
length: number
|
||||
language: LanguageType
|
||||
languageCategory: LanguageCategoryType
|
||||
// calculated in the store
|
||||
chapterCount: number
|
||||
//override default pronunciation when not undefined
|
||||
defaultPronIndex?: number
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
category: string
|
||||
tags: string[]
|
||||
url: string
|
||||
length: number
|
||||
language: LanguageType
|
||||
languageCategory: LanguageCategoryType
|
||||
// calculated in the store
|
||||
chapterCount: number
|
||||
//override default pronunciation when not undefined
|
||||
defaultPronIndex?: number
|
||||
}
|
||||
|
||||
export type PronunciationConfig = {
|
||||
name: string
|
||||
pron: PronunciationType
|
||||
name: string
|
||||
pron: PronunciationType
|
||||
}
|
||||
|
||||
export type LanguagePronunciationMapConfig = {
|
||||
defaultPronIndex: number
|
||||
pronunciation: PronunciationConfig[]
|
||||
defaultPronIndex: number
|
||||
pronunciation: PronunciationConfig[]
|
||||
}
|
||||
|
||||
export type LanguagePronunciationMap = {
|
||||
[key in LanguageType]: LanguagePronunciationMapConfig
|
||||
[key in LanguageType]: LanguagePronunciationMapConfig
|
||||
}
|
||||
|
||||
export type SoundResource = {
|
||||
key: string
|
||||
name: string
|
||||
filename: string
|
||||
key: string
|
||||
name: string
|
||||
filename: string
|
||||
}
|
||||
|
||||
export interface DictJson {
|
||||
description: string,
|
||||
category: string,
|
||||
tags: string[],
|
||||
url: string,
|
||||
length: number,
|
||||
language: string,
|
||||
languageCategory: string,
|
||||
description: string,
|
||||
category: string,
|
||||
tags: string[],
|
||||
url: string,
|
||||
length: number,
|
||||
language: string,
|
||||
languageCategory: string,
|
||||
}
|
||||
|
||||
export enum DictType {
|
||||
newDict = 'newDict',
|
||||
skipDict = 'skipDict',
|
||||
wrongDict = 'wrongDict',
|
||||
publicDict = 'publicDict',
|
||||
customDict = 'customDict',
|
||||
publicArticle = 'publicArticle',
|
||||
customArticle = 'customArticle'
|
||||
newDict = 'newDict',
|
||||
skipDict = 'skipDict',
|
||||
wrongDict = 'wrongDict',
|
||||
publicDict = 'publicDict',
|
||||
customDict = 'customDict',
|
||||
publicArticle = 'publicArticle',
|
||||
customArticle = 'customArticle'
|
||||
}
|
||||
|
||||
export const DefaultArticleWord: ArticleWord = {
|
||||
name: '',
|
||||
usphone: '',
|
||||
ukphone: '',
|
||||
trans: [],
|
||||
nextSpace: true,
|
||||
isSymbol: false,
|
||||
symbolPosition: ''
|
||||
name: '',
|
||||
usphone: '',
|
||||
ukphone: '',
|
||||
trans: [],
|
||||
nextSpace: true,
|
||||
isSymbol: false,
|
||||
symbolPosition: ''
|
||||
}
|
||||
|
||||
export interface ArticleWord extends Word {
|
||||
nextSpace: boolean,
|
||||
isSymbol: boolean,
|
||||
symbolPosition: 'start' | 'end' | '',
|
||||
nextSpace: boolean,
|
||||
isSymbol: boolean,
|
||||
symbolPosition: 'start' | 'end' | '',
|
||||
}
|
||||
|
||||
export interface Sentence {
|
||||
text: string,
|
||||
translate: string,
|
||||
words: ArticleWord[]
|
||||
text: string,
|
||||
translate: string,
|
||||
words: ArticleWord[]
|
||||
}
|
||||
|
||||
export enum TranslateType {
|
||||
custom = 'custom',
|
||||
network = 'network',
|
||||
none = 'none'
|
||||
custom = 'custom',
|
||||
network = 'network',
|
||||
none = 'none'
|
||||
}
|
||||
|
||||
export interface Article {
|
||||
title: string,
|
||||
titleTranslate: string,
|
||||
text: string,
|
||||
textCustomTranslate: string,
|
||||
textCustomTranslateIsFormat: boolean,//翻译是否格式化
|
||||
textNetworkTranslate: string,
|
||||
newWords: Word[],
|
||||
textAllWords: string[],
|
||||
sections: Sentence[][],
|
||||
useTranslateType: TranslateType
|
||||
id: string,
|
||||
title: string,
|
||||
titleTranslate: string,
|
||||
text: string,
|
||||
textCustomTranslate: string,
|
||||
textCustomTranslateIsFormat: boolean,//翻译是否格式化
|
||||
textNetworkTranslate: string,
|
||||
newWords: Word[],
|
||||
textAllWords: string[],
|
||||
sections: Sentence[][],
|
||||
useTranslateType: TranslateType
|
||||
}
|
||||
|
||||
export const DefaultArticle: Article = {
|
||||
title: '',
|
||||
titleTranslate: '',
|
||||
text: '',
|
||||
textCustomTranslate: '',
|
||||
textNetworkTranslate: '',
|
||||
textCustomTranslateIsFormat: false,
|
||||
newWords: [],
|
||||
textAllWords: [],
|
||||
sections: [],
|
||||
useTranslateType: TranslateType.network
|
||||
id: uuidv4(),
|
||||
title: '',
|
||||
titleTranslate: '',
|
||||
text: '',
|
||||
textCustomTranslate: '',
|
||||
textNetworkTranslate: '',
|
||||
textCustomTranslateIsFormat: false,
|
||||
newWords: [],
|
||||
textAllWords: [],
|
||||
sections: [],
|
||||
useTranslateType: TranslateType.network
|
||||
}
|
||||
|
||||
export interface Dict {
|
||||
name: string,
|
||||
sort: Sort,
|
||||
type: DictType,
|
||||
originWords: Word[],//原始单词
|
||||
words: Word[],
|
||||
chapterWordNumber: number,//章节单词数量
|
||||
chapterWords: Word[][],
|
||||
articles: Article[],
|
||||
chapterIndex: number,
|
||||
chapterWordIndex: number,
|
||||
statistics: Statistics[],
|
||||
url: string,
|
||||
name: string,
|
||||
sort: Sort,
|
||||
type: DictType,
|
||||
originWords: Word[],//原始单词
|
||||
words: Word[],
|
||||
chapterWordNumber: number,//章节单词数量
|
||||
chapterWords: Word[][],
|
||||
articles: Article[],
|
||||
chapterIndex: number,
|
||||
chapterWordIndex: number,
|
||||
statistics: Statistics[],
|
||||
url: string,
|
||||
}
|
||||
|
||||
export interface Statistics {
|
||||
startDate: number,//开始日期
|
||||
endDate: number//结束日期
|
||||
spend: number,//花费时间
|
||||
total: number//单词数量
|
||||
wrongWordNumber: number//错误数
|
||||
correctRate: number//正确率
|
||||
startDate: number,//开始日期
|
||||
endDate: number//结束日期
|
||||
spend: number,//花费时间
|
||||
total: number//单词数量
|
||||
wrongWordNumber: number//错误数
|
||||
correctRate: number//正确率
|
||||
}
|
||||
|
||||
export interface DisplayStatistics extends Statistics {
|
||||
wrongWords: Word[]
|
||||
wrongWords: Word[]
|
||||
}
|
||||
|
||||
export const DefaultDisplayStatistics: DisplayStatistics = {
|
||||
startDate: Date.now(),
|
||||
endDate: -1,
|
||||
spend: -1,
|
||||
total: -1,
|
||||
correctRate: -1,
|
||||
wrongWordNumber: -1,
|
||||
wrongWords: [],
|
||||
startDate: Date.now(),
|
||||
endDate: -1,
|
||||
spend: -1,
|
||||
total: -1,
|
||||
correctRate: -1,
|
||||
wrongWordNumber: -1,
|
||||
wrongWords: [],
|
||||
}
|
||||
|
||||
export enum Sort {
|
||||
normal = 0,
|
||||
random = 1,
|
||||
reverse = 2
|
||||
normal = 0,
|
||||
random = 1,
|
||||
reverse = 2
|
||||
}
|
||||
|
||||
|
||||
export const ShortKeyMap = {
|
||||
Show: 'Escape',
|
||||
Ignore: 'Tab',
|
||||
Remove: '`',
|
||||
Collect: 'Enter',
|
||||
Show: 'Escape',
|
||||
Ignore: 'Tab',
|
||||
Remove: '`',
|
||||
Collect: 'Enter',
|
||||
}
|
||||
|
||||
export enum TranslateEngine {
|
||||
Baidu = 0,
|
||||
Baidu = 0,
|
||||
}
|
||||
Reference in New Issue
Block a user