This commit is contained in:
zyronon
2023-10-15 00:11:36 +08:00
parent 1b22cab7ba
commit c7525a8fd2
14 changed files with 364 additions and 125 deletions

View File

@@ -39,4 +39,6 @@ BaseIcon 在选中模式下,应该显示白色
A cold welcome 有bug
[EditAbleText.vue](src%2Fcomponents%2FEditAbleText.vue) 不能自动聚焦
[EditAbleText.vue](src%2Fcomponents%2FEditAbleText.vue) 不能自动聚焦
在文章模式下,背单词时不能调出面板

12
components.d.ts vendored
View File

@@ -8,12 +8,10 @@ export {}
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']
BatchAddArticle: typeof import('./src/components/Add/BatchAddArticle.vue')['default']
ChapterDetail: typeof import('./src/components/ChapterDetail.vue')['default']
ChapterList: typeof import('./src/components/ChapterList.vue')['default']
Close: typeof import('./src/components/Close.vue')['default']
@@ -21,12 +19,17 @@ declare module 'vue' {
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']
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']
ElButton: typeof import('element-plus/es')['ElButton']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElOption: typeof import('element-plus/es')['ElOption']
@@ -37,6 +40,7 @@ declare module 'vue' {
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
FeedbackModal: typeof import('./src/components/Toolbar/FeedbackModal.vue')['default']
Fireworks: typeof import('./src/components/Fireworks.vue')['default']
Footer: typeof import('./src/components/Practice/Footer.vue')['default']

View File

@@ -2,76 +2,239 @@
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 DictList from "@/components/DictList.vue";
import {Dict, DictType, languageCategoryOptions, Sort} from "@/types.ts";
import {$computed, $ref} from "vue/macros";
import {useDisableEventListener} from "@/hooks/event.ts";
import {onMounted, reactive, watch} from "vue";
import {dictionaryResources} from "@/assets/dictionary.ts";
import {cloneDeep} from "lodash-es";
import {useRuntimeStore} from "@/stores/runtime.ts";
import {FormInstance, FormRules} from "element-plus";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import ChapterList from "@/components/ChapterList.vue";
import {useBaseStore} from "@/stores/base.ts";
const baseStore = useBaseStore()
const settingStore = useSettingStore()
const runtimeStore = useRuntimeStore()
const emit = defineEmits([
'close',
])
let step = $ref(0)
let step = $ref(1)
let isEdit = $ref(true)
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',
}
])
let list = $computed(() => {
return baseStore.myDicts.filter(v => v.type === DictType.customArticle).concat([{name: '',} as any])
})
let form = reactive({
id: '',
name: '123',
description: '',
category: '',
tags: [],
languageCategory: '',
language: '',
})
let languageCategoryList = []
let categoryList = {}
let tagList = {}
const ruleFormRef = $ref<FormInstance>()
const rules = reactive<FormRules>({
name: [
{required: true, message: '请输入名称', trigger: 'blur'},
{max: 20, message: '名称不能超过20个字符', trigger: 'blur'},
],
category: [{required: true, message: '请选择', trigger: 'change'}],
tags: [{required: true, message: '请选择', trigger: 'change'}],
languageCategory: [{required: true, message: '请选择', trigger: 'change'}],
})
watch(() => form.languageCategory, () => form.category = '')
watch(() => form.category, () => form.tags = [])
onMounted(() => {
dictionaryResources.map(v => {
// if (!languageCategoryList.find(w => w === v.languageCategory)) {
// languageCategoryList.push(v.languageCategory)
// }
if (categoryList[v.languageCategory]) {
if (!categoryList[v.languageCategory].find(w => w === v.category)) {
categoryList[v.languageCategory].push(v.category)
}
} else {
categoryList[v.languageCategory] = [v.category]
}
if (tagList[v.category]) {
tagList[v.category] = Array.from(new Set(tagList[v.category].concat(v.tags)))
} else {
tagList[v.category] = v.tags
}
})
console.log('languageCategoryList', languageCategoryList)
console.log('categoryList', categoryList)
console.log('tagList', tagList)
})
function selectDict(dict: Dict) {
runtimeStore.editDict = cloneDeep(dict)
isEdit = false
step = 1
}
async function onSubmit() {
await ruleFormRef.validate((valid, fields) => {
if (valid) {
let data = {
sort: Sort.normal,
type: DictType.customArticle,
originWords: [],
words: [],
chapterWordNumber: 30,
chapterWords: [],
chapterIndex: 0,
chapterWordIndex: 0,
statistics: [],
articles: [],
url: '',
...form,
}
if (form.id) {
let rIndex = baseStore.myDicts.findIndex(v => v.id === form.id)
runtimeStore.editDict = data
baseStore.myDicts[rIndex] = cloneDeep(data)
isEdit = false
} else {
if (baseStore.myDicts.find(v => v.name === form.name)) {
return ElMessage.warning('已有相同名称词典!')
} else {
runtimeStore.editDict = data
baseStore.myDicts.push(cloneDeep(data))
isEdit = false
console.log('submit!', data)
}
}
} else {
ElMessage.warning('请填写完整')
}
})
}
function close() {
emit('close')
}
</script>
<template>
<Modal
@close="emit('close')"
title="添加词典">
:show-close="false"
:header="false"
@close="close"
title="我的词典">
<div class="slide">
<div class="slide-list" :class="`step${step}`">
<div class="page">
<header>
<div class="title">
我的词典
</div>
<Icon @click="close"
class="hvr-grow pointer"
width="20" color="#929596"
icon="ion:close-outline"/>
</header>
<div class="list">
<DictItem :list="list"/>
<DictList
@add="step = 1;isEdit = true"
@selectDict="selectDict"
:list="list"/>
</div>
</div>
<div class="page add-page">
<header>
<div class="left" @click.stop="step = 0">
<Icon icon="octicon:arrow-left-24"
class="go" width="20" color="#000000"/>
<div class="title">
词典详情
</div>
</div>
<Icon @click="close"
class="hvr-grow pointer"
width="20" color="#929596"
icon="ion:close-outline"/>
</header>
<div class="detail" v-if="!isEdit">
<div class="dict">
<div class="name">{{ runtimeStore.editDict.name }}</div>
<div class="desc">{{ runtimeStore.editDict.description }}</div>
<div class="num">总文章{{ runtimeStore.editDict.articles.length }}</div>
<div class="num">创建日期-</div>
<div class="num">花费时间-</div>
<div class="num">累积错误-</div>
<div class="num">进度
<el-progress :percentage="10"
:stroke-width="8"
:show-text="false"/>
</div>
</div>
<div class="other">
<div class="common-title">
文章列表{{ runtimeStore.editDict.articles.length }}
<Icon @click="emitter.emit(EventKey.openArticleListModal)"
class="hvr-grow pointer"
width="24" color="#929596"
icon="mi:add"/>
</div>
<ChapterList
:is-article="true"
v-model:active-index="runtimeStore.editDict.chapterIndex"
:dict="runtimeStore.editDict"/>
</div>
</div>
<div class="edit" v-else>
<el-form
ref="ruleFormRef"
:rules="rules"
:model="form"
label-width="120px">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name"/>
</el-form-item>
<el-form-item label="描述">
<el-input v-model="form.description" type="textarea"/>
</el-form-item>
<el-form-item label="分类" prop="languageCategory">
<el-select
v-model="form.languageCategory" placeholder="请选择选项">
<el-option :label="i.name" :value="i.id" v-for="i in languageCategoryOptions"/>
</el-select>
</el-form-item>
<el-form-item label="用途" prop="category">
<el-select v-model="form.category" placeholder="请选择选项">
<el-option :label="i" :value="i" v-for="i in categoryList[form.languageCategory]"/>
</el-select>
</el-form-item>
<el-form-item label="标签" prop="tags">
<el-select
multiple
v-model="form.tags" placeholder="请选择选项">
<el-option :label="i" :value="i" v-for="i in tagList[form.category]"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="step = 0">返回</el-button>
<el-button type="primary" @click="onSubmit">确定</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
@@ -83,7 +246,7 @@ let list = $ref([
@import "@/assets/css/variable";
.slide {
width: 1100rem;
width: 700rem;
height: 75vh;
.slide-list {
@@ -98,4 +261,82 @@ let list = $ref([
}
}
$header-height: 60rem;
.page {
padding: $space;
padding-top: 0;
width: 50%;
header {
color: black;
display: flex;
justify-content: space-between;
align-items: center;
height: $header-height;
.left {
cursor: pointer;
display: flex;
gap: 10rem;
align-items: center;
}
}
}
.add-page {
color: black;
.detail {
display: flex;
position: relative;
gap: $space;
.dict {
overflow: auto;
flex: 3;
display: flex;
flex-direction: column;
gap: 10rem;
padding: 15rem;
min-height: 100rem;
position: relative;
border-radius: 10rem;
background: var(--color-second-bg);
color: var(--color-font-1);
font-size: 14rem;
.name {
font-size: 28rem;
margin-bottom: 10rem;
}
.desc {
font-size: 18rem;
margin-bottom: 30rem;
}
.count {
cursor: pointer;
border-bottom: 2px solid var(--color-item-active);
}
}
.other {
flex: 5;
border-radius: 10rem;
background: var(--color-second-bg);
color: var(--color-font-1);
padding: 10rem;
display: flex;
flex-direction: column;
.common-title {
display: flex;
align-items: center;
justify-content: space-between;
}
}
}
}
</style>

View File

@@ -12,7 +12,6 @@ const props = defineProps<{
const emit = defineEmits<{
'update:activeIndex': [index: number]
showWord: [list: any[]]
}>()

View File

@@ -17,6 +17,8 @@ defineEmits(['click'])
<style scoped lang="scss">
.close {
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
</style>

View File

@@ -9,26 +9,32 @@ const props = defineProps<{
const emit = defineEmits<{
selectDict: [val: Dict]
detail: []
detail: [],
add: []
}>()
</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>
<template v-for="i in list">
<div class="dict-item anim"
:class="selectDictName === i.name && 'active'"
@click="emit('selectDict',i)"
v-if="i.name"
>
<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>
<Icon icon="octicon:arrow-right-24" v-if="selectDictName === i.name"
@click.stop="emit('detail')"
class="go" width="20" color="#929596"/>
</div>
<div v-else class="dict-item add" @click.stop="emit('add')">
<Icon icon="fluent:add-20-filled" width="60" color="#929596"/>
</div>
</template>
</div>
</template>
@@ -75,6 +81,12 @@ const emit = defineEmits<{
}
}
}
.add {
display: flex;
align-items: center;
justify-content: center;
}
}
</style>

View File

@@ -37,13 +37,14 @@ useWindowClick((e: PointerEvent) => {
@import "@/assets/css/style.scss";
.base-input {
border: 1px solid var(--color-main-bg);
border: 1px solid var(--color-second-bg);
border-radius: 4rem;
padding: 3rem 5rem;
transition: all .3s;
display: flex;
align-items: center;
transition: all .3s;
background: white;
:deep(svg) {
transition: all .3s;

View File

@@ -128,7 +128,7 @@ async function cancel() {
<Icon @click="close"
v-if="showClose"
class="close hvr-grow pointer"
width="20" color="#929596"
width="24" color="#929596"
icon="ion:close-outline"/>
</Tooltip>
<div class="modal-header" v-if="header">

View File

@@ -250,7 +250,7 @@ $header-height: 50rem;
margin-left: calc(50% + (var(--toolbar-width) / 2) + $space);
width: $width;
background: var(--color-second-bg);
height: calc(100% - 40rem);
height: calc(100% - 20rem);
display: flex;
flex-direction: column;
transition: all .3s;

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" v-if="settingStore.translate">{{ props.article.titleTranslate }}</div>
</header>
<div class="article-content" ref="articleWrapperRef">
<article>

View File

@@ -12,7 +12,7 @@ let show = $ref(false)
function toggle() {
show = !show
emitter.emit(EventKey.openArticleListModal)
// emitter.emit(EventKey.openArticleListModal)
}
</script>
@@ -25,7 +25,7 @@ function toggle() {
/>
</IconWrapper>
</Tooltip>
<!-- <EditBatchArticleModal/>-->
<EditBatchArticleModal/>
<AddDict v-if="show" @close="show = false"/>
</div>
</template>

View File

@@ -2,7 +2,7 @@
import {dictionaryResources} from '@/assets/dictionary.ts'
import {useBaseStore} from "@/stores/base.ts"
import {watch} from "vue"
import {Dict, DictionaryResource, DictType, Sort, Word} from "@/types.ts"
import {Dict, DictionaryResource, DictType, languageCategoryOptions, Sort, Word} from "@/types.ts"
import {chunk, cloneDeep} from "lodash-es";
import {$computed, $ref} from "vue/macros";
import Modal from "@/components/Modal/Modal.vue";
@@ -38,14 +38,6 @@ const emit = defineEmits<{
close: []
}>()
const options = [
{id: 'article', name: '文章', flag: bookFlag},
{id: 'en', name: '英语', flag: enFlag},
{id: 'ja', name: '日语', flag: jpFlag},
{id: 'de', name: '德语', flag: deFlag},
{id: 'code', name: 'Code', flag: codeFlag},
]
const base = useBaseStore()
let currentLanguage = $ref('en')
let step = $ref(1)
@@ -155,10 +147,6 @@ function clickEvent(e) {
console.log('e', e)
}
function showWord(list: Word[]) {
console.log('list', list)
}
const dictIsArticle = $computed(() => {
return isArticle(runtimeStore.editDict.type)
})
@@ -179,7 +167,7 @@ const dictIsArticle = $computed(() => {
<div class="tab"
:class="currentLanguage === item.id && 'active'"
@click="currentLanguage = item.id"
v-for="item in options">
v-for="item in languageCategoryOptions">
<img :src='item.flag'/>
<span>{{ item.name }}</span>
</div>
@@ -320,7 +308,6 @@ const dictIsArticle = $computed(() => {
</template>
</div>
<ChapterList
@showWord="showWord"
:is-article="dictIsArticle"
v-model:active-index="runtimeStore.editDict.chapterIndex"
:dict="runtimeStore.editDict"/>
@@ -360,7 +347,7 @@ $time: 0.3s;
$header-height: 60rem;
.slide {
width: 1100rem;
width: 1000rem;
height: 75vh;
.slide-list {
@@ -500,7 +487,7 @@ $header-height: 60rem;
.setting {
overflow: auto;
flex: 5;
flex: 4;
background: white;
border-radius: 10rem;
background: var(--color-second-bg);

View File

@@ -8,7 +8,6 @@ export interface State {
newWordDict: Dict,
skipWordDict: Dict,
wrongWordDict: Dict,
dict: Dict,
myDicts: Dict[],
current: {
dictType: DictType,
@@ -24,6 +23,7 @@ export const useBaseStore = defineStore('base', {
state: (): State => {
return {
newWordDict: {
id:'newWordDict',
name: '生词本',
sort: Sort.normal,
type: DictType.newWordDict,
@@ -38,6 +38,7 @@ export const useBaseStore = defineStore('base', {
url: '',
},
skipWordDict: {
id:'skipWordDict',
name: '简单词',
sort: Sort.normal,
type: DictType.skipWordDict,
@@ -52,6 +53,7 @@ export const useBaseStore = defineStore('base', {
url: '',
},
wrongWordDict: {
id:'wrongWordDict',
name: '错词本',
sort: Sort.normal,
type: DictType.wrongWordDict,
@@ -65,36 +67,9 @@ export const useBaseStore = defineStore('base', {
statistics: [],
url: '',
},
// dict: {
// name: '新概念英语-2',
// sort: Sort.normal,
// type: DictType.innerDict,
// originWords: [],
// articles: [],
// words: [],
// chapterWordNumber: 15,
// chapterWords: [],
// chapterIndex: 0,
// chapterWordIndex: 0,
// statistics: [],
// url: '/dicts/NCE_2.json',
// },
dict: {
name: '新概念英语2-课文',
sort: Sort.normal,
type: DictType.publicArticle,
originWords: [],
articles: [],
words: [],
chapterWordNumber: 15,
chapterWords: [],
chapterIndex: 0,
chapterWordIndex: 0,
statistics: [],
url: '/articles/NCE_2.json',
},
myDicts: [
{
id:'新概念英语2-课文',
name: '新概念英语2-课文',
sort: Sort.normal,
type: DictType.publicArticle,
@@ -109,6 +84,7 @@ export const useBaseStore = defineStore('base', {
url: '/articles/NCE_2.json',
},
{
id:'新概念英语2',
name: '新概念英语2',
sort: Sort.normal,
type: DictType.publicDict,

View File

@@ -1,3 +1,9 @@
import bookFlag from "@/assets/img/flags/book.png";
import enFlag from "@/assets/img/flags/en.png";
import jpFlag from "@/assets/img/flags/ja.png";
import deFlag from "@/assets/img/flags/de.png";
import codeFlag from "@/assets/img/flags/code.png";
export type Word = {
"name": string,
"usphone": string,
@@ -34,6 +40,7 @@ export type DictionaryResource = {
}
export interface Dict {
id: string,
name: string,
sort: Sort,
type: DictType,
@@ -155,4 +162,12 @@ export const ShortKeyMap = {
export enum TranslateEngine {
Baidu = 0,
}
}
export const languageCategoryOptions = [
{id: 'article', name: '文章', flag: bookFlag},
{id: 'en', name: '英语', flag: enFlag},
{id: 'ja', name: '日语', flag: jpFlag},
{id: 'de', name: '德语', flag: deFlag},
{id: 'code', name: 'Code', flag: codeFlag},
]