save
This commit is contained in:
2
components.d.ts
vendored
2
components.d.ts
vendored
@@ -25,6 +25,8 @@ declare module 'vue' {
|
||||
EditAbleText: typeof import('./src/components/EditAbleText.vue')['default']
|
||||
EditArticle: typeof import('./src/components/article/EditArticle.vue')['default']
|
||||
EditBatchArticleModal: typeof import('./src/components/article/EditBatchArticleModal.vue')['default']
|
||||
EditBatchArticleModal2: typeof import('./src/components/article/EditBatchArticleModal2.vue')['default']
|
||||
EditBatchArticleModalFQ: typeof import('./src/components/article/EditBatchArticleModal-FQ.vue')['default']
|
||||
EditSingleArticleModal: typeof import('./src/components/article/EditSingleArticleModal.vue')['default']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
|
||||
@@ -158,6 +158,7 @@ a {
|
||||
min-height: 20rem;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: var(--color-item-bg);
|
||||
|
||||
&:focus {
|
||||
border: 1px solid var(--color-main-active);
|
||||
|
||||
@@ -112,7 +112,7 @@ defineEmits(['click'])
|
||||
border-bottom: 2px solid transparent;
|
||||
|
||||
&:hover {
|
||||
border-bottom: 2px solid black;
|
||||
border-bottom: 2px solid var(--color-font-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -266,11 +266,9 @@ const {
|
||||
<div class="panel-page-item">
|
||||
<div class="list-header">
|
||||
<div class="left">
|
||||
<Tooltip title="切换词典">
|
||||
<IconWrapper>
|
||||
<Icon @click="emitter.emit(EventKey.openDictModal,'list')" icon="basil:exchange-outline"/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
<BaseIcon title="切换词典"
|
||||
@click="emitter.emit(EventKey.openDictModal,'list')"
|
||||
icon="carbon:change-catalog"/>
|
||||
<div class="title">
|
||||
{{ store.dictTitle }}
|
||||
</div>
|
||||
|
||||
@@ -268,11 +268,9 @@ onUnmounted(() => {
|
||||
>
|
||||
<div class="list-header">
|
||||
<div class="left">
|
||||
<Tooltip title="切换词典">
|
||||
<IconWrapper>
|
||||
<Icon @click="emitter.emit(EventKey.openDictModal,'list')" icon="basil:exchange-outline"/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
<BaseIcon title="切换词典"
|
||||
@click="emitter.emit(EventKey.openDictModal,'list')"
|
||||
icon="carbon:change-catalog"/>
|
||||
<div class="title">
|
||||
{{ store.dictTitle }}
|
||||
</div>
|
||||
|
||||
@@ -383,7 +383,7 @@ defineExpose({save, getEditArticle: () => cloneDeep(editArticle)})
|
||||
}
|
||||
|
||||
.row {
|
||||
flex: 1;
|
||||
flex: 10;
|
||||
width: 33%;
|
||||
//height: 100%;
|
||||
display: flex;
|
||||
@@ -396,8 +396,8 @@ defineExpose({save, getEditArticle: () => cloneDeep(editArticle)})
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
opacity: 1;
|
||||
&:nth-child(1) {
|
||||
flex: 7;
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -436,7 +436,6 @@ defineExpose({save, getEditArticle: () => cloneDeep(editArticle)})
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
border-radius: 8rem;
|
||||
background: var(--color-main-bg);
|
||||
|
||||
.section {
|
||||
background: var(--color-item-bg);
|
||||
|
||||
365
src/components/article/EditBatchArticleModal-FQ.vue
Normal file
365
src/components/article/EditBatchArticleModal-FQ.vue
Normal file
@@ -0,0 +1,365 @@
|
||||
<script setup lang="ts">
|
||||
import {saveAs} from "file-saver";
|
||||
import {onMounted, onUnmounted} 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 {$computed, $ref} from "vue/macros";
|
||||
import List from "@/components/list/List.vue";
|
||||
import {v4 as uuidv4} from 'uuid';
|
||||
import Dialog from "@/components/dialog/Dialog.vue";
|
||||
import EditArticle from "@/components/article/EditArticle.vue";
|
||||
import {emitter, EventKey} from "@/utils/eventBus.ts";
|
||||
import {useDisableEventListener} from "@/hooks/event.ts";
|
||||
import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
|
||||
const base = useBaseStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
|
||||
let article = $ref<Article>(cloneDeep(DefaultArticle))
|
||||
let show = $ref(false)
|
||||
let showImportBtn = $ref(true)
|
||||
let editArticleRef: any = $ref()
|
||||
let listEl: any = $ref()
|
||||
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.openArticleListModal, (val: Article) => {
|
||||
console.log('val', val)
|
||||
show = true
|
||||
if (val) {
|
||||
article = cloneDeep(val)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.openArticleListModal)
|
||||
})
|
||||
|
||||
useDisableEventListener(() => show)
|
||||
|
||||
async function selectArticle(item: Article) {
|
||||
let r = await checkDataChange()
|
||||
if (r) {
|
||||
article = cloneDeep(item)
|
||||
}
|
||||
}
|
||||
|
||||
function checkDataChange() {
|
||||
return new Promise(resolve => {
|
||||
let editArticle: Article = editArticleRef.getEditArticle()
|
||||
|
||||
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) resolve(true)
|
||||
},
|
||||
() => void 0,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (editArticle.title.trim() && editArticle.text.trim()) {
|
||||
return MessageBox.confirm(
|
||||
'检测到数据有变动,是否保存?',
|
||||
'提示',
|
||||
async () => {
|
||||
let r = await editArticleRef.save('save')
|
||||
if (r) resolve(true)
|
||||
},
|
||||
() => void 0,
|
||||
)
|
||||
}
|
||||
}
|
||||
resolve(true)
|
||||
})
|
||||
}
|
||||
|
||||
async function add() {
|
||||
let r = await checkDataChange()
|
||||
if (r) {
|
||||
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.myDictList.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.myDictList.push(obj)
|
||||
runtimeStore.editDict = cloneDeep(runtimeStore.editDict)
|
||||
showImportBtn = true
|
||||
} catch (e) {
|
||||
showImportBtn = true
|
||||
ElMessage.error('文件解析失败,报错原因:' + e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function exportData() {
|
||||
let data = {
|
||||
name: runtimeStore.editDict.name,
|
||||
articles: cloneDeep(runtimeStore.editDict.articles).map(v => {
|
||||
delete v.sections
|
||||
delete v.id
|
||||
return v
|
||||
}),
|
||||
url: location.origin + runtimeStore.editDict.url,
|
||||
statistics: runtimeStore.editDict.statistics,
|
||||
}
|
||||
|
||||
let blob = new Blob([JSON.stringify(data, null, 2)], {type: "text/plain;charset=utf-8"});
|
||||
saveAs(blob, `${data.name}.json`);
|
||||
}
|
||||
|
||||
function saveArticle(val: Article): boolean {
|
||||
console.log('saveArticle', val)
|
||||
if (val.id) {
|
||||
let rIndex = runtimeStore.editDict.articles.findIndex(v => v.id === val.id)
|
||||
if (rIndex > -1) {
|
||||
runtimeStore.editDict.articles[rIndex] = cloneDeep(val)
|
||||
}
|
||||
} else {
|
||||
let has = runtimeStore.editDict.articles.find((item: Article) => item.title === val.title)
|
||||
if (has) {
|
||||
ElMessage.error('已存在同名文章!')
|
||||
return false
|
||||
}
|
||||
val.id = uuidv4()
|
||||
runtimeStore.editDict.articles.push(val)
|
||||
setTimeout(()=>{
|
||||
listEl.scrollBottom()
|
||||
})
|
||||
}
|
||||
article = cloneDeep(val)
|
||||
//TODO 保存完成后滚动到对应位置
|
||||
ElMessage.success('保存成功!')
|
||||
return true
|
||||
}
|
||||
|
||||
function saveAndNext(val: Article) {
|
||||
if (saveArticle(val)) {
|
||||
add()
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog
|
||||
v-model="show"
|
||||
:full-screen="true"
|
||||
:header="false"
|
||||
>
|
||||
<div class="add-article">
|
||||
<div class="slide">
|
||||
<header>
|
||||
<div class="dict-name">{{ runtimeStore.editDict.name }}</div>
|
||||
<BaseIcon title="选择其他词典/文章" icon="carbon:change-catalog"/>
|
||||
</header>
|
||||
<List
|
||||
ref="listEl"
|
||||
v-model:list="runtimeStore.editDict.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="add" v-if="!article.title">
|
||||
正在添加新文章...
|
||||
</div>
|
||||
<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"
|
||||
type="batch"
|
||||
@save="saveArticle"
|
||||
@saveAndNext="saveAndNext"
|
||||
:article="article"/>
|
||||
</div>
|
||||
</Dialog>
|
||||
</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: var(--color-font-1);
|
||||
background: var(--color-second-bg);
|
||||
display: flex;
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 20rem;
|
||||
top: 20rem;
|
||||
}
|
||||
|
||||
.slide {
|
||||
height: 100%;
|
||||
background: var(--color-main-bg);
|
||||
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: var(--color-font-1);
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
font-size: 18rem;
|
||||
}
|
||||
|
||||
.translate-name {
|
||||
font-size: 16rem;
|
||||
}
|
||||
|
||||
.add {
|
||||
width: 260rem;
|
||||
box-sizing: border-box;
|
||||
border-radius: 8rem;
|
||||
margin-bottom: 10rem;
|
||||
padding: 10rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transition: all .3s;
|
||||
color: var(--color-font-1);
|
||||
background: var(--color-item-active);
|
||||
}
|
||||
|
||||
.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>
|
||||
@@ -240,7 +240,6 @@ function saveAndNext(val: Article) {
|
||||
<div class="slide">
|
||||
<header>
|
||||
<div class="dict-name">{{ runtimeStore.editDict.name }}</div>
|
||||
<BaseIcon title="选择其他词典/文章" icon="carbon:change-catalog"/>
|
||||
</header>
|
||||
<List
|
||||
ref="listEl"
|
||||
@@ -299,8 +298,8 @@ function saveAndNext(val: Article) {
|
||||
}
|
||||
|
||||
.slide {
|
||||
width: 14vw;
|
||||
height: 100%;
|
||||
background: var(--color-main-bg);
|
||||
padding: 0 10rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -41,10 +41,11 @@ function showWordListModal(index: number, item: Word[]) {
|
||||
<div class="flex gap10">
|
||||
<input type="radio" :checked="activeIndex === index">
|
||||
<template v-if="isArticle">
|
||||
<div
|
||||
@click.stop="emitter.emit(EventKey.openArticleListModal,item)"
|
||||
>
|
||||
<div class="title">{{ index + 1 }}. {{ item.title }}</div>
|
||||
<div>
|
||||
<div class="title"
|
||||
@click.stop="emitter.emit(EventKey.openArticleListModal,item)"
|
||||
>{{ index + 1 }}. {{ item.title }}
|
||||
</div>
|
||||
<div class="item-sub-title" v-if="item.titleTranslate"> {{ item.titleTranslate }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -166,14 +166,13 @@ defineExpose({scrollBottom})
|
||||
|
||||
.search {
|
||||
margin: 10rem 0;
|
||||
width: 260rem;
|
||||
}
|
||||
|
||||
.list {
|
||||
.item {
|
||||
width: 260rem;
|
||||
box-sizing: border-box;
|
||||
background: #e1e1e1;
|
||||
background: var(--color-item-bg);
|
||||
color: var(--color-font-1);
|
||||
border-radius: 8rem;
|
||||
margin-bottom: 10rem;
|
||||
padding: 10rem;
|
||||
@@ -196,7 +195,7 @@ defineExpose({scrollBottom})
|
||||
|
||||
&.active {
|
||||
background: var(--color-item-active);
|
||||
color: white;
|
||||
color: var(--color-font-1);
|
||||
}
|
||||
|
||||
&.draggable {
|
||||
|
||||
Reference in New Issue
Block a user