This commit is contained in:
zyronon
2023-10-12 01:31:52 +08:00
parent aa9030b66a
commit 375420cf69
8 changed files with 226 additions and 153 deletions

View File

@@ -35,4 +35,6 @@ BaseIcon 在选中模式下,应该显示白色
添加文章时正文输入123报错
没有内容时,要显示占位符
没有内容时,要显示占位符
A cold welcome 有bug

View File

@@ -17,17 +17,24 @@ 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 {h, watch} from "vue";
import {useBaseStore} from "@/stores/base.ts";
interface IProps {
article?: Article
article?: Article,
type?: 'single' | 'batch'
}
const props = withDefaults(defineProps<IProps>(), {
article: () => cloneDeep(DefaultArticle),
type: 'single'
})
const emit = defineEmits<{
save: [val: Article],
saveAndNext: [val: Article]
}>()
let networkTranslateEngine = $ref('baidu')
let progress = $ref(0)
let failCount = $ref(0)
@@ -36,22 +43,21 @@ const TranslateEngineOptions = [
{value: 'youdao', label: '有道'},
]
const base = useBaseStore()
let article = $ref<Article>(cloneDeep(DefaultArticle))
let editArticle = $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)
editArticle = cloneDeep(val)
if (editArticle.text.trim()) {
if (editArticle.useTranslateType === TranslateType.custom) {
if (editArticle.textCustomTranslate.trim()) {
if (!editArticle.textCustomTranslateIsFormat) {
let r = getSplitTranslateText(editArticle.textCustomTranslate)
if (r) {
article.textCustomTranslate = r
editArticle.textCustomTranslate = r
ElMessage({
message: '检测到本地翻译未格式化,已自动格式化',
type: 'success',
duration: 5000
duration: 3000
})
}
}
@@ -59,29 +65,29 @@ watch(() => props.article, val => {
}
}
renewSections()
console.log('ar', article)
// 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 (editArticle.text.trim()) {
renewSectionTexts(editArticle)
if (editArticle.useTranslateType === TranslateType.custom) {
failCount = renewSectionTranslates(editArticle, editArticle.textCustomTranslate)
}
if (article.useTranslateType === TranslateType.network) {
failCount = renewSectionTranslates(article, article.textNetworkTranslate)
if (editArticle.useTranslateType === TranslateType.network) {
failCount = renewSectionTranslates(editArticle, editArticle.textNetworkTranslate)
}
} else {
article.sections = []
editArticle.sections = []
}
}
function appendTranslate(str: string) {
if (article.useTranslateType === TranslateType.custom) {
article.textCustomTranslate += str
if (editArticle.useTranslateType === TranslateType.custom) {
editArticle.textCustomTranslate += str
}
if (article.useTranslateType === TranslateType.network) {
article.textNetworkTranslate += str
if (editArticle.useTranslateType === TranslateType.network) {
editArticle.textNetworkTranslate += str
}
}
@@ -118,129 +124,105 @@ 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()) {
if (!editArticle.title.trim()) {
return ElMessage.error('请填写标题!')
}
if (!article.text.trim()) {
if (!editArticle.text.trim()) {
return ElMessage.error('请填写正文!')
}
renewSectionTexts(article)
article.textNetworkTranslate = ''
renewSectionTexts(editArticle)
editArticle.textNetworkTranslate = ''
//注意!!!
//这里需要用异步因为watch了article.networkTranslate改变networkTranslate了之后会重新设置article.sections
//导致getNetworkTranslate里面拿到的article.sections是废弃的值
setTimeout(async () => {
await getNetworkTranslate(article, TranslateEngine.Baidu, true, (v: number) => {
await getNetworkTranslate(editArticle, TranslateEngine.Baidu, true, (v: number) => {
progress = v
})
copy(JSON.stringify(article.sections))
copy(JSON.stringify(editArticle.sections))
})
}
function saveSentenceTranslate(sentence: Sentence, val: string) {
sentence.translate = val
if (article.useTranslateType === TranslateType.custom) {
article.textCustomTranslate = getSentenceAllTranslateText(article)
if (editArticle.useTranslateType === TranslateType.custom) {
editArticle.textCustomTranslate = getSentenceAllTranslateText(editArticle)
}
if (article.useTranslateType === TranslateType.network) {
article.textNetworkTranslate = getSentenceAllTranslateText(article)
if (editArticle.useTranslateType === TranslateType.network) {
editArticle.textNetworkTranslate = getSentenceAllTranslateText(editArticle)
}
renewSections()
}
function saveSentenceText(sentence: Sentence, val: string) {
sentence.text = val
article.text = getSentenceAllText(article)
editArticle.text = getSentenceAllText(editArticle)
renewSections()
}
defineExpose({save})
function save(option: 'save' | 'saveAndNext') {
return new Promise((resolve: Function) => {
// console.log('article', article)
// copy(JSON.stringify(article))
editArticle.title = editArticle.title.trim()
editArticle.titleTranslate = editArticle.titleTranslate.trim()
editArticle.text = editArticle.text.trim()
editArticle.textCustomTranslate = editArticle.textCustomTranslate.trim()
editArticle.textNetworkTranslate = editArticle.textNetworkTranslate.trim()
if (!editArticle.title) {
ElMessage.error('请填写标题!')
return resolve(false)
}
if (!editArticle.text) {
ElMessage.error('请填写正文!')
return resolve(false)
}
const saveTemp = () => {
editArticle.textCustomTranslateIsFormat = true
emit(option as any, editArticle)
return resolve(true)
}
if (editArticle.useTranslateType === TranslateType.network) {
if (!editArticle.textNetworkTranslate) {
return MessageBox.confirm(
'您选择了“网络翻译”,但译文内容却为空白,是否修改为“不需要翻译”并保存?',
'提示',
() => {
editArticle.useTranslateType = TranslateType.none
saveTemp()
},
() => void 0,
)
}
}
if (editArticle.useTranslateType === TranslateType.custom) {
if (!editArticle.textCustomTranslate) {
return MessageBox.confirm(
'您选择了“本地翻译”,但译文内容却为空白,是否修改为“不需要翻译”并保存?',
'提示',
() => {
editArticle.useTranslateType = TranslateType.none
saveTemp()
},
() => void 0,
)
}
}
saveTemp()
})
}
//不知道直接用editArticle取到是空的默认值
defineExpose({save, getEditArticle: () => cloneDeep(editArticle)})
</script>
<template>
@@ -250,7 +232,7 @@ defineExpose({save})
<div class="item">
<div class="label">标题</div>
<textarea
v-model="article.title"
v-model="editArticle.title"
type="textarea"
class="base-textarea"
placeholder="请填写原文标题"
@@ -260,7 +242,7 @@ defineExpose({save})
<div class="item basic">
<div class="label">正文</div>
<textarea
v-model="article.text"
v-model="editArticle.text"
@input="renewSections"
:readonly="![100,0].includes(progress)"
type="textarea"
@@ -276,7 +258,7 @@ defineExpose({save})
<div class="label">
<span>标题</span>
<el-radio-group
v-model="article.useTranslateType"
v-model="editArticle.useTranslateType"
@change="renewSections"
>
<el-radio-button :label="TranslateType.custom">本地翻译</el-radio-button>
@@ -285,7 +267,7 @@ defineExpose({save})
</el-radio-group>
</div>
<textarea
v-model="article.titleTranslate"
v-model="editArticle.titleTranslate"
type="textarea"
class="base-textarea"
placeholder="请填写翻译标题"
@@ -295,7 +277,7 @@ defineExpose({save})
<div class="item basic">
<div class="label">
<span>正文</span>
<div class="translate-item" v-if="article.useTranslateType === TranslateType.network">
<div class="translate-item" v-if="editArticle.useTranslateType === TranslateType.network">
<el-progress :percentage="progress"
:duration="30"
:striped="progress !== 100"
@@ -322,8 +304,8 @@ defineExpose({save})
</div>
</div>
<textarea
v-if="article.useTranslateType === TranslateType.custom"
v-model="article.textCustomTranslate"
v-if="editArticle.useTranslateType === TranslateType.custom"
v-model="editArticle.textCustomTranslate"
@input="renewSections"
:readonly="![100,0].includes(progress)"
@blur="onBlur"
@@ -334,8 +316,8 @@ defineExpose({save})
>
</textarea>
<textarea
v-if="article.useTranslateType === TranslateType.network"
v-model="article.textNetworkTranslate"
v-if="editArticle.useTranslateType === TranslateType.network"
v-model="editArticle.textNetworkTranslate"
@input="renewSections"
@blur="onBlur"
@focus="onFocus"
@@ -349,7 +331,7 @@ defineExpose({save})
<div class="row">
<div class="title">译文对照</div>
<div class="article-translate">
<div class="section" v-for="(item,indexI) in article.sections">
<div class="section" v-for="(item,indexI) in editArticle.sections">
<div class="sentence" v-for="(sentence,indexJ) in item">
<EditAbleText
:value="sentence.text"
@@ -362,16 +344,16 @@ defineExpose({save})
</div>
</div>
</div>
<div class="options" v-if="article.text.trim()">
<div class="options" v-if="editArticle.text.trim()">
<div class="warning">
<template v-if="failCount && article.useTranslateType !== TranslateType.none">
<template v-if="failCount && editArticle.useTranslateType !== TranslateType.none">
<Icon icon="typcn:warning-outline"/>
共有{{ failCount }}句没有翻译
</template>
</div>
<div class="left">
<BaseButton @click="save('save')">保存</BaseButton>
<BaseButton @click="save('next')">保存并添加下一篇</BaseButton>
<BaseButton v-if="type === 'batch'" @click="save('saveAndNext')">保存并添加下一篇</BaseButton>
</div>
</div>
</div>
@@ -382,6 +364,7 @@ defineExpose({save})
@import "@/assets/css/style.scss";
.content {
color: black;
flex: 1;
display: flex;
gap: $space;

View File

@@ -1,12 +1,12 @@
<script setup lang="ts">
import {saveAs} from "file-saver";
import {reactive} from "vue";
import {Article, DefaultArticle, DictType, Sort} from "@/types.ts";
import {onUnmounted, reactive} from "vue";
import {Article, DefaultArticle, DictType, Sort, TranslateType} 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 {$computed, $ref} from "vue/macros";
import List from "@/components/List.vue";
import {v4 as uuidv4} from 'uuid';
import {Icon} from "@iconify/vue";
@@ -14,13 +14,15 @@ 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";
import {useDisableEventListener} from "@/hooks/event.ts";
import {MessageBox} from "@/utils/MessageBox.tsx";
const base = useBaseStore()
let article = $ref<Article>(cloneDeep(DefaultArticle))
let show = $ref(false)
let showImportBtn = $ref(true)
let editArticleRef = $ref()
let editArticleRef: any = $ref()
onMounted(() => {
emitter.on(EventKey.openArticleListModal, (val: Article) => {
@@ -32,7 +34,11 @@ onMounted(() => {
})
})
// useDisableEventListener(() => data.show)
onUnmounted(() => {
emitter.off(EventKey.openArticleListModal)
})
useDisableEventListener(() => show)
function selectArticle(item: Article) {
article = cloneDeep(item)
@@ -40,11 +46,53 @@ function selectArticle(item: Article) {
}
function add() {
if (article.title.trim() && article.text.trim()) {
// return save('next', true)
let editArticle: Article = editArticleRef.getEditArticle()
const newArticle = () => {
article = cloneDeep(DefaultArticle)
// article.title = 'a'
// article.text = 'b'
}
// editArticleRef.save('save',true)
article = cloneDeep(DefaultArticle)
if (editArticle.id !== '-1') {
editArticle.title = editArticle.title.trim()
editArticle.titleTranslate = editArticle.titleTranslate.trim()
editArticle.text = editArticle.text.trim()
editArticle.textCustomTranslate = editArticle.textCustomTranslate.trim()
editArticle.textNetworkTranslate = editArticle.textNetworkTranslate.trim()
if (
editArticle.title !== article.title ||
editArticle.titleTranslate !== article.titleTranslate ||
editArticle.text !== article.text ||
editArticle.textCustomTranslate !== article.textCustomTranslate ||
editArticle.textNetworkTranslate !== article.textNetworkTranslate ||
editArticle.useTranslateType !== article.useTranslateType
) {
return MessageBox.confirm(
'检测到数据有变动,是否保存?',
'提示',
async () => {
let r = await editArticleRef.save('save')
if (r) newArticle()
},
() => void 0,
)
}
} else {
if (editArticle.title.trim() && editArticle.text.trim()) {
return MessageBox.confirm(
'检测到数据有变动,是否保存?',
'提示',
async () => {
let r = await editArticleRef.save('save')
if (r) newArticle()
},
() => void 0,
)
}
}
newArticle()
}
function importData(e: Event) {
@@ -142,6 +190,38 @@ function exportData() {
let blob = new Blob([JSON.stringify(data, null, 2)], {type: "text/plain;charset=utf-8"});
saveAs(blob, `${data.name}.json`);
}
function saveArticle(val: Article) {
console.log('saveArticle', val)
if (val.id !== '-1') {
let rIndex = base.currentEditDict.articles.findIndex(v => v.id === val.id)
if (rIndex > -1) {
base.currentEditDict.articles[rIndex] = cloneDeep(val)
}
} else {
let has = base.currentEditDict.articles.find((item: Article) => item.title === val.title)
if (has) {
return ElMessage.error('已存在同名文章!')
}
val.id = uuidv4()
base.currentEditDict.articles.push(val)
article = cloneDeep(val)
}
//TODO 保存完成后滚动到对应位置
ElMessage.success('保存成功!')
}
const list = $computed(() => {
if (article.id === '-1') {
return base.currentEditDict.articles.concat([article])
}
return base.currentEditDict.articles
})
function getTitle(item: Article, index: number,) {
if (item.id === '-1') return 'New article'
return `${index + 1}. ${item.title}`
}
</script>
<template>
@@ -157,13 +237,13 @@ function exportData() {
<BaseIcon title="选择其他词典/文章" icon="carbon:change-catalog"/>
</header>
<List
v-model:list="base.currentEditDict.articles"
v-model:list="list"
: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="name"> {{ getTitle(item, index) }}</div>
<div class="translate-name"> {{ ` ${item.titleTranslate}` }}</div>
</template>
</List>
@@ -178,6 +258,8 @@ function exportData() {
</div>
<EditArticle
ref="editArticleRef"
type="batch"
@save="saveArticle"
:article="article"/>
<Icon @click="show = false"

View File

@@ -14,6 +14,9 @@ const props = withDefaults(defineProps<IProps>(), {
article: () => cloneDeep(DefaultArticle),
modelValue: false
})
const emit = defineEmits<{
save: [],
}>()
</script>
@@ -25,7 +28,9 @@ const props = withDefaults(defineProps<IProps>(), {
>
<div class="wrapper">
<EditArticle
:article="article"/>
:article="article"
@save="emit('save')"
/>
</div>
</Modal>
</template>

View File

@@ -157,10 +157,11 @@ function next() {
repeat()
}
function saveArticle(article: Article) {
console.log('saveArticle', article)
function saveArticle() {
console.log('saveArticle')
showEditArticle = false
store.currentDict.articles[store.currentDict.chapterIndex] = articleData.article = article
articleData.article = cloneDeep(store.currentDict.articles[store.currentDict.chapterIndex])
// store.currentDict.articles[store.currentDict.chapterIndex] = articleData.article = article
}
function test() {

View File

@@ -412,7 +412,7 @@ function otherWord(word: ArticleWord, i: number, i2: number, i3: number) {
<div class="article-wrapper">
<header>
<div class="title">{{ props.article.title }}</div>
<div class="titleTranslate">{{ props.article.titleTranslate }}</div>
<div class="titleTranslate" >{{ props.article.titleTranslate }}</div>
</header>
<div class="article-content" ref="articleWrapperRef">
<article>
@@ -620,7 +620,7 @@ function otherWord(word: ArticleWord, i: number, i2: number, i3: number) {
}
.input {
font-weight: bold;
//font-weight: bold;
color: var(--color-main-active);
}

View File

@@ -147,7 +147,7 @@ const groupedByCategoryAndTag = $computed(() => {
const groupedByCategoryAndTag = groupedByCategory.map(
([category, dicts]) => [category, groupByDictTags(dicts)] as [string, Record<string, DictionaryResource[]>],
)
console.log('groupedByCategoryAndTag', groupedByCategoryAndTag)
// console.log('groupedByCategoryAndTag', groupedByCategoryAndTag)
return groupedByCategoryAndTag
})

View File

@@ -103,7 +103,7 @@ export interface Article {
export const DefaultArticle: Article = {
// id: uuidv4(),
id: '',
id: '-1',
title: '',
titleTranslate: '',
text: '',