This commit is contained in:
zyronon
2023-09-28 01:15:13 +08:00
parent 81dad99b46
commit 5e4051c192
6 changed files with 319 additions and 280 deletions

View File

@@ -21,11 +21,13 @@
"compromise": "^14.10.0",
"copy-to-clipboard": "^3.3.3",
"element-plus": "^2.3.9",
"file-saver": "^2.0.5",
"hover.css": "^2.3.2",
"localforage": "^1.10.0",
"lodash-es": "^4.17.21",
"mitt": "^3.0.1",
"pinia": "^2.1.6",
"sentence-splitter": "^4.2.1",
"swiper": "^10.1.0",
"tesseract.js": "^4.1.1",
"uuid": "^9.0.1",

31
pnpm-lock.yaml generated
View File

@@ -26,6 +26,9 @@ dependencies:
element-plus:
specifier: ^2.3.9
version: 2.3.14(vue@3.3.4)
file-saver:
specifier: ^2.0.5
version: 2.0.5
hover.css:
specifier: ^2.3.2
version: 2.3.2
@@ -41,6 +44,9 @@ dependencies:
pinia:
specifier: ^2.1.6
version: 2.1.6(typescript@5.2.2)(vue@3.3.4)
sentence-splitter:
specifier: ^4.2.1
version: 4.2.1
swiper:
specifier: ^10.1.0
version: 10.3.0
@@ -819,6 +825,10 @@ packages:
resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==}
dev: false
/@textlint/ast-node-types@13.3.3:
resolution: {integrity: sha512-KCpJppfX3Km69twa6SmVEJ8mkyAZSrxw3XaaLQSlpc7PWnLUJSCHGPVECI1nSUDhiTd1r6zlRvWuyIAZJiov+A==}
dev: false
/@tsconfig/node10@1.0.9:
resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
requiresBuild: true
@@ -1201,6 +1211,10 @@ packages:
resolution: {integrity: sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==}
dev: false
/boundary@2.0.0:
resolution: {integrity: sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==}
dev: false
/brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies:
@@ -1700,6 +1714,10 @@ packages:
escape-string-regexp: 1.0.5
dev: true
/file-saver@2.0.5:
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
dev: false
/fill-range@7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'}
@@ -2684,6 +2702,13 @@ packages:
lru-cache: 6.0.0
dev: true
/sentence-splitter@4.2.1:
resolution: {integrity: sha512-zn7awgCg40lyb+fe6N/fRJS3r+Ag3SmrmiYHZZSM9oQ2HTnwSMooUgQXSMLeQdi5HWMYOnhrovE2JZ3pyGU0dg==}
dependencies:
'@textlint/ast-node-types': 13.3.3
structured-source: 4.0.0
dev: false
/side-channel@1.0.4:
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
dependencies:
@@ -2743,6 +2768,12 @@ packages:
acorn: 8.10.0
dev: true
/structured-source@4.0.0:
resolution: {integrity: sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==}
dependencies:
boundary: 2.0.0
dev: false
/suffix-thumb@5.0.2:
resolution: {integrity: sha512-I5PWXAFKx3FYnI9a+dQMWNqTxoRt6vdBdb0O+BJ1sxXCWtSoQCusc13E58f+9p4MYx/qCnEMkD5jac6K2j3dgA==}
dev: false

View File

@@ -99,6 +99,15 @@ a {
border-radius: 10rem;
}
/* 火狐美化滚动条 */
* {
scrollbar-color: $second #f3f4f9;
/* 滑块颜色 滚动条背景颜色 */
scrollbar-width: thin;
/* 滚动条宽度有三种thin、auto、none */
}
footer {
box-sizing: content-box;
height: $footer-height;

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import {saveAs} from 'file-saver';
import {watch} from "vue";
import {Article, DefaultArticle, Sentence, TranslateEngine, TranslateType} from "@/types.ts";
import BaseButton from "@/components/BaseButton.vue";
@@ -54,12 +54,14 @@ watch(() => props.article, n => {
if (article.textCustomTranslate.trim()) {
if (!article.textCustomTranslateIsFormat) {
let r = getSplitTranslateText(article.textCustomTranslate)
if (r) article.textCustomTranslate = r
ElMessage({
message: '检测到本地翻译未格式化,已自动格式',
type: 'success',
duration: 5000
})
if (r) {
article.textCustomTranslate = r
ElMessage({
message: '检测到本地翻译未格式化,已自动格式化',
type: 'success',
duration: 5000
})
}
}
}
}
@@ -240,31 +242,9 @@ function save(option: 'save' | 'next', mute: boolean = false) {
saveTemp()
}
function changeTranslateType() {
if (article.text.trim()) {
if (article.useTranslateType === TranslateType.custom) {
if (article.textCustomTranslate.trim()) {
renewSectionTranslates(article, article.textCustomTranslate)
} else {
renewSectionTexts(article)
}
}
if (article.useTranslateType === TranslateType.network) {
if (article.textNetworkTranslate.trim()) {
renewSectionTranslates(article, article.textNetworkTranslate)
} else {
renewSectionTexts(article)
}
}
if (article.useTranslateType === TranslateType.none) {
renewSectionTexts(article)
}
}
}
function selectArticle(item: Article) {
article = cloneDeep(item)
if (!article?.sections?.length){
if (!article?.sections?.length) {
renewSections()
}
console.log('article', article)
@@ -277,6 +257,22 @@ function add() {
article = cloneDeep(DefaultArticle)
}
//TODO
function importData() {
}
function exportData() {
let data = {
name: base.currentEditDict.name,
articles: base.currentEditDict.articles,
url: base.currentEditDict.url,
statistics: location.origin + base.currentEditDict.statistics,
}
let blob = new Blob([JSON.stringify(data, null, 2)], {type: "text/plain;charset=utf-8"});
saveAs(blob, `${data.name}.json`);
}
</script>
<template>
@@ -298,8 +294,8 @@ function add() {
</template>
</List>
<div class="footer">
<BaseButton>导入</BaseButton>
<BaseButton>导出</BaseButton>
<BaseButton @click="importData">导入</BaseButton>
<BaseButton @click="exportData">导出</BaseButton>
<BaseButton @click="add">新增</BaseButton>
</div>
</div>
@@ -337,7 +333,7 @@ function add() {
<span>标题</span>
<el-radio-group
v-model="article.useTranslateType"
@change="changeTranslateType"
@change="renewSections"
>
<el-radio-button :label="TranslateType.custom">本地翻译</el-radio-button>
<el-radio-button :label="TranslateType.network">网络翻译</el-radio-button>
@@ -490,6 +486,7 @@ function add() {
display: flex;
gap: $space;
padding: $space;
padding-top: 10rem;
//opacity: 0;
}
@@ -561,7 +558,7 @@ function add() {
.sentence {
margin-bottom: 20rem;
&:last-child{
&:last-child {
margin-bottom: 0;
}

View File

@@ -146,7 +146,7 @@ function delItem(item: T) {
.list-wrapper {
flex: 1;
overflow: auto;
overflow: overlay;
padding-right: 5rem;
.search {

View File

@@ -5,258 +5,258 @@ 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',
}
],
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
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
}
},
getters: {
skipWordNames: (state: State) => {
return state.skipWordDict.originWords.map(v => v.name.toLowerCase())
},
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
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
}
},
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)) {
} 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
})
}
}
},
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)) {
} 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: 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)
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)
}
},
})