update
This commit is contained in:
1
components.d.ts
vendored
1
components.d.ts
vendored
@@ -43,6 +43,7 @@ declare module 'vue' {
|
||||
IconWrapper: typeof import('./src/components/IconWrapper.vue')['default']
|
||||
Input: typeof import('./src/components/Input.vue')['default']
|
||||
List: typeof import('./src/components/List.vue')['default']
|
||||
ListItem: typeof import('./src/components/ListItem.vue')['default']
|
||||
MiniModal: typeof import('./src/components/MiniModal.vue')['default']
|
||||
Modal: typeof import('./src/components/Modal/Modal.vue')['default']
|
||||
Options: typeof import('./src/components/Practice/Options.vue')['default']
|
||||
|
||||
@@ -5,6 +5,7 @@ import Input from "@/components/Input.vue";
|
||||
import {$computed, $ref} from "vue/macros";
|
||||
import {cloneDeep, throttle} from "lodash-es";
|
||||
import {Article} from "@/types.ts";
|
||||
import ListItem from "@/components/ListItem.vue";
|
||||
|
||||
interface IProps {
|
||||
list: Article[]
|
||||
@@ -19,9 +20,7 @@ const emit = defineEmits<{
|
||||
'update:list': [list: Article[]],
|
||||
}>()
|
||||
|
||||
let dragItem: Article = $ref({id: ''} as any)
|
||||
let searchKey = $ref('')
|
||||
let draggable = $ref(false)
|
||||
|
||||
let localList = $computed({
|
||||
get() {
|
||||
@@ -41,47 +40,6 @@ let localList = $computed({
|
||||
}
|
||||
})
|
||||
|
||||
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() {
|
||||
@@ -103,64 +61,16 @@ defineExpose({scrollBottom})
|
||||
<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="ph:star"/>
|
||||
<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>
|
||||
<ListItem @click="emit('selectItem',item)"
|
||||
v-for="(item,index) in localList"
|
||||
:key="item.id">
|
||||
<div class="name"> {{ `${index + 1}. ${item.title}` }}</div>
|
||||
<div class="translate-name"> {{ ` ${item.titleTranslate}` }}</div>
|
||||
</ListItem>
|
||||
</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;
|
||||
@@ -173,50 +83,7 @@ defineExpose({scrollBottom})
|
||||
}
|
||||
|
||||
.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>
|
||||
80
src/components/ListItem.vue
Normal file
80
src/components/ListItem.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import VolumeIcon from "@/components/VolumeIcon.vue";
|
||||
|
||||
defineProps<{
|
||||
showVolume?: boolean,
|
||||
active?: boolean
|
||||
}>()
|
||||
|
||||
defineEmits<{
|
||||
toggleDel: [val: any],
|
||||
toggleCollect: [val: any],
|
||||
play: [],
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="list-item"
|
||||
:class="active"
|
||||
>
|
||||
<div class="left">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div class="right">
|
||||
<BaseIcon
|
||||
@click="$emit('toggleDel')"
|
||||
title="收藏" icon="ph:star"/>
|
||||
<BaseIcon
|
||||
@click="$emit('toggleCollect')"
|
||||
title="删除" icon="fluent:delete-24-regular"/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.list-item {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: white;
|
||||
color: black;
|
||||
font-size: 18rem;
|
||||
border-radius: 8rem;
|
||||
margin-bottom: 10rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transition: all .3s;
|
||||
padding: 10rem;
|
||||
gap: 20rem;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
gap: 10rem;
|
||||
flex-direction: column;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5rem;
|
||||
transition: all .3s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: rgb(226, 226, 226);
|
||||
|
||||
.right {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: var(--color-item-active);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -86,11 +86,11 @@ onUnmounted(() => {
|
||||
margin-bottom: 10rem;
|
||||
transition: all .3s;
|
||||
position: relative;
|
||||
margin-top: 20rem;
|
||||
margin-top: 15rem;
|
||||
|
||||
&.hide {
|
||||
margin-bottom: -90rem;
|
||||
margin-top: 65rem;
|
||||
margin-top: 50rem;
|
||||
|
||||
.progress {
|
||||
bottom: calc(100% + 20rem);
|
||||
|
||||
@@ -147,33 +147,33 @@ defineExpose({del, showWord, hideWord})
|
||||
|
||||
<template>
|
||||
<div class="typing-word">
|
||||
<div class="translate"
|
||||
:style="{
|
||||
<div class="translate"
|
||||
:style="{
|
||||
fontSize: settingStore.fontSize.wordTranslateFontSize +'rem',
|
||||
opacity: settingStore.translate ? 1 : 0
|
||||
}"
|
||||
>
|
||||
<div v-for="i in word.trans">{{ i }}</div>
|
||||
</div>
|
||||
<div class="word-wrapper">
|
||||
<div class="word"
|
||||
:class="wrong && 'is-wrong'"
|
||||
:style="{fontSize: settingStore.fontSize.wordForeignFontSize +'rem'}"
|
||||
>
|
||||
<span class="input" v-if="input">{{ input }}</span>
|
||||
<span class="wrong" v-if="wrong">{{ wrong }}</span>
|
||||
<template v-if="settingStore.dictation">
|
||||
<div v-for="i in word.trans">{{ i }}</div>
|
||||
</div>
|
||||
<div class="word-wrapper">
|
||||
<div class="word"
|
||||
:class="wrong && 'is-wrong'"
|
||||
:style="{fontSize: settingStore.fontSize.wordForeignFontSize +'rem'}"
|
||||
>
|
||||
<span class="input" v-if="input">{{ input }}</span>
|
||||
<span class="wrong" v-if="wrong">{{ wrong }}</span>
|
||||
<template v-if="settingStore.dictation">
|
||||
<span class="letter" v-if="!showFullWord"
|
||||
@mouseenter="settingStore.allowWordTip && (showFullWord = true)">{{
|
||||
displayWord.split('').map(() => '_').join('')
|
||||
}}</span>
|
||||
<span class="letter" v-else @mouseleave="showFullWord = false">{{ displayWord }}</span>
|
||||
</template>
|
||||
<span class="letter" v-else>{{ displayWord }}</span>
|
||||
<span class="letter" v-else @mouseleave="showFullWord = false">{{ displayWord }}</span>
|
||||
</template>
|
||||
<span class="letter" v-else>{{ displayWord }}</span>
|
||||
</div>
|
||||
<VolumeIcon ref="volumeIconRef" :simple="true" :cb="()=>playWordAudio(word.name)"/>
|
||||
</div>
|
||||
<VolumeIcon ref="volumeIconRef" :simple="true" :cb="()=>playWordAudio(word.name)"/>
|
||||
</div>
|
||||
<div class="phonetic">{{ settingStore.wordSoundType === 'us' ? word.usphone : word.ukphone }}</div>
|
||||
<div class="phonetic">{{ settingStore.wordSoundType === 'us' ? word.usphone : word.ukphone }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -182,9 +182,11 @@ defineExpose({del, showWord, hideWord})
|
||||
|
||||
.typing-word {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.phonetic, .translate {
|
||||
font-size: 20rem;
|
||||
@@ -192,6 +194,12 @@ defineExpose({del, showWord, hideWord})
|
||||
transition: all .3s;
|
||||
}
|
||||
|
||||
.translate{
|
||||
position: absolute;
|
||||
transform: translateY(-50%);
|
||||
margin-bottom: 90rem;
|
||||
}
|
||||
|
||||
.word-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -104,6 +104,7 @@ function next(isTyping: boolean = true) {
|
||||
emitter.emit(EventKey.openStatModal, stat)
|
||||
}
|
||||
} else {
|
||||
2
|
||||
data.index++
|
||||
isTyping && practiceStore.inputWordNumber++
|
||||
console.log('这个词完了')
|
||||
@@ -205,11 +206,14 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
@wrong="wordWrong"
|
||||
@next="next"
|
||||
/>
|
||||
<Options
|
||||
@remove="remove"
|
||||
@skip="skip"
|
||||
@collect="collect"
|
||||
/>
|
||||
<div class="options-wrapper">
|
||||
<Options
|
||||
@remove="remove"
|
||||
@skip="skip"
|
||||
@collect="collect"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Teleport to="body">
|
||||
<div class="word-panel-wrapper">
|
||||
<Panel>
|
||||
@@ -302,6 +306,11 @@ useOnKeyboardEventListener(onKeyDown, onKeyUp)
|
||||
}
|
||||
}
|
||||
|
||||
.options-wrapper{
|
||||
position: absolute;
|
||||
//bottom: 0;
|
||||
margin-top: 120rem;
|
||||
}
|
||||
}
|
||||
|
||||
$article-width: 50vw;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import VolumeIcon from "@/components/VolumeIcon.vue";
|
||||
import {Icon} from "@iconify/vue";
|
||||
import {usePlayWordAudio} from "@/hooks/sound.ts";
|
||||
import {Word} from "@/types.ts";
|
||||
import ListItem from "@/components/ListItem.vue";
|
||||
import VolumeIcon from "@/components/VolumeIcon.vue";
|
||||
|
||||
const playWordAudio = usePlayWordAudio()
|
||||
const props = defineProps<{
|
||||
defineProps<{
|
||||
word: Word,
|
||||
active?: boolean
|
||||
}>()
|
||||
@@ -17,84 +17,53 @@ const emit = defineEmits<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="word-item"
|
||||
:class="{active}">
|
||||
<div class="left">
|
||||
<div class="title">
|
||||
<span class="word">{{ word.name }}</span>
|
||||
<span class="phonetic">{{ word.usphone }}</span>
|
||||
</div>
|
||||
<div class="translate">{{ word.trans.join(';') }}</div>
|
||||
<ListItem
|
||||
class="item"
|
||||
:show-volume="true"
|
||||
@play="playWordAudio(word.name)"
|
||||
:active="active">
|
||||
<div class="title">
|
||||
<span class="word">{{ word.name }}</span>
|
||||
<span class="phonetic">{{ word.usphone }}</span>
|
||||
<VolumeIcon class="volume" @click="playWordAudio(word.name)"></VolumeIcon>
|
||||
</div>
|
||||
<div class="right">
|
||||
<VolumeIcon @click="playWordAudio(word.name)"></VolumeIcon>
|
||||
<div class="del"
|
||||
@click.stop="emit('del')"
|
||||
>
|
||||
<Icon
|
||||
icon="fluent:delete-28-regular"
|
||||
width="20"
|
||||
color="#929596"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="translate" v-if="word.trans.length">{{ word.trans.join(';') }}</div>
|
||||
</ListItem>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "@/assets/css/variable.scss";
|
||||
|
||||
.word-item {
|
||||
background: var(--color-header-bg);
|
||||
border-radius: 6rem;
|
||||
padding: 12rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transition: all .3s;
|
||||
color: var(--color-font-1);
|
||||
|
||||
&.active {
|
||||
background: $second;
|
||||
color: white;
|
||||
|
||||
.phonetic {
|
||||
color: white!important;
|
||||
}
|
||||
.item {
|
||||
.volume {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
//background: $dark-main-bg;
|
||||
//background: $item-hover;
|
||||
background: rgb(226, 226, 226);
|
||||
}
|
||||
|
||||
.left {
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rem;
|
||||
|
||||
.word {
|
||||
font-size: 24rem;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.phonetic {
|
||||
font-size: 14rem;
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 5rem;
|
||||
|
||||
.del {
|
||||
cursor: pointer;
|
||||
.volume {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rem;
|
||||
|
||||
.word {
|
||||
font-size: 18rem;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.phonetic {
|
||||
font-size: 14rem;
|
||||
color: gray;
|
||||
}
|
||||
}
|
||||
|
||||
.translate {
|
||||
font-size: 16rem;
|
||||
}
|
||||
</style>
|
||||
@@ -71,46 +71,5 @@ watch(() => props.list, () => {
|
||||
padding: 0 $space;
|
||||
overflow: auto;
|
||||
|
||||
.item {
|
||||
background: var(--color-header-bg);
|
||||
border-radius: 6rem;
|
||||
padding: 12rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
transition: all .3s;
|
||||
color: var(--color-font-1);
|
||||
|
||||
&.active {
|
||||
background: $second;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
//background: $dark-main-bg;
|
||||
//background: $item-hover;
|
||||
background: rgb(226, 226, 226);
|
||||
}
|
||||
|
||||
.left {
|
||||
.letter {
|
||||
margin-bottom: 10rem;
|
||||
font-size: 24rem;
|
||||
line-height: 1;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
gap: 20rem
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -26,8 +26,8 @@ export const useBaseStore = defineStore('base', {
|
||||
...cloneDeep(DefaultDict),
|
||||
words: [
|
||||
{
|
||||
"name": "cancel",
|
||||
"trans": [],
|
||||
"name": "cancelcancelcancelcancelcancelcancelcancelcancel",
|
||||
"trans": ['取消取消取消取消取消取消取消取消取消取消取消取消取消取消取消取消'],
|
||||
"usphone": "",
|
||||
"ukphone": ""
|
||||
},
|
||||
@@ -194,10 +194,10 @@ export const useBaseStore = defineStore('base', {
|
||||
}
|
||||
],
|
||||
current: {
|
||||
// dictType: DictType.word,
|
||||
// index: 1,
|
||||
dictType: DictType.article,
|
||||
index: 0,
|
||||
dictType: DictType.word,
|
||||
index: 1,
|
||||
// dictType: DictType.article,
|
||||
// index: 0,
|
||||
practiceType: DictType.word,
|
||||
},
|
||||
simpleWords: [
|
||||
|
||||
Reference in New Issue
Block a user