This commit is contained in:
zyronon
2023-10-30 10:35:00 +08:00
parent 1cbbd24cda
commit ecfbe4e061
4 changed files with 283 additions and 77 deletions

2
components.d.ts vendored
View File

@@ -9,6 +9,8 @@ declare module 'vue' {
export interface GlobalComponents {
Add: typeof import('./src/components/Toolbar/Add.vue')['default']
AddDict: typeof import('./src/components/Add/AddDict.vue')['default']
ArticleItem: typeof import('./src/components/Article/ArticleItem.vue')['default']
ArticleList: typeof import('./src/components/Article/ArticleList.vue')['default']
ArticlePanel: typeof import('./src/components/Practice/PracticeArticle/ArticlePanel.vue')['default']
Backgorund: typeof import('./src/components/Backgorund.vue')['default']
BaseButton: typeof import('./src/components/BaseButton.vue')['default']

View File

@@ -0,0 +1,220 @@
<script setup lang="ts">
import BaseIcon from "@/components/BaseIcon.vue";
import Input from "@/components/Input.vue";
import {$computed, $ref} from "vue/macros";
import {cloneDeep, throttle} from "lodash-es";
import {Article} from "@/types.ts";
interface IProps {
list: Article[]
selectItem: Article,
}
const props = defineProps<IProps>()
const emit = defineEmits<{
selectItem: [index: Article],
delSelectItem: [],
'update:searchKey': [val: string],
'update:list': [list: Article[]],
}>()
let dragItem: Article = $ref({id: ''} as any)
let searchKey = $ref('')
let draggable = $ref(false)
let localList = $computed({
get() {
if (searchKey) {
return props.list.filter((item: Article) => {
//把搜索内容,分词之后,判断是否有这个词,比单纯遍历包含体验更好
return searchKey.toLowerCase().split(' ').filter(v => v).some(value => {
return item.title.toLowerCase().includes(value) || item.titleTranslate.toLowerCase().includes(value)
})
})
} else {
return props.list
}
},
set(newValue) {
emit('update:list', newValue)
}
})
function dragstart(item: Article) {
dragItem = item;
}
const dragenter = throttle((e, item: Article) => {
// console.log('dragenter', 'item.id', item.id, 'dragItem.id', dragItem.id)
e.preventDefault();
// 避免源对象触发自身的dragenter事件
if (dragItem.id && dragItem.id !== item.id) {
let rIndex = props.list.findIndex(v => v.id === dragItem.id)
let rIndex2 = props.list.findIndex(v => v.id === item.id)
// console.log('dragenter', 'item-Index', rIndex2, 'dragItem.index', rIndex)
//这里不能直接用localList splice。不知道为什么会导致有筛选的情况下多动无法变换位置
let temp = cloneDeep(props.list)
temp.splice(rIndex, 1);
temp.splice(rIndex2, 0, cloneDeep(dragItem));
localList = temp;
}
}, 300)
function dragover(e) {
// console.log('dragover')
e.preventDefault();
}
function dragend() {
// console.log('dragend')
draggable = false
dragItem = {id: ''} as Article
}
function delItem(item: Article) {
if (item.id === props.selectItem.id) {
emit('delSelectItem')
}
let rIndex = props.list.findIndex(v => v.id === item.id)
if (rIndex > -1) {
localList.splice(rIndex, 1)
}
}
let el: HTMLDivElement = $ref()
function scrollBottom() {
el.scrollTo({
top: el.scrollHeight,
left: 0,
behavior: "smooth",
});
}
defineExpose({scrollBottom})
</script>
<template>
<div class="list-wrapper"
ref="el"
>
<div class="search">
<Input v-model="searchKey"/>
</div>
<transition-group name="drag" class="list" tag="div">
<div class="item"
:class="[
(selectItem.id === item.id) && 'active',
draggable && 'draggable',
(dragItem.id === item.id) && 'active'
]"
@click="emit('selectItem',item)"
v-for="(item,index) in localList"
:key="item.id"
:draggable="draggable"
@dragstart="dragstart(item)"
@dragenter="dragenter($event, item)"
@dragover="dragover($event)"
@dragend="dragend()"
>
<div class="left">
<div class="name"> {{ `${index + 1}. ${item.title}` }}</div>
<div class="translate-name"> {{ ` ${item.titleTranslate}` }}</div>
</div>
<div class="right">
<BaseIcon
@click="delItem(item)"
title="删除" icon="fluent:delete-24-regular"/>
<div
@mousedown="draggable = true"
@mouseup="draggable = false"
>
<BaseIcon
icon="carbon:move"/>
</div>
</div>
</div>
</transition-group>
</div>
</template>
<style scoped lang="scss">
.drag-move, /* 对移动中的元素应用的过渡 */
.drag-enter-active,
.drag-leave-active {
transition: all 0.5s ease;
}
.drag-enter-from,
.drag-leave-to {
opacity: 0;
transform: translateX(50rem);
}
/* 确保将离开的元素从布局流中删除
以便能够正确地计算移动的动画。 */
.drag-leave-active {
position: absolute;
}
.list-wrapper {
transition: all .3s;
flex: 1;
overflow: overlay;
padding-right: 5rem;
.search {
margin: 10rem 0;
width: 100%;
}
.list {
.item {
width: 100%;
box-sizing: border-box;
background: #e1e1e1;
border-radius: 8rem;
margin-bottom: 10rem;
padding: 10rem;
display: flex;
justify-content: space-between;
transition: all .3s;
color: black;
.left {
.name {
font-size: 18rem;
}
.translate-name {
font-size: 16rem;
}
}
.right {
display: flex;
flex-direction: column;
transition: all .3s;
opacity: 0;
}
&:hover {
.right {
opacity: 1;
}
}
&.active {
background: var(--color-item-active);
color: white;
}
&.draggable {
cursor: move;
}
}
}
}
</style>

View File

@@ -10,13 +10,6 @@ import BaseButton from "@/components/BaseButton.vue";
import {useSettingStore} from "@/stores/setting.ts";
import Close from "@/components/Close.vue";
const props = defineProps<{
list?: Word[],
index: number
}>()
const emit = defineEmits<{
'update:index': [i: number]
}>()
const store = useBaseStore()
const settingStore = useSettingStore()
let tabIndex = $ref(0)
@@ -38,41 +31,8 @@ watch(() => settingStore.showPanel, n => {
}
})
const currentDict: Dict = $computed(() => {
return store.myDicts[store.current.index]
})
const currentData = $computed(() => {
if (store.current.dictType !== currentDict.type) return {
list: currentDict.chapterWords[currentDict.chapterIndex] ?? [],
index: -1
}
else return props
})
const newData = $computed(() => {
if (store.current.dictType !== DictType.collect) return {list: store.collect.words ?? [], index: -1}
else return props
})
const wrongData = $computed(() => {
if (store.current.dictType !== DictType.wrong) return {list: store.wrong.words ?? [], index: -1}
else return props
})
const skipData = $computed(() => {
if (store.current.dictType !== DictType.skip) return {list: store.skip.words ?? [], index: -1}
else return props
})
function changeIndex(i: number, dict: Dict) {
dict.wordIndex = i
console.log('i', i, dict.type)
if (store.current.dictType === dict.type) {
emit('update:index', i)
} else {
store.changeDict(dict, dict.chapterIndex, i)
}
store.changeDict(dict, dict.chapterIndex, i)
}
</script>
@@ -86,47 +46,30 @@ function changeIndex(i: number, dict: Dict) {
v-if="!settingStore.showToolbar"/>
</Transition>
<div class="tabs">
<div class="tab current" :class="tabIndex === 0 && 'active'" @click="tabIndex = 0">
{{ currentDict.name + ` ${currentDict.chapterIndex + 1}` }}
</div>
<div class="tab current" :class="tabIndex === 0 && 'active'" @click="tabIndex = 0">当前</div>
<div class="tab" :class="tabIndex === 1 && 'active'" @click="tabIndex = 1">{{ store.collect.name }}</div>
<div class="tab" :class="tabIndex === 2 && 'active'" @click="tabIndex = 2">
{{ store.wrong.name }}
</div>
<div class="tab" :class="tabIndex === 2 && 'active'" @click="tabIndex = 2">{{ store.wrong.name }}</div>
<div class="tab" :class="tabIndex === 3 && 'active'" @click="tabIndex = 3">{{ store.skip.name }}</div>
</div>
</header>
<div class="slide">
<div class="slide-list" :class="`step${tabIndex}`">
<div class="slide-item">
<header>
<div class="dict-name">词数{{ currentData.list.length }}</div>
</header>
<div class="content">
<slot></slot>
</div>
<footer v-if="![DictType.customWord,DictType.word].includes(store.current.dictType)">
<PopConfirm
:title="`确认切换?`"
@confirm="changeIndex(0,currentDict)"
>
<BaseButton>切换</BaseButton>
</PopConfirm>
</footer>
<slot></slot>
</div>
<div class="slide-item">
<header>
<div class="dict-name">总词数{{ newData.list.length }}</div>
<div class="dict-name">总词数{{ store.collect.words.length }}</div>
</header>
<div class="content">
<WordList
class="word-list"
@change="(i:number) => changeIndex(i,store.collect)"
:isActive="settingStore.showPanel && tabIndex === 1"
:list="newData.list"
:activeIndex="newData.index"/>
:list="store.collect.words"
:activeIndex="-1"/>
</div>
<footer v-if="store.current.dictType !== DictType.collect && newData.list.length">
<footer v-if="store.current.dictType !== DictType.collect && store.collect.words.length">
<PopConfirm
:title="`确认切换?`"
@confirm="changeIndex(0,store.collect)"
@@ -138,18 +81,18 @@ function changeIndex(i: number, dict: Dict) {
<div class="slide-item">
<header>
<a href="" target="_blank"></a>
<div class="dict-name">总词数{{ wrongData.list.length }}</div>
<div class="dict-name">总词数{{ store.wrong.words.length }}</div>
</header>
<div class="content">
<WordList
class="word-list"
@change="(i:number) => changeIndex(i,store.wrong)"
:isActive="settingStore.showPanel && tabIndex === 2"
:list="wrongData.list"
:activeIndex="wrongData.index"/>
:list="store.wrong.words"
:activeIndex="-1"/>
</div>
<footer
v-if="store.current.dictType !== DictType.wrong && wrongData.list.length">
v-if="store.current.dictType !== DictType.wrong && store.wrong.words.length">
<PopConfirm
:title="`确认切换?`"
@confirm="changeIndex(0,store.wrong)"
@@ -160,17 +103,17 @@ function changeIndex(i: number, dict: Dict) {
</div>
<div class="slide-item">
<header>
<div class="dict-name">总词数{{ skipData.list.length }}</div>
<div class="dict-name">总词数{{ store.skip.words.length }}</div>
</header>
<div class="content">
<WordList
class="word-list"
@change="(i:number) => changeIndex(i,store.skip)"
:isActive="settingStore.showPanel && tabIndex === 3"
:list="skipData.list"
:activeIndex="skipData.index"/>
:list="store.skip.words"
:activeIndex="-1"/>
</div>
<footer v-if="store.current.dictType !== DictType.skip && skipData.list.length">
<footer v-if="store.current.dictType !== DictType.skip && store.skip.words.length">
<PopConfirm
:title="`确认切换?`"
@confirm="changeIndex(0,store.skip)"

View File

@@ -12,6 +12,10 @@ import {useBaseStore} from "@/stores/base.ts";
import EditSingleArticleModal from "@/components/Article/EditSingleArticleModal.vue";
import {usePracticeStore} from "@/stores/practice.ts";
import {emitter, EventKey} from "@/utils/eventBus.ts";
import ArticleList from "@/components/Article/ArticleList.vue";
import IconWrapper from "@/components/IconWrapper.vue";
import {Icon} from "@iconify/vue";
import Tooltip from "@/components/Tooltip.vue";
const store = useBaseStore()
const practiceStore = usePracticeStore()
@@ -230,10 +234,26 @@ function nextWord(word: ArticleWord) {
<div class="panel-wrapper">
<ArticlePanel
v-if="tabIndex === 0"
:list="[]"
v-model:index="index">
1234
v-if="tabIndex === 0">
<div class="current-practice-article-list">
<header>
<div class="left">
<Tooltip title="切换词典">
<IconWrapper>
<Icon icon="basil:exchange-outline"/>
</IconWrapper>
</Tooltip>
<div class="title">
{{ store.currentDict.name + `${store.currentDict.chapterIndex + 1}` }}
</div>
</div>
<div class="right">
{{ store.currentDict.articles.length }}
</div>
</header>
<ArticleList :select-item="articleData.article"
v-model:list="store.currentDict.articles"/>
</div>
</ArticlePanel>
</div>
@@ -284,4 +304,25 @@ $article-width: 50vw;
margin-left: calc(50% + ($article-width / 2) + $space);
height: calc(100% - 20rem);
}
.current-practice-article-list {
padding: 20rem;
overflow: auto;
header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14rem;
color: black;
.left {
display: flex;
align-items: center;
gap: 10rem;
}
}
}
</style>