This commit is contained in:
zyronon
2023-10-11 02:17:34 +08:00
parent b651a50907
commit aa9030b66a
18 changed files with 1714 additions and 1259 deletions

6
components.d.ts vendored
View File

@@ -8,7 +8,6 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
Add: typeof import('./src/components/Toolbar/Add.vue')['default']
AddArticle: typeof import('./src/components/Practice/AddArticle.vue')['default']
AddArticle2: typeof import('./src/components/Add/AddArticle2.vue')['default']
Backgorund: typeof import('./src/components/Backgorund.vue')['default']
BaseButton: typeof import('./src/components/BaseButton.vue')['default']
@@ -20,7 +19,12 @@ declare module 'vue' {
DictGroup: typeof import('./src/components/Toolbar/DictGroup.vue')['default']
DictList: typeof import('./src/components/DictList.vue')['default']
DictModal: typeof import('./src/components/Toolbar/DictModal.vue')['default']
Edit: typeof import('./src/components/Article/Edit.vue')['default']
EditAbleText: typeof import('./src/components/EditAbleText.vue')['default']
EditArticle: typeof import('./src/components/Article/EditArticle.vue')['default']
EditArticleModal: typeof import('./src/components/Article/EditArticleModal.vue')['default']
EditBatchArticleModal: typeof import('./src/components/Article/EditBatchArticleModal.vue')['default']
EditSingleArticleModal: typeof import('./src/components/Article/EditSingleArticleModal.vue')['default']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElOption: typeof import('element-plus/es')['ElOption']

View File

@@ -36,6 +36,7 @@
},
"devDependencies": {
"@iconify/vue": "^4.1.1",
"@types/file-saver": "^2.0.5",
"@types/lodash-es": "^4.17.9",
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.0.1",

1229
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,12 +4,9 @@ import {onMounted, watch} from "vue";
import {useBaseStore} from "@/stores/base.ts";
import {SaveDictKey} from "@/types.ts"
import Practice from "@/components/Practice/Practice.vue"
import AddArticle from "@/components/Practice/AddArticle.vue";
import {useEventListener, useStartKeyboardEventListener} from "@/hooks/event.ts";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {useSettingStore} from "@/stores/setting.ts";
import BatchAddArticle from "@/components/Add/BatchAddArticle.vue";
const store = useBaseStore()
const runtimeStore = useRuntimeStore()

View File

@@ -1,14 +1,14 @@
<script setup lang="ts">
import {saveAs} from 'file-saver';
import {watch} from "vue";
import {saveAs} from "file-saver";
import {onMounted, reactive, watch} from "vue";
import {Article, DefaultArticle, DictType, Sentence, Sort, TranslateEngine, TranslateType} from "@/types.ts";
import BaseButton from "@/components/BaseButton.vue";
import {
getNetworkTranslate,
getSentenceAllText,
getSentenceAllTranslateText,
renewSectionTranslates,
renewSectionTexts
renewSectionTexts,
renewSectionTranslates
} from "@/hooks/translate.ts";
import * as copy from "copy-to-clipboard";
import {getSplitTranslateText} from "@/hooks/article.ts";
@@ -21,36 +21,34 @@ import {$ref} from "vue/macros";
import List from "@/components/List.vue";
import {v4 as uuidv4} from 'uuid';
import {Icon} from "@iconify/vue";
import Modal from "@/components/Modal/Modal.vue";
import {emitter, EventKey} from "@/utils/eventBus.ts";
interface IProps {
article?: Article
}
const props = withDefaults(defineProps<IProps>(), {
article: () => DefaultArticle
})
const base = useBaseStore()
let article = $ref<Article>(cloneDeep(props.article))
let article = $ref<Article>(cloneDeep(DefaultArticle))
let networkTranslateEngine = $ref('baidu')
let progress = $ref(0)
let failCount = $ref(0)
const data = reactive({
show: false
})
const TranslateEngineOptions = [
{value: 'baidu', label: '百度'},
{value: 'youdao', label: '有道'},
]
const emit = defineEmits([
'update:modelValue',
'close',
'save'
])
// useDisableEventListener(() => props.modelValue)
// useDisableEventListener(() => data.show)
watch(() => props.article, n => {
if (n) {
article = cloneDeep(props.article)
onMounted(() => {
emitter.on(EventKey.openArticleListModal, (val: Article) => {
console.log('val',val)
data.show = true
article = cloneDeep(val)
if (article.text.trim()) {
if (article.useTranslateType === TranslateType.custom) {
if (article.textCustomTranslate.trim()) {
@@ -69,7 +67,7 @@ watch(() => props.article, n => {
}
}
renewSections()
}
})
})
async function startNetworkTranslate() {
@@ -359,166 +357,176 @@ function exportData() {
</script>
<template>
<div class="add-article">
<div class="slide">
<header>
<div class="dict-name">{{ base.currentEditDict.name }}</div>
<BaseIcon title="选择其他词典/文章" icon="carbon:change-catalog"/>
</header>
<List
v-model:list="base.currentEditDict.articles"
:select-item="article"
@del-select-item="article = cloneDeep(DefaultArticle)"
@select-item="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">
<div class="import" v-if="showImportBtn">
<BaseButton>导入</BaseButton>
<input type="file" accept="application/json" @change="importData">
<Modal
v-model="data.show"
:full-screen="true"
:header="false"
>
<div class="add-article">
<div class="slide">
<header>
<div class="dict-name">{{ base.currentEditDict.name }}</div>
<BaseIcon title="选择其他词典/文章" icon="carbon:change-catalog"/>
</header>
<List
v-model:list="base.currentEditDict.articles"
:select-item="article"
@del-select-item="article = cloneDeep(DefaultArticle)"
@select-item="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">
<div class="import" v-if="showImportBtn">
<BaseButton>导入</BaseButton>
<input type="file" accept="application/json" @change="importData">
</div>
<BaseButton @click="exportData">导出</BaseButton>
<BaseButton @click="add">新增</BaseButton>
</div>
<BaseButton @click="exportData">导出</BaseButton>
<BaseButton @click="add">新增</BaseButton>
</div>
</div>
<div class="content">
<div class="row">
<div class="title">原文</div>
<div class="item">
<div class="label">标题</div>
<textarea
v-model="article.title"
type="textarea"
class="base-textarea"
placeholder="请填写原文标题"
>
</textarea>
</div>
<div class="item basic">
<div class="label">正文</div>
<textarea
v-model="article.text"
@input="renewSections"
:readonly="![100,0].includes(progress)"
type="textarea"
class="base-textarea"
placeholder="请填写原文正文"
>
</textarea>
</div>
</div>
<div class="row">
<div class="title">译文</div>
<div class="item">
<div class="label">
<span>标题</span>
<el-radio-group
v-model="article.useTranslateType"
@change="renewSections"
<div class="content">
<div class="row">
<div class="title">原文</div>
<div class="item">
<div class="label">标题</div>
<textarea
v-model="article.title"
type="textarea"
class="base-textarea"
placeholder="请填写原文标题"
>
<el-radio-button :label="TranslateType.custom">本地翻译</el-radio-button>
<el-radio-button :label="TranslateType.network">网络翻译</el-radio-button>
<el-radio-button :label="TranslateType.none">不需要翻译</el-radio-button>
</el-radio-group>
</div>
<textarea
v-model="article.titleTranslate"
type="textarea"
class="base-textarea"
placeholder="请填写翻译标题"
>
</textarea>
</div>
<div class="item basic">
<div class="label">正文</div>
<textarea
v-model="article.text"
@input="renewSections"
:readonly="![100,0].includes(progress)"
type="textarea"
class="base-textarea"
placeholder="请填写原文正文"
>
</textarea>
</div>
</div>
<div class="item basic">
<div class="label">
<span>正文</span>
<div class="translate-item" v-if="article.useTranslateType === TranslateType.network">
<el-progress :percentage="progress"
:duration="30"
:striped="progress !== 100"
:striped-flow="progress !== 100"
:stroke-width="8"
:show-text="true"/>
<el-select v-model="networkTranslateEngine"
style="width: 80rem;"
<div class="row">
<div class="title">译文</div>
<div class="item">
<div class="label">
<span>标题</span>
<el-radio-group
v-model="article.useTranslateType"
@change="renewSections"
>
<el-option
v-for="item in TranslateEngineOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<BaseButton
size="small"
@click="startNetworkTranslate"
:loading="progress!==0 && progress !== 100"
>开始翻译
</BaseButton>
<el-radio-button :label="TranslateType.custom">本地翻译</el-radio-button>
<el-radio-button :label="TranslateType.network">网络翻译</el-radio-button>
<el-radio-button :label="TranslateType.none">不需要翻译</el-radio-button>
</el-radio-group>
</div>
<textarea
v-model="article.titleTranslate"
type="textarea"
class="base-textarea"
placeholder="请填写翻译标题"
>
</textarea>
</div>
<div class="item basic">
<div class="label">
<span>正文</span>
<div class="translate-item" v-if="article.useTranslateType === TranslateType.network">
<el-progress :percentage="progress"
:duration="30"
:striped="progress !== 100"
:striped-flow="progress !== 100"
:stroke-width="8"
:show-text="true"/>
<el-select v-model="networkTranslateEngine"
style="width: 80rem;"
>
<el-option
v-for="item in TranslateEngineOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<BaseButton
size="small"
@click="startNetworkTranslate"
:loading="progress!==0 && progress !== 100"
>开始翻译
</BaseButton>
</div>
</div>
<textarea
v-if="article.useTranslateType === TranslateType.custom"
v-model="article.textCustomTranslate"
@input="renewSections"
:readonly="![100,0].includes(progress)"
@blur="onBlur"
@focus="onFocus"
type="textarea"
class="base-textarea"
placeholder="请填写翻译正文"
>
</textarea>
<textarea
v-if="article.useTranslateType === TranslateType.network"
v-model="article.textNetworkTranslate"
@input="renewSections"
@blur="onBlur"
@focus="onFocus"
type="textarea"
class="base-textarea"
placeholder="等待网络翻译中..."
>
</textarea>
</div>
</div>
<div class="row">
<div class="title">译文对照</div>
<div class="article-translate">
<div class="section" v-for="(item,indexI) in article.sections">
<div class="sentence" v-for="(sentence,indexJ) in item">
<EditAbleText
:value="sentence.text"
@save="(e:string) => saveSentenceText(sentence,e)"
/>
<EditAbleText
:value="sentence.translate"
@save="(e:string) => saveSentenceTranslate(sentence,e)"
/>
</div>
</div>
</div>
<textarea
v-if="article.useTranslateType === TranslateType.custom"
v-model="article.textCustomTranslate"
@input="renewSections"
:readonly="![100,0].includes(progress)"
@blur="onBlur"
@focus="onFocus"
type="textarea"
class="base-textarea"
placeholder="请填写翻译正文"
>
</textarea>
<textarea
v-if="article.useTranslateType === TranslateType.network"
v-model="article.textNetworkTranslate"
@input="renewSections"
@blur="onBlur"
@focus="onFocus"
type="textarea"
class="base-textarea"
placeholder="等待网络翻译中..."
>
</textarea>
</div>
</div>
<div class="row">
<div class="title">译文对照</div>
<div class="article-translate">
<div class="section" v-for="(item,indexI) in article.sections">
<div class="sentence" v-for="(sentence,indexJ) in item">
<EditAbleText
:value="sentence.text"
@save="(e:string) => saveSentenceText(sentence,e)"
/>
<EditAbleText
:value="sentence.translate"
@save="(e:string) => saveSentenceTranslate(sentence,e)"
/>
<div class="options" v-if="article.text.trim()">
<div class="warning">
<template v-if="failCount">
<Icon icon="typcn:warning-outline"/>
共有{{ failCount }}句没有翻译
</template>
</div>
<div class="left">
<BaseButton @click="save('save')">保存</BaseButton>
<BaseButton @click="save('next')">保存并添加下一篇</BaseButton>
</div>
</div>
</div>
<div class="options" v-if="article.text.trim()">
<div class="warning">
<template v-if="failCount">
<Icon icon="typcn:warning-outline"/>
共有{{ failCount }}句没有翻译
</template>
</div>
<div class="left">
<BaseButton @click="save('save')">保存</BaseButton>
<BaseButton @click="save('next')">保存并添加下一篇</BaseButton>
</div>
</div>
</div>
<Icon @click="data.show = false"
class="close hvr-grow pointer"
width="20" color="#929596"
icon="ion:close-outline"/>
</div>
</div>
</Modal>
</template>
<style scoped lang="scss">
@@ -526,6 +534,7 @@ function exportData() {
.add-article {
//position: fixed;
position: relative;
left: 0;
top: 0;
z-index: 9;
@@ -536,6 +545,12 @@ function exportData() {
background: var(--color-main-bg);
display: flex;
.close {
position: absolute;
right: 20rem;
top: 20rem;
}
.slide {
height: 100%;
background: white;

View File

@@ -0,0 +1,491 @@
<script setup lang="ts">
import {Article, DefaultArticle, Sentence, TranslateEngine, TranslateType} from "@/types.ts";
import BaseButton from "@/components/BaseButton.vue";
import EditAbleText from "@/components/EditAbleText.vue";
import {Icon} from "@iconify/vue";
import {
getNetworkTranslate,
getSentenceAllText,
getSentenceAllTranslateText,
renewSectionTexts,
renewSectionTranslates
} from "@/hooks/translate.ts";
import * as copy from "copy-to-clipboard";
import {$ref} from "vue/macros";
import {MessageBox} from "@/utils/MessageBox.tsx";
import {getSplitTranslateText} from "@/hooks/article.ts";
import {cloneDeep} from "lodash-es";
import {v4 as uuidv4} from "uuid";
import {watch} from "vue";
import {useBaseStore} from "@/stores/base.ts";
interface IProps {
article?: Article
}
const props = withDefaults(defineProps<IProps>(), {
article: () => cloneDeep(DefaultArticle),
})
let networkTranslateEngine = $ref('baidu')
let progress = $ref(0)
let failCount = $ref(0)
const TranslateEngineOptions = [
{value: 'baidu', label: '百度'},
{value: 'youdao', label: '有道'},
]
const base = useBaseStore()
let article = $ref<Article>(cloneDeep(DefaultArticle))
watch(() => props.article, val => {
article = cloneDeep(val)
if (article.text.trim()) {
if (article.useTranslateType === TranslateType.custom) {
if (article.textCustomTranslate.trim()) {
if (!article.textCustomTranslateIsFormat) {
let r = getSplitTranslateText(article.textCustomTranslate)
if (r) {
article.textCustomTranslate = r
ElMessage({
message: '检测到本地翻译未格式化,已自动格式化',
type: 'success',
duration: 5000
})
}
}
}
}
}
renewSections()
console.log('ar', article)
}, {immediate: true})
function renewSections() {
if (article.text.trim()) {
renewSectionTexts(article)
if (article.useTranslateType === TranslateType.custom) {
failCount = renewSectionTranslates(article, article.textCustomTranslate)
}
if (article.useTranslateType === TranslateType.network) {
failCount = renewSectionTranslates(article, article.textNetworkTranslate)
}
} else {
article.sections = []
}
}
function appendTranslate(str: string) {
if (article.useTranslateType === TranslateType.custom) {
article.textCustomTranslate += str
}
if (article.useTranslateType === TranslateType.network) {
article.textNetworkTranslate += str
}
}
function onPaste(event: ClipboardEvent) {
event.preventDefault()
// @ts-ignore
let paste = (event.clipboardData || window.clipboardData).getData("text");
return MessageBox.confirm(
'是否需要自动分句',
'提示',
() => {
let r = getSplitTranslateText(paste)
if (r) {
appendTranslate(r)
renewSections()
}
},
() => {
appendTranslate(paste)
renewSections()
},
{
confirmButtonText: '需要',
cancelButtonText: '关闭',
}
)
}
function onBlur() {
document.removeEventListener('paste', onPaste);
}
function onFocus() {
document.addEventListener('paste', onPaste);
}
function save(option: 'save' | 'next', mute: boolean = false) {
console.log('article', article)
copy(JSON.stringify(article))
if (mute) {
if (article.title.trim() && article.text.trim()) {
return false
}
}
article.title = article.title.trim()
article.titleTranslate = article.titleTranslate.trim()
article.text = article.text.trim()
article.textCustomTranslate = article.textCustomTranslate.trim()
article.textNetworkTranslate = article.textNetworkTranslate.trim()
if (!article.title) {
return ElMessage.error('请填写标题!')
}
if (!article.text) {
return ElMessage.error('请填写正文!')
}
let has = base.currentEditDict.articles.find((item: Article) => item.title === article.title)
if (has && !article.id) {
return ElMessage.error('已存在同名文章!')
}
const saveTemp = () => {
article.textCustomTranslateIsFormat = true
// emit('close')
// emit('save', cloneDeep(article))
if (article.id) {
let rIndex = base.currentEditDict.articles.findIndex(v => v.id === article.id)
if (rIndex > -1) {
base.currentEditDict.articles[rIndex] = cloneDeep(article)
}
} else {
let data = {...article, id: uuidv4()}
base.currentEditDict.articles.push(data)
if (option === 'save') {
article = cloneDeep(data)
}
}
if (option === 'next') {
article = cloneDeep(DefaultArticle)
}
//TODO 保存完成后滚动到对应位置
if (!mute) {
ElMessage.success('保存成功!')
}
}
if (article.useTranslateType === TranslateType.network) {
if (!article.textNetworkTranslate) {
return MessageBox.confirm(
'您选择了“网络翻译”,但译文内容却为空白,是否修改为“不需要翻译”并保存?',
'提示',
() => {
article.useTranslateType = TranslateType.none
saveTemp()
},
() => void 0,
)
}
}
if (article.useTranslateType === TranslateType.custom) {
if (!article.textCustomTranslate) {
return MessageBox.confirm(
'您选择了“本地翻译”,但译文内容却为空白,是否修改为“不需要翻译”并保存?',
'提示',
() => {
article.useTranslateType = TranslateType.none
saveTemp()
},
() => void 0,
)
}
}
saveTemp()
}
async function startNetworkTranslate() {
if (!article.title.trim()) {
return ElMessage.error('请填写标题!')
}
if (!article.text.trim()) {
return ElMessage.error('请填写正文!')
}
renewSectionTexts(article)
article.textNetworkTranslate = ''
//注意!!!
//这里需要用异步因为watch了article.networkTranslate改变networkTranslate了之后会重新设置article.sections
//导致getNetworkTranslate里面拿到的article.sections是废弃的值
setTimeout(async () => {
await getNetworkTranslate(article, TranslateEngine.Baidu, true, (v: number) => {
progress = v
})
copy(JSON.stringify(article.sections))
})
}
function saveSentenceTranslate(sentence: Sentence, val: string) {
sentence.translate = val
if (article.useTranslateType === TranslateType.custom) {
article.textCustomTranslate = getSentenceAllTranslateText(article)
}
if (article.useTranslateType === TranslateType.network) {
article.textNetworkTranslate = getSentenceAllTranslateText(article)
}
renewSections()
}
function saveSentenceText(sentence: Sentence, val: string) {
sentence.text = val
article.text = getSentenceAllText(article)
renewSections()
}
defineExpose({save})
</script>
<template>
<div class="content">
<div class="row">
<div class="title">原文</div>
<div class="item">
<div class="label">标题</div>
<textarea
v-model="article.title"
type="textarea"
class="base-textarea"
placeholder="请填写原文标题"
>
</textarea>
</div>
<div class="item basic">
<div class="label">正文</div>
<textarea
v-model="article.text"
@input="renewSections"
:readonly="![100,0].includes(progress)"
type="textarea"
class="base-textarea"
placeholder="请填写原文正文"
>
</textarea>
</div>
</div>
<div class="row">
<div class="title">译文</div>
<div class="item">
<div class="label">
<span>标题</span>
<el-radio-group
v-model="article.useTranslateType"
@change="renewSections"
>
<el-radio-button :label="TranslateType.custom">本地翻译</el-radio-button>
<el-radio-button :label="TranslateType.network">网络翻译</el-radio-button>
<el-radio-button :label="TranslateType.none">不需要翻译</el-radio-button>
</el-radio-group>
</div>
<textarea
v-model="article.titleTranslate"
type="textarea"
class="base-textarea"
placeholder="请填写翻译标题"
>
</textarea>
</div>
<div class="item basic">
<div class="label">
<span>正文</span>
<div class="translate-item" v-if="article.useTranslateType === TranslateType.network">
<el-progress :percentage="progress"
:duration="30"
:striped="progress !== 100"
:striped-flow="progress !== 100"
:stroke-width="8"
:show-text="true"/>
<el-select v-model="networkTranslateEngine"
style="width: 80rem;"
>
<el-option
v-for="item in TranslateEngineOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<BaseButton
size="small"
@click="startNetworkTranslate"
:loading="progress!==0 && progress !== 100"
>开始翻译
</BaseButton>
</div>
</div>
<textarea
v-if="article.useTranslateType === TranslateType.custom"
v-model="article.textCustomTranslate"
@input="renewSections"
:readonly="![100,0].includes(progress)"
@blur="onBlur"
@focus="onFocus"
type="textarea"
class="base-textarea"
placeholder="请填写翻译正文"
>
</textarea>
<textarea
v-if="article.useTranslateType === TranslateType.network"
v-model="article.textNetworkTranslate"
@input="renewSections"
@blur="onBlur"
@focus="onFocus"
type="textarea"
class="base-textarea"
placeholder="等待网络翻译中..."
>
</textarea>
</div>
</div>
<div class="row">
<div class="title">译文对照</div>
<div class="article-translate">
<div class="section" v-for="(item,indexI) in article.sections">
<div class="sentence" v-for="(sentence,indexJ) in item">
<EditAbleText
:value="sentence.text"
@save="(e:string) => saveSentenceText(sentence,e)"
/>
<EditAbleText
:value="sentence.translate"
@save="(e:string) => saveSentenceTranslate(sentence,e)"
/>
</div>
</div>
</div>
<div class="options" v-if="article.text.trim()">
<div class="warning">
<template v-if="failCount && article.useTranslateType !== TranslateType.none">
<Icon icon="typcn:warning-outline"/>
共有{{ failCount }}句没有翻译
</template>
</div>
<div class="left">
<BaseButton @click="save('save')">保存</BaseButton>
<BaseButton @click="save('next')">保存并添加下一篇</BaseButton>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
@import "@/assets/css/style.scss";
.content {
flex: 1;
display: flex;
gap: $space;
padding: $space;
padding-top: 10rem;
}
.row {
flex: 1;
width: 33%;
//height: 100%;
display: flex;
flex-direction: column;
//opacity: 0;
.basic {
flex: 1;
display: flex;
flex-direction: column;
}
&:nth-child(2) {
opacity: 1;
}
.title {
font-size: 22rem;
text-align: center;
}
.item {
width: 100%;
//margin-bottom: 10rem;
.label {
height: 45rem;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16rem;
}
}
.translate-item {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
gap: calc($space / 2);
}
.el-progress {
flex: 1;
}
.article-translate {
margin-top: 10rem;
margin-bottom: 20rem;
flex: 1;
overflow: auto;
border-radius: 8rem;
.section {
background: white;
margin-bottom: 20rem;
padding: $space;
border-radius: 8rem;
&:last-child {
margin-bottom: 0;
}
.sentence {
margin-bottom: 20rem;
&:last-child {
margin-bottom: 0;
}
.text {
font-size: 18rem;
}
}
}
}
.options {
display: flex;
align-items: center;
justify-content: space-between;
.warning {
display: flex;
align-items: center;
font-size: 20rem;
color: red;
gap: 10rem;
}
.left {
gap: $space;
display: flex;
}
}
}
</style>

View File

@@ -0,0 +1,264 @@
<script setup lang="ts">
import {saveAs} from "file-saver";
import {reactive} from "vue";
import {Article, DefaultArticle, DictType, Sort} from "@/types.ts";
import BaseButton from "@/components/BaseButton.vue";
import {cloneDeep} from "lodash-es";
import BaseIcon from "@/components/BaseIcon.vue";
import {useBaseStore} from "@/stores/base.ts";
import {$ref} from "vue/macros";
import List from "@/components/List.vue";
import {v4 as uuidv4} from 'uuid';
import {Icon} from "@iconify/vue";
import Modal from "@/components/Modal/Modal.vue";
import EditArticle from "@/components/Article/EditArticle.vue";
import {onMounted} from "vue";
import {emitter, EventKey} from "@/utils/eventBus.ts";
const base = useBaseStore()
let article = $ref<Article>(cloneDeep(DefaultArticle))
let show = $ref(false)
let showImportBtn = $ref(true)
let editArticleRef = $ref()
onMounted(() => {
emitter.on(EventKey.openArticleListModal, (val: Article) => {
console.log('val', val)
show = true
if (val) {
article = cloneDeep(val)
}
})
})
// useDisableEventListener(() => data.show)
function selectArticle(item: Article) {
article = cloneDeep(item)
// console.log('article', article)
}
function add() {
if (article.title.trim() && article.text.trim()) {
// return save('next', true)
}
// editArticleRef.save('save',true)
article = cloneDeep(DefaultArticle)
}
function importData(e: Event) {
showImportBtn = false
let file = e.target.files[0]
let reader = new FileReader();//新建一个FileReader
reader.readAsText(file, "UTF-8");//读取文件
reader.onload = function (evt) { //读取完文件之后会回来这里
let fileString = evt.target.result; // 读取文件内容
// console.log('fileString', fileString)
try {
let obj: any = JSON.parse(fileString)
console.log('obj', obj)
if (!obj?.name) {
showImportBtn = true
return ElMessage.error('请填写词典名称!')
} else {
if (base.myDicts.find(v => v.name === obj.name)) {
showImportBtn = true
return ElMessage.error('词典名称已存在!')
}
}
if (!obj?.articles) {
showImportBtn = true
return ElMessage.error('请填写文章!')
}
if (!obj?.articles instanceof Array) {
showImportBtn = true
return ElMessage.error('请填写文章!')
}
for (let i = 0; i < obj.articles.length; i++) {
let item = obj.articles[i]
if (!item?.title) {
showImportBtn = true
return ElMessage.error(`请填写第${i + 1}篇文章名称!`)
}
if (!item?.text) {
showImportBtn = true
return ElMessage.error(`请填写第${i + 1}篇文章正文!`)
}
if (item?.useTranslateType === 'custom') {
if (!item?.textCustomTranslate) {
showImportBtn = true
return ElMessage.error(`请填写第${i + 1}篇文章 翻译 正文!`)
}
}
if (!obj.articles[i]?.titleTranslate) obj.articles[i].titleTranslate = ''
if (!obj.articles[i]?.textFormat) obj.articles[i].textFormat = ''
if (!obj.articles[i]?.textCustomTranslate) obj.articles[i].textCustomTranslate = ''
if (!obj.articles[i]?.newWords) obj.articles[i].newWords = []
if (!obj.articles[i]?.textCustomTranslateIsFormat) obj.articles[i].textCustomTranslateIsFormat = false
if (!obj.articles[i]?.useTranslateType) obj.articles[i].useTranslateType = 'none'
if (!obj.articles[i]?.textAllWords) obj.articles[i].textAllWords = []
if (!obj.articles[i]?.sections) obj.articles[i].sections = []
obj.articles[i].id = uuidv4()
}
obj.sort = Sort.normal
obj.type = DictType.customArticle
obj.originWords = []
obj.words = []
obj.chapterWords = []
obj.chapterWordNumber = 0
obj.chapterIndex = 0
obj.chapterWordIndex = 0
obj.url = ''
if (!obj.statistics) obj.statistics = []
ElMessage.success({
message: '导入成功,已切换到',
duration: 5000
})
base.myDicts.push(obj)
base.current.editIndex = base.myDicts.length - 1
showImportBtn = true
} catch (e) {
showImportBtn = true
ElMessage.error('文件解析失败,报错原因:' + e.message)
}
}
}
function exportData() {
let data = {
name: base.currentEditDict.name,
articles: cloneDeep(base.currentEditDict.articles).map(v => {
delete v.sections
delete v.id
return v
}),
url: location.origin + base.currentEditDict.url,
statistics: 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>
<Modal
v-model="show"
:full-screen="true"
:header="false"
>
<div class="add-article">
<div class="slide">
<header>
<div class="dict-name">{{ base.currentEditDict.name }}</div>
<BaseIcon title="选择其他词典/文章" icon="carbon:change-catalog"/>
</header>
<List
v-model:list="base.currentEditDict.articles"
:select-item="article"
@del-select-item="article = cloneDeep(DefaultArticle)"
@select-item="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">
<div class="import" v-if="showImportBtn">
<BaseButton>导入</BaseButton>
<input type="file" accept="application/json" @change="importData">
</div>
<BaseButton @click="exportData">导出</BaseButton>
<BaseButton @click="add">新增</BaseButton>
</div>
</div>
<EditArticle
ref="editArticleRef"
:article="article"/>
<Icon @click="show = false"
class="close hvr-grow pointer"
width="20" color="#929596"
icon="ion:close-outline"/>
</div>
</Modal>
</template>
<style scoped lang="scss">
@import "@/assets/css/style.scss";
.add-article {
//position: fixed;
position: relative;
left: 0;
top: 0;
z-index: 9;
width: 100%;
height: 100%;
box-sizing: border-box;
color: black;
background: var(--color-main-bg);
display: flex;
.close {
position: absolute;
right: 20rem;
top: 20rem;
}
.slide {
height: 100%;
background: white;
padding: 0 10rem;
display: flex;
flex-direction: column;
$height: 60rem;
header {
height: $height;
display: flex;
justify-content: space-between;
align-items: center;
//opacity: 0;
.dict-name {
font-size: 30rem;
color: black;
}
}
.name {
font-size: 18rem;
}
.translate-name {
font-size: 16rem;
}
.footer {
height: $height;
display: flex;
gap: 10rem;
align-items: center;
justify-content: flex-end;
.import {
display: inline-flex;
position: relative;
input {
position: absolute;
height: 100%;
width: 100%;
opacity: 0;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,42 @@
<script setup lang="ts">
import {Article, DefaultArticle} from "@/types.ts";
import {cloneDeep} from "lodash-es";
import Modal from "@/components/Modal/Modal.vue";
import EditArticle from "@/components/Article/EditArticle.vue";
interface IProps {
article?: Article
modelValue: boolean
}
const props = withDefaults(defineProps<IProps>(), {
article: () => cloneDeep(DefaultArticle),
modelValue: false
})
</script>
<template>
<Modal
:header="false"
:model-value="props.modelValue"
:full-screen="true"
>
<div class="wrapper">
<EditArticle
:article="article"/>
</div>
</Modal>
</template>
<style scoped lang="scss">
@import "@/assets/css/style.scss";
.wrapper {
width: 100%;
height: 100%;
display: flex;
background: var(--color-main-bg);
}
</style>

View File

@@ -30,21 +30,18 @@ const list: any[] = $computed(() => {
v-for="(item,index) in list"
@click="emit('update:activeIndex', index)">
<input type="radio" :checked="activeIndex === index">
<div class="title">
<template v-if="isArticle">
<div>{{ index + 1 }}.</div>
<div class="count"
@click.stop="emitter.emit(EventKey.openWordListModal,{title:`第${index + 1}章`,list:item})"
>{{ item.title }}</div>
</template>
<template v-else>
<div>{{ index + 1 }}</div>
<div class="count"
@click.stop="emitter.emit(EventKey.openWordListModal,{title:`第${index + 1}章`,list:item})"
>{{ item.length }}
</div>
</template>
</div>
<template v-if="isArticle">
<div class="title"
@click.stop="emitter.emit(EventKey.openArticleListModal,item)"
>{{ index + 1 }}.&nbsp;&nbsp;&nbsp;{{ item.title }}
</div>
</template>
<template v-else>
<div class="title"
@click.stop="emitter.emit(EventKey.openWordListModal,{title:`第${index + 1}章`,list:item})"
>{{ index + 1 }}&nbsp;&nbsp;&nbsp;{{ item.length }}
</div>
</template>
</div>
</div>
</template>
@@ -75,8 +72,16 @@ const list: any[] = $computed(() => {
background: var(--color-item-active);
color: var(--color-font-active-1);
.count {
border-bottom: 2px solid white !important;
&:hover {
.title {
border-bottom: 2px solid white !important;
}
}
}
&:hover {
.title {
border-bottom: 2px solid var(--color-item-active);
}
}
@@ -85,13 +90,9 @@ const list: any[] = $computed(() => {
}
.title {
display: flex;
gap: 10rem;
.count {
cursor: pointer;
border-bottom: 2px solid var(--color-item-active);
}
transition: all .3s;
cursor: pointer;
border-bottom: 2px solid transparent;
}
}
}

View File

@@ -65,6 +65,7 @@ function save() {
}
.text {
color: black;
min-height: 18rem;
}
</style>

View File

@@ -5,6 +5,7 @@ import {Icon} from '@iconify/vue';
import {useEsc} from "@/hooks/event.ts";
import {$ref} from "vue/macros";
import BaseButton from "@/components/BaseButton.vue";
import {useRuntimeStore} from "@/stores/runtime.ts";
export interface ModalProps {
modelValue?: boolean,
@@ -36,10 +37,12 @@ const emit = defineEmits([
'cancel',
])
let zIndex = $ref(999)
let visible = $ref(false)
let openTime = $ref(Date.now())
let maskRef = $ref<HTMLDivElement>(null)
let modalRef = $ref<HTMLDivElement>(null)
const runtimeStore = useRuntimeStore()
function close() {
if (!visible) {
@@ -62,6 +65,7 @@ function close() {
emit('close')
visible = false
resolve(true)
runtimeStore.modalList.pop()
}, closeTime)
});
}
@@ -69,6 +73,11 @@ function close() {
watch(() => props.modelValue, n => {
// console.log('n', n)
if (n) {
runtimeStore.modalList.push({
id: Date.now(),
close
})
zIndex = zIndex + runtimeStore.modalList.length
visible = true
} else {
close()
@@ -79,6 +88,11 @@ onMounted(() => {
// console.log('props.modelValue', props.modelValue)
if (props.modelValue === undefined) {
visible = true
runtimeStore.modalList.push({
id: Date.now(),
close
})
zIndex = zIndex + runtimeStore.modalList.length
}
})
@@ -98,7 +112,7 @@ async function cancel() {
<template>
<Teleport to="body">
<div class="modal-root" v-if="visible">
<div class="modal-root" :style="{'z-index': zIndex}" v-if="visible">
<div class="modal-mask"
ref="maskRef"
v-if="!fullScreen"

View File

@@ -1,484 +0,0 @@
<script setup lang="ts">
import {onMounted, reactive, watch} from "vue";
import {Article, DefaultArticle, Sentence, TranslateEngine, TranslateType} from "@/types.ts";
import BaseButton from "@/components/BaseButton.vue";
import {
getNetworkTranslate,
getSentenceAllText,
getSentenceAllTranslateText,
renewSectionTranslates, renewSectionTexts
} from "@/hooks/translate.ts";
import * as copy from "copy-to-clipboard";
import {getSplitTranslateText, splitEnArticle} from "@/hooks/article.ts";
import EditAbleText from "@/components/EditAbleText.vue";
import {Icon} from "@iconify/vue";
import {cloneDeep} from "lodash-es";
import {useDisableEventListener, useEsc} from "@/hooks/event.ts";
import {Action} from "element-plus";
import {MessageBox} from "@/utils/MessageBox.tsx";
import Modal from "@/components/Modal/Modal.vue";
interface IProps {
article?: Article
modelValue: boolean
}
const props = withDefaults(defineProps<IProps>(), {
article: () => cloneDeep(DefaultArticle),
modelValue: false
})
let article = $ref<Article>(cloneDeep(props.article))
let networkTranslateEngine = $ref('baidu')
let progress = $ref(0)
const TranslateEngineOptions = [
{value: 'baidu', label: '百度'},
{value: 'youdao', label: '有道'},
]
const emit = defineEmits([
'update:modelValue',
'close',
'save'
])
useDisableEventListener(() => props.modelValue)
watch(() => props.modelValue, n => {
if (n) {
article = cloneDeep(props.article)
if (article.text.trim()) {
if (article.useTranslateType === TranslateType.custom) {
if (article.textCustomTranslate.trim()) {
if (!article.textCustomTranslateIsFormat) {
let r = getSplitTranslateText(article.textCustomTranslate)
if (r) article.textCustomTranslate = r
ElMessage({
message: '检测到本地翻译未格式化,已自动格式',
type: 'success',
duration: 5000
})
}
}
}
}
updateSentenceTranslate()
}
})
onMounted(() => {
})
async function startNetworkTranslate() {
if (!article.title.trim()) {
return ElMessage.error('请填写标题!')
}
if (!article.text.trim()) {
return ElMessage.error('请填写正文!')
}
renewSectionTexts(article)
article.textNetworkTranslate = ''
//注意!!!
//这里需要用异步因为watch了article.networkTranslate改变networkTranslate了之后会重新设置article.sections
//导致getNetworkTranslate里面拿到的article.sections是废弃的值
setTimeout(async () => {
await getNetworkTranslate(article, TranslateEngine.Baidu, true, (v: number) => {
progress = v
})
copy(JSON.stringify(article.sections))
})
}
function saveSentenceTranslate(sentence: Sentence, val: string) {
sentence.translate = val
if (article.useTranslateType === TranslateType.custom) {
article.textCustomTranslate = getSentenceAllTranslateText(article)
}
if (article.useTranslateType === TranslateType.network) {
article.textNetworkTranslate = getSentenceAllTranslateText(article)
}
}
function saveSentenceText(sentence: Sentence, val: string) {
sentence.text = val
article.text = getSentenceAllText(article)
updateSentenceTranslate()
}
function updateSentenceTranslate() {
if (article.text.trim()) {
renewSectionTexts(article)
if (article.useTranslateType === TranslateType.custom) {
renewSectionTranslates(article, article.textCustomTranslate)
}
if (article.useTranslateType === TranslateType.network) {
renewSectionTranslates(article, article.textNetworkTranslate)
}
} else {
article.sections = []
}
}
function appendTranslate(str: string) {
if (article.useTranslateType === TranslateType.custom) {
article.textCustomTranslate += str
}
if (article.useTranslateType === TranslateType.network) {
article.textNetworkTranslate += str
}
}
function onPaste(event: ClipboardEvent) {
event.preventDefault()
// @ts-ignore
let paste = (event.clipboardData || window.clipboardData).getData("text");
return MessageBox.confirm(
'是否需要进行自动分句',
'提示',
() => {
let r = getSplitTranslateText(paste)
if (r) appendTranslate(r)
},
() => {
appendTranslate(paste)
},
{
confirmButtonText: '需要',
cancelButtonText: '关闭',
}
)
}
function onBlur() {
document.removeEventListener('paste', onPaste);
}
function onFocus() {
document.addEventListener('paste', onPaste);
}
function save() {
console.log('article', article)
copy(JSON.stringify(article))
if (!article.title.trim()) {
return ElMessage.error('请填写标题!')
}
if (!article.text.trim()) {
return ElMessage.error('请填写正文!')
}
const saveTemp = () => {
article.textCustomTranslateIsFormat = true
emit('close')
emit('save', cloneDeep(article))
}
if (article.useTranslateType === TranslateType.network) {
if (!article.textNetworkTranslate.trim()) {
return MessageBox.confirm(
'您选择了“网络翻译”,但译文内容却为空白,是否修改为“不需要翻译”并保存?',
'提示',
() => {
article.useTranslateType = TranslateType.none
saveTemp()
},
() => void 0,
)
}
}
if (article.useTranslateType === TranslateType.custom) {
if (!article.textCustomTranslate.trim()) {
return MessageBox.confirm(
'您选择了“本地翻译”,但译文内容却为空白,是否修改为“不需要翻译”并保存?',
'提示',
() => {
article.useTranslateType = TranslateType.none
saveTemp()
},
() => void 0,
)
}
}
saveTemp()
}
watch(() => article.textCustomTranslate, (str: string) => {
updateSentenceTranslate()
})
watch(() => article.textNetworkTranslate, (str: string) => {
updateSentenceTranslate()
})
watch(() => article.useTranslateType, () => {
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)
}
}
})
</script>
<template>
<Modal
:model-value="props.modelValue"
:full-screen="true"
>
<div class="add-article" @click.stop="null">
<div class="content">
<div class="row">
<div class="title">原文</div>
<div class="item">
<div class="label">标题</div>
<textarea
v-model="article.title"
type="textarea"
class="base-textarea"
placeholder="请填写原文标题"
>
</textarea>
</div>
<div class="item basic">
<div class="label">正文</div>
<textarea
v-model="article.text"
@input="updateSentenceTranslate"
:readonly="![100,0].includes(progress)"
type="textarea"
class="base-textarea"
placeholder="请填写原文正文"
>
</textarea>
</div>
</div>
<div class="row">
<div class="title">译文</div>
<div class="item">
<div class="label">
<span>标题</span>
<el-radio-group v-model="article.useTranslateType">
<el-radio-button :label="TranslateType.custom">本地翻译</el-radio-button>
<el-radio-button :label="TranslateType.network">网络翻译</el-radio-button>
<el-radio-button :label="TranslateType.none">不需要翻译</el-radio-button>
</el-radio-group>
</div>
<textarea
v-model="article.titleTranslate"
type="textarea"
class="base-textarea"
placeholder="请填写翻译标题"
>
</textarea>
</div>
<div class="item basic">
<div class="label">
<span>正文</span>
<div class="translate-item" v-if="article.useTranslateType === TranslateType.network">
<el-progress :percentage="progress"
:duration="30"
:striped="progress !== 100"
:striped-flow="progress !== 100"
:stroke-width="8"
:show-text="true"/>
<el-select v-model="networkTranslateEngine"
style="width: 80rem;"
>
<el-option
v-for="item in TranslateEngineOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<BaseButton
size="small"
@click="startNetworkTranslate"
:loading="progress!==0 && progress !== 100"
>开始翻译
</BaseButton>
</div>
</div>
<textarea
v-if="article.useTranslateType === TranslateType.custom"
v-model="article.textCustomTranslate"
:readonly="![100,0].includes(progress)"
@blur="onBlur"
@focus="onFocus"
type="textarea"
class="base-textarea"
placeholder="请填写翻译正文"
>
</textarea>
<textarea
v-if="article.useTranslateType === TranslateType.network"
v-model="article.textNetworkTranslate"
@blur="onBlur"
@focus="onFocus"
type="textarea"
class="base-textarea"
placeholder="等待网络翻译中..."
>
</textarea>
</div>
</div>
<div class="row">
<div class="title">译文对照</div>
<div class="article-translate">
<div class="section" v-for="(item,indexI) in article.sections">
<div class="sentence" v-for="(sentence,indexJ) in item">
<EditAbleText
:value="sentence.text"
@save="(e:string) => saveSentenceText(sentence,e)"
/>
<EditAbleText
:value="sentence.translate"
@save="(e:string) => saveSentenceTranslate(sentence,e)"
/>
</div>
</div>
</div>
<div class="options">
<BaseButton @click="save">保存</BaseButton>
</div>
</div>
</div>
<Icon @click="emit('update:modelValue')"
class="close hvr-grow pointer"
width="20" color="#929596"
icon="ion:close-outline"/>
</div>
</Modal>
</template>
<style scoped lang="scss">
@import "@/assets/css/style.scss";
.add-article {
position: fixed;
left: 0;
top: 0;
z-index: 9;
width: 100vw;
height: 100vh;
padding: $space;
box-sizing: border-box;
color: black;
background: var(--color-main-bg);
.close {
position: absolute;
right: 20rem;
top: 20rem;
}
.content {
width: 100%;
height: 100%;
display: flex;
gap: $space;
}
.row {
flex: 1;
width: 33%;
//height: 100%;
display: flex;
flex-direction: column;
//opacity: 0;
.basic {
flex: 1;
display: flex;
flex-direction: column;
}
&:nth-child(2) {
opacity: 1;
}
.title {
font-size: 22rem;
text-align: center;
}
.item {
width: 100%;
//margin-bottom: 10rem;
.label {
height: 45rem;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16rem;
}
}
.translate-item {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
gap: calc($space / 2);
}
.el-progress {
flex: 1;
}
.article-translate {
margin-top: 10rem;
margin-bottom: 20rem;
flex: 1;
overflow: auto;
border-radius: 8rem;
.section {
background: white;
margin-bottom: 20rem;
padding: $space;
border-radius: 8rem;
&:last-child {
margin-bottom: 0;
}
.sentence {
margin-bottom: 20rem;
.text {
font-size: 18rem;
}
}
}
}
.options {
display: flex;
justify-content: flex-end;
}
}
}
</style>

View File

@@ -13,12 +13,10 @@ import {emitter, EventKey} from "@/utils/eventBus";
import {useSettingStore} from "@/stores/setting";
import {cloneDeep} from "lodash-es";
import {Article, DefaultArticle, TranslateType} from "@/types.ts";
import AddArticle from "@/components/Practice/AddArticle.vue";
import {useStartKeyboardEventListener} from "@/hooks/event.ts";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {renewSectionTranslates, renewSectionTexts} from "@/hooks/translate.ts";
import BaseButton from "@/components/BaseButton.vue";
import {renewSectionTexts, renewSectionTranslates} from "@/hooks/translate.ts";
import {MessageBox} from "@/utils/MessageBox.tsx";
import EditArticleModal from "@/components/Article/EditSingleArticleModal.vue";
const practiceStore = usePracticeStore()
const store = useBaseStore()
@@ -201,9 +199,9 @@ function test() {
@repeat="repeat"
@next="next"
/>
<AddArticle v-model="showEditArticle"
:article="editArticle"
@save="saveArticle"
<EditArticleModal v-model="showEditArticle"
:article="editArticle"
@save="saveArticle"
/>
</template>

View File

@@ -3,19 +3,11 @@
import {Icon} from "@iconify/vue";
import IconWrapper from "@/components/IconWrapper.vue";
import Tooltip from "@/components/Tooltip.vue";
import {useBaseStore} from "@/stores/base.ts";
import {useWindowClick} from "@/hooks/event.ts";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import AddArticle from "@/components/Practice/AddArticle.vue";
const store = useBaseStore()
let show = $ref(false)
useWindowClick(() => show = false)
import EditBatchArticleModal from "@/components/Article/EditBatchArticleModal.vue";
function toggle() {
if (!show) emitter.emit(EventKey.closeOther)
show = !show
emitter.emit(EventKey.openArticleListModal)
}
</script>
@@ -28,8 +20,7 @@ function toggle() {
/>
</IconWrapper>
</Tooltip>
<AddArticle v-model="show"
/>
<EditBatchArticleModal/>
</div>
</template>

View File

@@ -65,7 +65,6 @@ async function selectDict(item: DictionaryResource) {
base.current.editIndex = rIndex
} else {
let data = {
...item,
sort: Sort.normal,
type: DictType.publicDict,
originWords: [],
@@ -75,7 +74,8 @@ async function selectDict(item: DictionaryResource) {
chapterIndex: 0,
chapterWordIndex: 0,
statistics: [],
articles: []
articles: [],
...item,
}
if (item.languageCategory === 'article') {
@@ -95,7 +95,7 @@ async function selectDict(item: DictionaryResource) {
r.json().then(v => {
data.originWords = v
data.words = v
data.chapterWords = chunk(v, currentSelectDict.chapterWordNumber)
data.chapterWords = chunk(v, data.chapterWordNumber)
base.myDicts.push(data)
base.current.editIndex = base.myDicts.length - 1
})
@@ -104,7 +104,7 @@ async function selectDict(item: DictionaryResource) {
}
function changeDict() {
store.changeDict(currentSelectDict)
store.changeDict(base.currentEditDict)
close()
}
@@ -113,7 +113,7 @@ function close() {
}
function resetChapterList() {
currentSelectDict.chapterWords = chunk(currentSelectDict.words, currentSelectDict.chapterWordNumber)
base.currentEditDict.chapterWords = chunk(base.currentEditDict.words, base.currentEditDict.chapterWordNumber)
}
function groupBy<T>(elements: T[], iteratee: (value: T) => string) {
@@ -193,7 +193,7 @@ const dictIsArticle = $computed(() => {
<div class="dict-list-wrapper">
<DictGroup
v-for="item in groupedByCategoryAndTag"
:select-dict-name="currentSelectDict.name"
:select-dict-name="base.currentEditDict.name"
@selectDict="selectDict"
@detail="step = 1"
:groupByTag="item[1]"/>

View File

@@ -237,7 +237,7 @@ export function splitCNArticle(text: string): Sentence[][] {
// console.log('sentence', sentenceRow)
})
})
console.log('sections', sections)
// console.log('sections', sections)
return sections
}

View File

@@ -8,7 +8,7 @@ export function renewSectionTranslates(article: Article, translate: string) {
let failCount = 0
if (translate.trim()) {
let articleTranslate = translate.split('\n')
console.log('articleTranslate', articleTranslate)
// console.log('articleTranslate', articleTranslate)
// console.log('articleTranslate', articleTranslate)
let count = 0
for (let i = 0; i < article.sections.length; i++) {

View File

@@ -5,6 +5,7 @@ export const EventKey = {
resetWord: 'resetWord',
openStatModal: 'openStatModal',
openWordListModal: 'openWordListModal',
openArticleListModal: 'openArticleListModal',
closeOther: 'closeOther',
keydown: 'keydown',
keyup: 'keyup',