save
This commit is contained in:
3
components.d.ts
vendored
3
components.d.ts
vendored
@@ -9,10 +9,12 @@ declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
Add: typeof import('./src/components/Toolbar/Add.vue')['default']
|
||||
AddArticle: typeof import('./src/components/Practice/AddArticle.vue')['default']
|
||||
Alert: typeof import('./src/components/Modal/Alert.vue')['default']
|
||||
Backgorund: typeof import('./src/components/Backgorund.vue')['default']
|
||||
BaseButton: typeof import('./src/components/BaseButton.vue')['default']
|
||||
ChapterDetail: typeof import('./src/components/ChapterDetail.vue')['default']
|
||||
ChapterList: typeof import('./src/components/ChapterList.vue')['default']
|
||||
Confirm: typeof import('./src/components/Modal/Confirm.vue')['default']
|
||||
DictList: typeof import('./src/components/DictList.vue')['default']
|
||||
DictModal: typeof import('./src/components/Toolbar/DictModal.vue')['default']
|
||||
EditAbleText: typeof import('./src/components/EditAbleText.vue')['default']
|
||||
@@ -32,7 +34,6 @@ declare module 'vue' {
|
||||
IconWrapper: typeof import('./src/components/IconWrapper.vue')['default']
|
||||
MiniModal: typeof import('./src/components/MiniModal.vue')['default']
|
||||
Modal: typeof import('./src/components/Modal/Modal.vue')['default']
|
||||
NewModal: typeof import('./src/components/Modal/NewModal.vue')['default']
|
||||
PopConfirm: typeof import('./src/components/PopConfirm.vue')['default']
|
||||
Practice: typeof import('./src/components/Practice/Practice.vue')['default']
|
||||
RepeatSetting: typeof import('./src/components/Toolbar/RepeatSetting.vue')['default']
|
||||
|
||||
@@ -39,7 +39,6 @@ onMounted(() => {
|
||||
useEventListener('keyup', (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
let lastItem = runtimeStore.modalList.pop()
|
||||
console.log('la',lastItem)
|
||||
lastItem && lastItem.close()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -111,7 +111,11 @@ function click() {
|
||||
}
|
||||
|
||||
&.link {
|
||||
border-bottom-color: black;
|
||||
border-radius: 0;
|
||||
border-bottom: 2px solid transparent;
|
||||
&:hover{
|
||||
border-bottom: 2px solid black;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import Modal from "@/components/Modal/Modal.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal>
|
||||
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -10,20 +10,29 @@ interface IProps {
|
||||
modelValue?: boolean,
|
||||
showClose?: boolean,
|
||||
title?: string,
|
||||
subTitle?: string,
|
||||
fullScreen?: boolean;
|
||||
padding?: boolean
|
||||
footer?: boolean
|
||||
header?: boolean
|
||||
confirmButtonText?: string
|
||||
cancelButtonText?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
modelValue: undefined,
|
||||
showClose: true,
|
||||
fullScreen: false,
|
||||
footer: false,
|
||||
header: true,
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
})
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:modelValue',
|
||||
'close'
|
||||
'close',
|
||||
'ok',
|
||||
'cancel',
|
||||
])
|
||||
|
||||
let visible = $ref(false)
|
||||
@@ -47,6 +56,7 @@ function close() {
|
||||
setTimeout(() => {
|
||||
emit('update:modelValue', false)
|
||||
emit('close')
|
||||
emit('cancel')
|
||||
visible = false
|
||||
resolve(true)
|
||||
}, closeTime)
|
||||
@@ -54,6 +64,7 @@ function close() {
|
||||
}
|
||||
|
||||
watch(() => props.modelValue, n => {
|
||||
// console.log('n', n)
|
||||
if (n) {
|
||||
visible = true
|
||||
} else {
|
||||
@@ -62,6 +73,7 @@ watch(() => props.modelValue, n => {
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// console.log('props.modelValue', props.modelValue)
|
||||
if (props.modelValue === undefined) {
|
||||
visible = true
|
||||
}
|
||||
@@ -84,7 +96,7 @@ useEsc(close, () => props.modelValue)
|
||||
fullScreen?'full':'window'
|
||||
]"
|
||||
>
|
||||
<div class="modal-header">
|
||||
<div class="modal-header" v-if="header">
|
||||
<div class="title">{{ props.title }}</div>
|
||||
<Tooltip title="关闭">
|
||||
<Icon @click="close"
|
||||
@@ -93,18 +105,16 @@ useEsc(close, () => props.modelValue)
|
||||
width="20" color="#929596"
|
||||
icon="ion:close-outline"/>
|
||||
</Tooltip>
|
||||
<!-- <div class="sub-title" v-if="props.subTitle">{{ props.subTitle }}</div>-->
|
||||
</div>
|
||||
<div class="modal-body" :class="{padding}">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="modal-footer" v-if="footer">
|
||||
<div class="left">
|
||||
|
||||
</div>
|
||||
<div class="right">
|
||||
<BaseButton type="link">取消</BaseButton>
|
||||
<BaseButton>确定</BaseButton>
|
||||
<BaseButton type="link" @click="close">取消</BaseButton>
|
||||
<BaseButton @click="emit('ok'),close()">确定</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -223,15 +233,9 @@ $header-height: 60rem;
|
||||
.title {
|
||||
color: var(--color-font-1);
|
||||
font-weight: bold;
|
||||
font-size: 28rem;
|
||||
font-size: 24rem;
|
||||
line-height: 33rem;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
color: var(--color-font-1);
|
||||
font-weight: 500;
|
||||
font-size: 14rem;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
@@ -288,11 +292,5 @@ $header-height: 60rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 20rem;
|
||||
top: 20rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -16,42 +16,59 @@ 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)
|
||||
article: () => cloneDeep(DefaultArticle),
|
||||
modelValue: false
|
||||
})
|
||||
|
||||
let article = reactive<Article>(props.article)
|
||||
|
||||
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(['close', 'save'])
|
||||
const emit = defineEmits([
|
||||
'update:modelValue',
|
||||
'close',
|
||||
'save'
|
||||
])
|
||||
|
||||
useDisableEventListener()
|
||||
useDisableEventListener(() => props.modelValue)
|
||||
|
||||
onMounted(() => {
|
||||
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
|
||||
ElMessageBox.alert('检测到本地翻译未格式化,已自动格式', '提示', {
|
||||
confirmButtonText: '好的',
|
||||
})
|
||||
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()
|
||||
}
|
||||
updateSentenceTranslate()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
})
|
||||
|
||||
async function startNetworkTranslate() {
|
||||
@@ -100,6 +117,8 @@ function updateSentenceTranslate() {
|
||||
if (article.useTranslateType === TranslateType.network) {
|
||||
updateLocalSentenceTranslate(article, article.textNetworkTranslate)
|
||||
}
|
||||
} else {
|
||||
article.sections = []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,6 +174,16 @@ function save() {
|
||||
|
||||
if (article.useTranslateType === TranslateType.network) {
|
||||
if (!article.textNetworkTranslate.trim()) {
|
||||
// MessageBox.confirm(
|
||||
// '您选择了“网络翻译”,但译文内容却为空白,是否修改为“不需要翻译”并保存?',
|
||||
// '提示',
|
||||
// () => {
|
||||
// // article.useTranslateType = TranslateType.none
|
||||
// // saveTemp()
|
||||
// },
|
||||
// () => void 0,
|
||||
// )
|
||||
// return
|
||||
return ElMessageBox.confirm('您选择了“网络翻译”,但译文内容却为空白,是否修改为“不需要翻译”并保存?', '提示', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
@@ -215,10 +244,11 @@ watch(() => article.useTranslateType, () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal @close="$emit('close')"
|
||||
title="设置"
|
||||
:full-screen="true"
|
||||
subTitle="修改立即生效,实时保存">
|
||||
<Modal
|
||||
:model-value="props.modelValue"
|
||||
@close="emit('update:modelValue')"
|
||||
:full-screen="true"
|
||||
>
|
||||
<div class="add-article" @click.stop="null">
|
||||
<div class="content">
|
||||
<div class="row">
|
||||
@@ -338,7 +368,7 @@ watch(() => article.useTranslateType, () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Icon @click="$emit('close')"
|
||||
<Icon @click="emit('update:modelValue')"
|
||||
class="close hvr-grow pointer"
|
||||
width="20" color="#929596"
|
||||
icon="ion:close-outline"/>
|
||||
|
||||
@@ -17,6 +17,8 @@ import AddArticle from "@/components/Practice/AddArticle.vue";
|
||||
import {useStartKeyboardEventListener} from "@/hooks/event.ts";
|
||||
import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {updateLocalSentenceTranslate, updateSections} from "@/hooks/translate.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import {MessageBox} from "@/utils/MessageBox.tsx";
|
||||
|
||||
const practiceStore = usePracticeStore()
|
||||
const store = useBaseStore()
|
||||
@@ -57,7 +59,7 @@ watch(() => store.load, n => {
|
||||
|
||||
function getCurrentPractice() {
|
||||
if (store.isArticle) {
|
||||
return
|
||||
// return
|
||||
let tempArticle = {...DefaultArticle, ...store.currentDict.articles[store.currentDict.chapterIndex]}
|
||||
console.log('article', tempArticle)
|
||||
if (tempArticle.sections.length) {
|
||||
@@ -149,11 +151,24 @@ function saveArticle(article: Article) {
|
||||
articleData.article = article
|
||||
}
|
||||
|
||||
function test() {
|
||||
MessageBox.confirm(
|
||||
'2您选择了“本地翻译”,但译文内容却为空白,是否修改为“不需要翻译”并保存?',
|
||||
'1提示',
|
||||
() => {
|
||||
console.log('ok')
|
||||
},
|
||||
() => {
|
||||
console.log('cencal')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="practice">
|
||||
<Toolbar/>
|
||||
<!-- <BaseButton @click="test">test</BaseButton>-->
|
||||
|
||||
<TypeArticle
|
||||
v-if="store.isArticle"
|
||||
:article="articleData.article"
|
||||
@@ -173,8 +188,7 @@ function saveArticle(article: Article) {
|
||||
@repeat="repeat"
|
||||
@next="next"
|
||||
/>
|
||||
<AddArticle v-if="showEditArticle"
|
||||
@close="showEditArticle = false"
|
||||
<AddArticle v-model="showEditArticle"
|
||||
:article="editArticle"
|
||||
@save="saveArticle"
|
||||
/>
|
||||
|
||||
@@ -38,6 +38,7 @@ function options(emitType: string) {
|
||||
|
||||
<template>
|
||||
<Modal
|
||||
:header="false"
|
||||
v-model="statModalIsOpen"
|
||||
@close="options('next')">
|
||||
<div class="statistics">
|
||||
|
||||
@@ -202,7 +202,7 @@ function onKeyDown(e: KeyboardEvent) {
|
||||
let letter = e.key
|
||||
|
||||
let key = currentWord.name[stringIndex]
|
||||
console.log('key', key,)
|
||||
// console.log('key', key,)
|
||||
|
||||
let isWrong = false
|
||||
if (settingStore.ignoreCase) {
|
||||
|
||||
@@ -28,8 +28,7 @@ function toggle() {
|
||||
/>
|
||||
</IconWrapper>
|
||||
</Tooltip>
|
||||
<AddArticle v-if="show"
|
||||
@close="show = false"
|
||||
<AddArticle v-model="show"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -22,8 +22,7 @@ useWatchAllSound()
|
||||
<template>
|
||||
<Modal
|
||||
@close="emit('close')"
|
||||
title="设置"
|
||||
subTitle="修改立即生效,实时保存">
|
||||
title="设置">
|
||||
<div class="setting-modal">
|
||||
<div class="tabs">
|
||||
<div class="tab" :class="tabIndex === 0 && 'active'" @click="tabIndex = 0">
|
||||
|
||||
@@ -4,84 +4,95 @@ import {useRuntimeStore} from "@/stores/runtime.ts";
|
||||
import {$ref} from "vue/macros";
|
||||
|
||||
export function useWindowClick(cb: () => void) {
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.closeOther, cb)
|
||||
window.addEventListener('click', cb)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('click', cb)
|
||||
})
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.closeOther, cb)
|
||||
window.addEventListener('click', cb)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('click', cb)
|
||||
})
|
||||
}
|
||||
|
||||
export function useEventListener(type: string, listener: EventListenerOrEventListenerObject) {
|
||||
onMounted(() => window.addEventListener(type, listener))
|
||||
onUnmounted(() => window.removeEventListener(type, listener))
|
||||
onMounted(() => window.addEventListener(type, listener))
|
||||
onUnmounted(() => window.removeEventListener(type, listener))
|
||||
}
|
||||
|
||||
export function useStartKeyboardEventListener() {
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const runtimeStore = useRuntimeStore()
|
||||
|
||||
useEventListener('keydown', (e: KeyboardEvent) => {
|
||||
if (!runtimeStore.disableEventListener) {
|
||||
emitter.emit(EventKey.keydown, e)
|
||||
}
|
||||
})
|
||||
useEventListener('keyup', (e: KeyboardEvent) => {
|
||||
if (!runtimeStore.disableEventListener) {
|
||||
emitter.emit(EventKey.keyup, e)
|
||||
}
|
||||
})
|
||||
useEventListener('keydown', (e: KeyboardEvent) => {
|
||||
if (!runtimeStore.disableEventListener) {
|
||||
emitter.emit(EventKey.keydown, e)
|
||||
}
|
||||
})
|
||||
useEventListener('keyup', (e: KeyboardEvent) => {
|
||||
if (!runtimeStore.disableEventListener) {
|
||||
emitter.emit(EventKey.keyup, e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function useOnKeyboardEventListener(onKeyDown: (e: KeyboardEvent) => void, onKeyUp: (e: KeyboardEvent) => void) {
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.keydown, onKeyDown)
|
||||
emitter.on(EventKey.keyup, onKeyUp)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.keydown, onKeyDown)
|
||||
emitter.off(EventKey.keyup, onKeyUp)
|
||||
})
|
||||
onMounted(() => {
|
||||
emitter.on(EventKey.keydown, onKeyDown)
|
||||
emitter.on(EventKey.keyup, onKeyUp)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
emitter.off(EventKey.keydown, onKeyDown)
|
||||
emitter.off(EventKey.keyup, onKeyUp)
|
||||
})
|
||||
}
|
||||
|
||||
export function useDisableEventListener() {
|
||||
const runtimeStore = useRuntimeStore()
|
||||
onMounted(() => {
|
||||
runtimeStore.disableEventListener = true
|
||||
})
|
||||
onUnmounted(() => {
|
||||
runtimeStore.disableEventListener = false
|
||||
})
|
||||
export function useDisableEventListener(watchVal?: any) {
|
||||
const runtimeStore = useRuntimeStore()
|
||||
watch(watchVal, n => {
|
||||
if (n) {
|
||||
runtimeStore.disableEventListener = true
|
||||
} else {
|
||||
runtimeStore.disableEventListener = false
|
||||
}
|
||||
})
|
||||
onMounted(() => {
|
||||
if (!watchVal) {
|
||||
runtimeStore.disableEventListener = true
|
||||
}
|
||||
})
|
||||
onUnmounted(() => {
|
||||
if (!watchVal) {
|
||||
runtimeStore.disableEventListener = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function useEsc(close: () => void, watchVal?: any) {
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const id = $ref(Date.now())
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const id = $ref(Date.now())
|
||||
|
||||
watch(watchVal, n => {
|
||||
if (n) {
|
||||
runtimeStore.modalList.push({id, close})
|
||||
} else {
|
||||
let rIndex = runtimeStore.modalList.findIndex(item => item.id === id)
|
||||
if (rIndex > 0) {
|
||||
runtimeStore.modalList.splice(rIndex, 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
watch(watchVal, n => {
|
||||
if (n) {
|
||||
runtimeStore.modalList.push({id, close})
|
||||
} else {
|
||||
let rIndex = runtimeStore.modalList.findIndex(item => item.id === id)
|
||||
if (rIndex > 0) {
|
||||
runtimeStore.modalList.splice(rIndex, 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (watchVal() === undefined) {
|
||||
runtimeStore.modalList.push({id, close})
|
||||
}
|
||||
})
|
||||
onMounted(() => {
|
||||
if (watchVal() === undefined) {
|
||||
runtimeStore.modalList.push({id, close})
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (watchVal() === undefined) {
|
||||
let rIndex = runtimeStore.modalList.findIndex(item => item.id === id)
|
||||
if (rIndex > 0) {
|
||||
runtimeStore.modalList.splice(rIndex, 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
onUnmounted(() => {
|
||||
if (watchVal() === undefined) {
|
||||
let rIndex = runtimeStore.modalList.findIndex(item => item.id === id)
|
||||
if (rIndex > 0) {
|
||||
runtimeStore.modalList.splice(rIndex, 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
68
src/utils/MessageBox.tsx
Normal file
68
src/utils/MessageBox.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import {createApp} from 'vue'
|
||||
import Modal from "@/components/Modal/Modal.vue";
|
||||
|
||||
export class MessageBox {
|
||||
static confirm(
|
||||
content: string,
|
||||
title: string,
|
||||
onOk: () => any = () => void 0,
|
||||
onCancel: () => any = () => void 0,
|
||||
) {
|
||||
let remove = () => {
|
||||
let parent = document.querySelector('.dialog-ctn')
|
||||
parent.remove()
|
||||
}
|
||||
let tempOnCancel = () => {
|
||||
remove()
|
||||
onCancel()
|
||||
}
|
||||
|
||||
const app = createApp({
|
||||
render() {
|
||||
return <Modal
|
||||
footer={true}
|
||||
title={title}
|
||||
onOk={onOk}
|
||||
padding={true}
|
||||
onCancel={tempOnCancel}
|
||||
>
|
||||
<div style=' width: 350rem;color: black;'>{content}</div>
|
||||
</Modal>
|
||||
},
|
||||
})
|
||||
let parent = document.createElement('div')
|
||||
parent.classList.add(...['dialog-ctn'])
|
||||
document.body.append(parent)
|
||||
app.mount(parent)
|
||||
}
|
||||
|
||||
static notice(
|
||||
content: string,
|
||||
title: string,
|
||||
) {
|
||||
let remove = () => {
|
||||
let parent = document.querySelector('.dialog-ctn')
|
||||
parent.remove()
|
||||
}
|
||||
let tempOnCancel = () => {
|
||||
remove()
|
||||
}
|
||||
|
||||
const app = createApp({
|
||||
render() {
|
||||
return <Modal
|
||||
footer={false}
|
||||
title={title}
|
||||
padding={true}
|
||||
onCancel={tempOnCancel}
|
||||
>
|
||||
<div style=' width: 350rem;color: black;'>{content}</div>
|
||||
</Modal>
|
||||
},
|
||||
})
|
||||
let parent = document.createElement('div')
|
||||
parent.classList.add(...['dialog-ctn'])
|
||||
document.body.append(parent)
|
||||
app.mount(parent)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user