save
This commit is contained in:
2
components.d.ts
vendored
2
components.d.ts
vendored
@@ -9,6 +9,7 @@ declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
Add: typeof import('./src/components/Toolbar/Add.vue')['default']
|
||||
AddArticle2: typeof import('./src/components/Add/AddArticle2.vue')['default']
|
||||
AddDict: typeof import('./src/components/Add/AddDict.vue')['default']
|
||||
Backgorund: typeof import('./src/components/Backgorund.vue')['default']
|
||||
BaseButton: typeof import('./src/components/BaseButton.vue')['default']
|
||||
BaseIcon: typeof import('./src/components/BaseIcon.vue')['default']
|
||||
@@ -17,6 +18,7 @@ declare module 'vue' {
|
||||
ChapterList: typeof import('./src/components/ChapterList.vue')['default']
|
||||
Close: typeof import('./src/components/Close.vue')['default']
|
||||
DictGroup: typeof import('./src/components/Toolbar/DictGroup.vue')['default']
|
||||
DictItem: typeof import('./src/components/DictItem.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']
|
||||
|
||||
@@ -51,7 +51,6 @@ useEventListener('keyup', (e: KeyboardEvent) => {
|
||||
<Practice/>
|
||||
<!-- <AddArticle/>-->
|
||||
<!-- <Side/>-->
|
||||
<!-- <BatchAddArticle/>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,714 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
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,
|
||||
renewSectionTexts,
|
||||
renewSectionTranslates
|
||||
} from "@/hooks/translate.ts";
|
||||
import * as copy from "copy-to-clipboard";
|
||||
import {getSplitTranslateText} from "@/hooks/article.ts";
|
||||
import EditAbleText from "@/components/EditAbleText.vue";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
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 {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
|
||||
|
||||
const base = useBaseStore()
|
||||
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([
|
||||
'save'
|
||||
])
|
||||
|
||||
// useDisableEventListener(() => data.show)
|
||||
|
||||
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()) {
|
||||
if (!article.textCustomTranslateIsFormat) {
|
||||
let r = getSplitTranslateText(article.textCustomTranslate)
|
||||
if (r) {
|
||||
article.textCustomTranslate = r
|
||||
ElMessage({
|
||||
message: '检测到本地翻译未格式化,已自动格式化',
|
||||
type: 'success',
|
||||
duration: 5000
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
renewSections()
|
||||
})
|
||||
})
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
function selectArticle(item: Article) {
|
||||
article = cloneDeep(item)
|
||||
if (!article?.sections?.length) {
|
||||
renewSections()
|
||||
}
|
||||
// console.log('article', article)
|
||||
}
|
||||
|
||||
function add() {
|
||||
if (article.title.trim() && article.text.trim()) {
|
||||
return save('next', true)
|
||||
}
|
||||
article = cloneDeep(DefaultArticle)
|
||||
}
|
||||
|
||||
let showImportBtn = $ref(true)
|
||||
|
||||
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="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>
|
||||
</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"
|
||||
>
|
||||
<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">
|
||||
<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>
|
||||
<Icon @click="data.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
gap: $space;
|
||||
padding: $space;
|
||||
padding-top: 10rem;
|
||||
//opacity: 0;
|
||||
}
|
||||
|
||||
.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>
|
||||
101
src/components/Add/AddDict.vue
Normal file
101
src/components/Add/AddDict.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<script setup lang="ts">
|
||||
import Modal from "@/components/Modal/Modal.vue"
|
||||
import {Icon} from '@iconify/vue';
|
||||
import {useSettingStore} from "@/stores/setting.ts";
|
||||
import DictItem from "@/components/DictItem.vue";
|
||||
import {DictType, Sort} from "@/types.ts";
|
||||
import {$ref} from "vue/macros";
|
||||
import {useDisableEventListener} from "@/hooks/event.ts";
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
|
||||
const emit = defineEmits([
|
||||
'close',
|
||||
])
|
||||
|
||||
let step = $ref(0)
|
||||
|
||||
useDisableEventListener()
|
||||
|
||||
let list = $ref([
|
||||
{
|
||||
name: '新概念英语2',
|
||||
sort: Sort.normal,
|
||||
type: DictType.publicDict,
|
||||
originWords: [],
|
||||
articles: [],
|
||||
words: [],
|
||||
chapterWordNumber: 15,
|
||||
chapterWords: [],
|
||||
chapterIndex: 0,
|
||||
chapterWordIndex: 0,
|
||||
statistics: [],
|
||||
url: '/dicts/NCE_2.json',
|
||||
},
|
||||
{
|
||||
name: '新概念英语2',
|
||||
sort: Sort.normal,
|
||||
type: DictType.publicDict,
|
||||
originWords: [],
|
||||
articles: [],
|
||||
words: [],
|
||||
chapterWordNumber: 15,
|
||||
chapterWords: [],
|
||||
chapterIndex: 0,
|
||||
chapterWordIndex: 0,
|
||||
statistics: [],
|
||||
url: '/dicts/NCE_2.json',
|
||||
},
|
||||
{
|
||||
name: '新概念英语2',
|
||||
sort: Sort.normal,
|
||||
type: DictType.publicDict,
|
||||
originWords: [],
|
||||
articles: [],
|
||||
words: [],
|
||||
chapterWordNumber: 15,
|
||||
chapterWords: [],
|
||||
chapterIndex: 0,
|
||||
chapterWordIndex: 0,
|
||||
statistics: [],
|
||||
url: '/dicts/NCE_2.json',
|
||||
}
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal
|
||||
@close="emit('close')"
|
||||
title="添加词典">
|
||||
<div class="slide">
|
||||
<div class="slide-list" :class="`step${step}`">
|
||||
<div class="page">
|
||||
<div class="list">
|
||||
<DictItem :list="list"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/variable";
|
||||
|
||||
.slide {
|
||||
width: 1100rem;
|
||||
height: 75vh;
|
||||
|
||||
.slide-list {
|
||||
width: 200%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.step1 {
|
||||
transform: translate3d(-50%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,41 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import AddArticle2 from "@/components/Add/AddArticle2.vue";
|
||||
import {$ref} from "vue/macros";
|
||||
import {cloneDeep} from "lodash-es";
|
||||
import {DefaultArticle} from "@/types.ts";
|
||||
import {onMounted, watch} from "vue";
|
||||
import {splitCNArticle, splitEnArticle} from "@/hooks/article.ts";
|
||||
|
||||
const base = useBaseStore()
|
||||
|
||||
let articleData = $ref({
|
||||
article: cloneDeep(DefaultArticle),
|
||||
sectionIndex: 0,
|
||||
sentenceIndex: 0,
|
||||
wordIndex: 0,
|
||||
stringIndex: 0,
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// splitEnArticle('')
|
||||
splitCNArticle('')
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="BatchAddArticle">
|
||||
<AddArticle2/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/variable";
|
||||
|
||||
#BatchAddArticle {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
color: black;
|
||||
}
|
||||
</style>
|
||||
80
src/components/DictItem.vue
Normal file
80
src/components/DictItem.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<script setup lang="ts">
|
||||
import {Dict} from "@/types.ts";
|
||||
import {Icon} from "@iconify/vue";
|
||||
|
||||
const props = defineProps<{
|
||||
list?: Dict[],
|
||||
selectDictName?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
selectDict: [val: Dict]
|
||||
detail: []
|
||||
}>()
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dict-list">
|
||||
<div class="dict-item anim"
|
||||
:class="selectDictName === i.name && 'active'"
|
||||
@click="emit('selectDict',i)"
|
||||
v-for="i in list"
|
||||
>
|
||||
<div class="name">{{ i.name }}</div>
|
||||
<div class="desc">{{ i.description }}</div>
|
||||
<div class="num">{{ i.length }}词</div>
|
||||
|
||||
<Icon icon="octicon:arrow-right-24" v-if="selectDictName === i.name"
|
||||
@click.stop="emit('detail')"
|
||||
class="go" width="20" color="#929596"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dict-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 15rem;
|
||||
|
||||
.dict-item {
|
||||
cursor: pointer;
|
||||
padding: 10rem;
|
||||
min-height: 100rem;
|
||||
border-radius: 10rem;
|
||||
position: relative;
|
||||
background: var(--color-item-bg);
|
||||
color: var(--color-font-1);
|
||||
font-size: 14rem;
|
||||
|
||||
.name {
|
||||
font-size: 18rem;
|
||||
}
|
||||
|
||||
.desc {
|
||||
color: var(--color-font-2);
|
||||
}
|
||||
|
||||
.num {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.go {
|
||||
position: absolute;
|
||||
right: 10rem;
|
||||
bottom: 15rem;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: var(--color-item-active);
|
||||
color: var(--color-font-active-1);
|
||||
|
||||
.desc {
|
||||
color: var(--color-font-active-2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,56 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import {inject} from "vue"
|
||||
|
||||
const next: () => void = inject('next')
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dict-wrapper page">
|
||||
<div class="tags">
|
||||
<div class="tag" :class="i === 5 &&'active'" v-for="i in 2">六级</div>
|
||||
</div>
|
||||
<div class="dict-list">
|
||||
<div class="dict-item" v-for="i in 5" @click="next">
|
||||
<div class="name">CET-4</div>
|
||||
<div class="desc">大学英语四级词库</div>
|
||||
<div class="num">2607词</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dict-wrapper {
|
||||
.tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 20rem;
|
||||
|
||||
.tag {
|
||||
cursor: pointer;
|
||||
padding: 5rem 10rem;
|
||||
border-radius: 20rem;
|
||||
|
||||
&.active {
|
||||
background: gray;
|
||||
color: whitesmoke;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dict-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 10rem;
|
||||
|
||||
.dict-item {
|
||||
cursor: pointer;
|
||||
padding: 10rem;
|
||||
border-radius: 10rem;
|
||||
border: 1px solid gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -5,8 +5,13 @@ import IconWrapper from "@/components/IconWrapper.vue";
|
||||
import Tooltip from "@/components/Tooltip.vue";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import EditBatchArticleModal from "@/components/Article/EditBatchArticleModal.vue";
|
||||
import AddDict from "@/components/Add/AddDict.vue";
|
||||
import {$ref} from "vue/macros";
|
||||
|
||||
let show = $ref(false)
|
||||
|
||||
function toggle() {
|
||||
show = !show
|
||||
emitter.emit(EventKey.openArticleListModal)
|
||||
}
|
||||
</script>
|
||||
@@ -20,7 +25,8 @@ function toggle() {
|
||||
/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
<EditBatchArticleModal/>
|
||||
<!-- <EditBatchArticleModal/>-->
|
||||
<AddDict v-if="show" @close="show = false"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -111,6 +111,5 @@ watch(() => props.groupByTag, () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user