wip
This commit is contained in:
@@ -50,7 +50,7 @@ const studyProgress = $computed(() => {
|
||||
@change="$emit('check')"
|
||||
class="absolute left-3 bottom-3 z-2"/>
|
||||
<div class="custom z-1" v-if="item.custom">自定义</div>
|
||||
<div class="custom bg-red! color-white z-1" v-else-if="item.update">更新中</div>
|
||||
<!-- <div class="custom bg-red! color-white z-1" v-else-if="item.update">更新中</div>-->
|
||||
<!-- <div class="sync bg-red! color-white z-1" v-if="!item.sync && isUser && !showCheckbox">未同步</div>-->
|
||||
</div>
|
||||
<div class="text-base mt-1" v-if="item?.cover">{{ item?.name }}</div>
|
||||
|
||||
@@ -107,9 +107,9 @@ defineExpose({ scrollToBottom, scrollToItem })
|
||||
<IconFluentStar16Regular v-if="!isArticleCollect(item)" />
|
||||
<IconFluentStar16Filled v-else />
|
||||
</BaseIcon>
|
||||
<BaseIcon title="可播放音频" v-if="item.audioSrc || item.audioFileId" noBg>
|
||||
<IconBxVolumeFull class="opacity-100! color-gray" />
|
||||
</BaseIcon>
|
||||
<!-- <BaseIcon title="可播放音频" v-if="item.audioSrc || item.audioFileId" noBg>-->
|
||||
<!-- <IconBxVolumeFull class="opacity-100! color-gray" />-->
|
||||
<!-- </BaseIcon>-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,34 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import BasePage from "@/components/BasePage.vue";
|
||||
import BackIcon from "@/components/BackIcon.vue";
|
||||
import Empty from "@/components/Empty.vue";
|
||||
import ArticleList from "@/components/list/ArticleList.vue";
|
||||
import { useBaseStore } from "@/stores/base.ts";
|
||||
import { Article, Dict, DictId, DictType } from "@/types/types.ts";
|
||||
import { useRuntimeStore } from "@/stores/runtime.ts";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import EditBook from "@/pages/article/components/EditBook.vue";
|
||||
import { computed, onMounted } from "vue";
|
||||
import { _dateFormat, _getDictDataByUrl, msToHourMinute, resourceWrap, total, useNav } from "@/utils";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import { useArticleOptions } from "@/hooks/dict.ts";
|
||||
import { getDefaultArticle, getDefaultDict } from "@/types/func.ts";
|
||||
import Toast from "@/components/base/toast/Toast.ts";
|
||||
import ArticleAudio from "@/pages/article/components/ArticleAudio.vue";
|
||||
import { MessageBox } from "@/utils/MessageBox.tsx";
|
||||
import { useSettingStore } from "@/stores/setting.ts";
|
||||
import { useFetch } from "@vueuse/core";
|
||||
import { AppEnv, DICT_LIST } from "@/config/env.ts";
|
||||
import { detail } from "@/apis";
|
||||
import BasePage from '@/components/BasePage.vue'
|
||||
import BackIcon from '@/components/BackIcon.vue'
|
||||
import Empty from '@/components/Empty.vue'
|
||||
import ArticleList from '@/components/list/ArticleList.vue'
|
||||
import { useBaseStore } from '@/stores/base.ts'
|
||||
import { Article, Dict, DictId, DictType } from '@/types/types.ts'
|
||||
import { useRuntimeStore } from '@/stores/runtime.ts'
|
||||
import BaseButton from '@/components/BaseButton.vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import EditBook from '@/pages/article/components/EditBook.vue'
|
||||
import { computed, onMounted } from 'vue'
|
||||
import { _dateFormat, _getDictDataByUrl, msToHourMinute, resourceWrap, total, useNav } from '@/utils'
|
||||
import { getDefaultArticle, getDefaultDict } from '@/types/func.ts'
|
||||
import Toast from '@/components/base/toast/Toast.ts'
|
||||
import ArticleAudio from '@/pages/article/components/ArticleAudio.vue'
|
||||
import { MessageBox } from '@/utils/MessageBox.tsx'
|
||||
import { useSettingStore } from '@/stores/setting.ts'
|
||||
import { useFetch } from '@vueuse/core'
|
||||
import { AppEnv, DICT_LIST } from '@/config/env.ts'
|
||||
import { detail } from '@/apis'
|
||||
|
||||
const runtimeStore = useRuntimeStore()
|
||||
const settingStore = useSettingStore()
|
||||
const base = useBaseStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const {nav} = useNav()
|
||||
const { nav } = useNav()
|
||||
|
||||
let isEdit = $ref(false)
|
||||
let isAdd = $ref(false)
|
||||
@@ -65,13 +62,13 @@ async function addMyStudyList() {
|
||||
name: sbook.name,
|
||||
custom: sbook.custom,
|
||||
complete: sbook.complete,
|
||||
s:`name:${sbook.name},index:${sbook.lastLearnIndex},title:${sbook.articles[sbook.lastLearnIndex].title}`,
|
||||
s: `name:${sbook.name},index:${sbook.lastLearnIndex},title:${sbook.articles[sbook.lastLearnIndex].title}`,
|
||||
})
|
||||
nav('/practice-articles/' + sbook.id)
|
||||
}
|
||||
|
||||
const showBookDetail = computed(() => {
|
||||
return !(isAdd || isEdit);
|
||||
return !(isAdd || isEdit)
|
||||
})
|
||||
|
||||
async function init() {
|
||||
@@ -80,12 +77,13 @@ async function init() {
|
||||
runtimeStore.editDict = getDefaultDict()
|
||||
} else {
|
||||
if (!runtimeStore.editDict.id) {
|
||||
await router.push("/articles")
|
||||
await router.push('/articles')
|
||||
} else {
|
||||
if (!runtimeStore.editDict?.articles?.length
|
||||
&& !runtimeStore.editDict?.custom
|
||||
&& ![DictId.articleCollect].includes(runtimeStore.editDict.en_name || runtimeStore.editDict.id)
|
||||
&& !runtimeStore.editDict?.is_default
|
||||
if (
|
||||
!runtimeStore.editDict?.articles?.length &&
|
||||
!runtimeStore.editDict?.custom &&
|
||||
![DictId.articleCollect].includes(runtimeStore.editDict.en_name || runtimeStore.editDict.id) &&
|
||||
!runtimeStore.editDict?.is_default
|
||||
) {
|
||||
loading = true
|
||||
let r = await _getDictDataByUrl(runtimeStore.editDict, DictType.article)
|
||||
@@ -94,7 +92,7 @@ async function init() {
|
||||
|
||||
if (base.article.bookList.find(book => book.id === runtimeStore.editDict.id)) {
|
||||
if (AppEnv.CAN_REQUEST) {
|
||||
let res = await detail({id: runtimeStore.editDict.id})
|
||||
let res = await detail({ id: runtimeStore.editDict.id })
|
||||
if (res.success) {
|
||||
runtimeStore.editDict.statistics = res.data.statistics
|
||||
if (res.data.articles.length) {
|
||||
@@ -118,37 +116,32 @@ function formClose() {
|
||||
else router.back()
|
||||
}
|
||||
|
||||
const {
|
||||
isArticleCollect,
|
||||
toggleArticleCollect
|
||||
} = useArticleOptions()
|
||||
|
||||
const {data: book_list} = useFetch(resourceWrap(DICT_LIST.ARTICLE.ALL)).json()
|
||||
const { data: book_list } = useFetch(resourceWrap(DICT_LIST.ARTICLE.ALL)).json()
|
||||
|
||||
function reset() {
|
||||
MessageBox.confirm(
|
||||
'继续此操作会重置所有文章,并从官方书籍获取最新文章列表,学习记录不会被重置。确认恢复默认吗?',
|
||||
'恢复默认',
|
||||
async () => {
|
||||
let dict = book_list.value.find(v => v.url === runtimeStore.editDict.url) as Dict
|
||||
if (dict && dict.id) {
|
||||
dict = await _getDictDataByUrl(dict, DictType.article)
|
||||
let rIndex = base.article.bookList.findIndex(v => v.id === runtimeStore.editDict.id)
|
||||
if (rIndex > -1) {
|
||||
let item = base.article.bookList[rIndex]
|
||||
item.custom = false
|
||||
item.id = dict.id
|
||||
item.articles = dict.articles
|
||||
if (item.lastLearnIndex >= item.articles.length) {
|
||||
item.lastLearnIndex = item.articles.length - 1
|
||||
}
|
||||
runtimeStore.editDict = item
|
||||
Toast.success('恢复成功')
|
||||
return
|
||||
'继续此操作会重置所有文章,并从官方书籍获取最新文章列表,学习记录不会被重置。确认恢复默认吗?',
|
||||
'恢复默认',
|
||||
async () => {
|
||||
let dict = book_list.value.find(v => v.url === runtimeStore.editDict.url) as Dict
|
||||
if (dict && dict.id) {
|
||||
dict = await _getDictDataByUrl(dict, DictType.article)
|
||||
let rIndex = base.article.bookList.findIndex(v => v.id === runtimeStore.editDict.id)
|
||||
if (rIndex > -1) {
|
||||
let item = base.article.bookList[rIndex]
|
||||
item.custom = false
|
||||
item.id = dict.id
|
||||
item.articles = dict.articles
|
||||
if (item.lastLearnIndex >= item.articles.length) {
|
||||
item.lastLearnIndex = item.articles.length - 1
|
||||
}
|
||||
runtimeStore.editDict = item
|
||||
Toast.success('恢复成功')
|
||||
return
|
||||
}
|
||||
Toast.error('恢复失败')
|
||||
}
|
||||
Toast.error('恢复失败')
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -181,38 +174,36 @@ function next() {
|
||||
<BasePage>
|
||||
<div class="card mb-0 dict-detail-card flex flex-col" v-if="showBookDetail">
|
||||
<div class="dict-header flex justify-between items-center relative">
|
||||
<BackIcon class="dict-back z-2"/>
|
||||
<BackIcon class="dict-back z-2" />
|
||||
<div class="dict-title absolute text-2xl text-align-center w-full">{{ runtimeStore.editDict.name }}</div>
|
||||
<div class="dict-actions flex">
|
||||
<BaseButton v-if="runtimeStore.editDict.custom && runtimeStore.editDict.url" type="info" @click="reset">
|
||||
恢复默认
|
||||
</BaseButton>
|
||||
<BaseButton :loading="studyLoading||loading" type="info" @click="isEdit = true">编辑</BaseButton>
|
||||
<BaseButton :loading="studyLoading || loading" type="info" @click="isEdit = true">编辑</BaseButton>
|
||||
<BaseButton type="info" @click="router.push('batch-edit-article')">文章管理</BaseButton>
|
||||
<BaseButton :loading="studyLoading||loading" @click="addMyStudyList">学习</BaseButton>
|
||||
<BaseButton :loading="studyLoading || loading" @click="addMyStudyList">学习</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-4 mt-2">
|
||||
<img :src="runtimeStore.editDict?.cover"
|
||||
class="w-30 rounded-md"
|
||||
v-if="runtimeStore.editDict?.cover"
|
||||
alt="">
|
||||
<img :src="runtimeStore.editDict?.cover" class="w-30 rounded-md" v-if="runtimeStore.editDict?.cover" alt="" />
|
||||
<div class="text-lg">介绍:{{ runtimeStore.editDict.description }}</div>
|
||||
</div>
|
||||
<div class="text-base " v-if="totalSpend">总学习时长:{{ totalSpend }}</div>
|
||||
<div class="text-base" v-if="totalSpend">总学习时长:{{ totalSpend }}</div>
|
||||
|
||||
<div class="line my-3"></div>
|
||||
|
||||
<div class="flex flex-1 overflow-hidden">
|
||||
<div class="left flex-[2] scroll p-0">
|
||||
<ArticleList
|
||||
v-if="runtimeStore.editDict.length"
|
||||
@title="handleCheckedChange"
|
||||
@click="handleCheckedChange"
|
||||
:list="runtimeStore.editDict.articles"
|
||||
:active-id="selectArticle.id">
|
||||
v-if="runtimeStore.editDict.length"
|
||||
@title="handleCheckedChange"
|
||||
@click="handleCheckedChange"
|
||||
:list="runtimeStore.editDict.articles"
|
||||
:active-id="selectArticle.id"
|
||||
>
|
||||
</ArticleList>
|
||||
<Empty v-else/>
|
||||
<Empty v-else />
|
||||
</div>
|
||||
<div class="right flex-[4] shrink-0 pl-4 overflow-auto">
|
||||
<div v-if="selectArticle.id">
|
||||
@@ -220,50 +211,46 @@ function next() {
|
||||
<div class="text-2xl font-bold">学习记录</div>
|
||||
<div class="mt-1 mb-3">总学习时长:{{ msToHourMinute(total(currentPractice, 'spend')) }}</div>
|
||||
<div
|
||||
class="item border border-item border-solid mt-2 p-2 bg-[var(--bg-history)] rounded-md flex justify-between"
|
||||
v-for="i in currentPractice">
|
||||
class="item border border-item border-solid mt-2 p-2 bg-[var(--bg-history)] rounded-md flex justify-between"
|
||||
v-for="i in currentPractice"
|
||||
>
|
||||
<span class="color-gray">{{ _dateFormat(i.startDate) }}</span>
|
||||
<span>{{ msToHourMinute(i.spend) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="en-article-family title text-xl">
|
||||
<div class="text-center text-2xl my-2">
|
||||
<ArticleAudio
|
||||
:article="selectArticle"
|
||||
:autoplay="settingStore.articleAutoPlayNext"
|
||||
@ended="next"/>
|
||||
<div class="text-center text-2xl mb-6">
|
||||
<ArticleAudio :article="selectArticle" :autoplay="settingStore.articleAutoPlayNext" @ended="next" />
|
||||
</div>
|
||||
<div class="text-center text-2xl">{{ selectArticle.title }}</div>
|
||||
<div class="text-2xl" v-if="selectArticle.text">
|
||||
<div class="my-5" v-for="t in selectArticle.text.split('\n\n')">{{ t }}</div>
|
||||
<div class="text-right italic mb-5">{{ selectArticle?.quote?.text }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<div class="text-center text-2xl">{{ selectArticle.titleTranslate }}</div>
|
||||
<div class="text-xl" v-if="selectArticle.textTranslate">
|
||||
<div class="my-5" v-for="t in selectArticle.textTranslate.split('\n\n')">{{ t }}</div>
|
||||
<div class="text-right italic mb-5">{{ selectArticle?.quote?.translate }}</div>
|
||||
</div>
|
||||
<Empty v-else/>
|
||||
<Empty v-else />
|
||||
</div>
|
||||
</div>
|
||||
<Empty v-else/>
|
||||
<Empty v-else />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-0 dict-detail-card" v-else>
|
||||
<div class="dict-header flex justify-between items-center relative">
|
||||
<BackIcon class="dict-back z-2" @click="isAdd ? $router.back():(isEdit = false)"/>
|
||||
<div class="dict-title absolute text-2xl text-align-center w-full">{{ runtimeStore.editDict.id ? '修改' : '创建' }}书籍
|
||||
<BackIcon class="dict-back z-2" @click="isAdd ? $router.back() : (isEdit = false)" />
|
||||
<div class="dict-title absolute text-2xl text-align-center w-full">
|
||||
{{ runtimeStore.editDict.id ? '修改' : '创建' }}书籍
|
||||
</div>
|
||||
</div>
|
||||
<div class="center">
|
||||
<EditBook
|
||||
:is-add="isAdd"
|
||||
:is-book="true"
|
||||
@close="formClose"
|
||||
@submit="isEdit = isAdd = false"
|
||||
/>
|
||||
<EditBook :is-add="isAdd" :is-book="true" @close="formClose" @submit="isEdit = isAdd = false" />
|
||||
</div>
|
||||
</div>
|
||||
</BasePage>
|
||||
@@ -284,7 +271,7 @@ function next() {
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.dict-detail-card {
|
||||
height: calc(100vh - 2rem);
|
||||
height: calc(100vh - 2rem);
|
||||
}
|
||||
|
||||
.dict-header {
|
||||
@@ -327,5 +314,4 @@ function next() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
+14
-5
@@ -66,7 +66,7 @@ export interface Sentence {
|
||||
}
|
||||
|
||||
export interface Article {
|
||||
id?: number|string
|
||||
id?: number | string
|
||||
title: string
|
||||
titleTranslate: string
|
||||
text: string
|
||||
@@ -83,6 +83,18 @@ export interface Article {
|
||||
correctAnswer: string[]
|
||||
explanation: string
|
||||
}[]
|
||||
quote?: {
|
||||
start: number
|
||||
text: string
|
||||
translate: string
|
||||
end: number
|
||||
}
|
||||
question?: {
|
||||
start: number
|
||||
text: string
|
||||
translate: string
|
||||
end: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface Statistics {
|
||||
@@ -309,10 +321,7 @@ export const WordPracticeModeStageMap: Record<WordPracticeMode, WordPracticeStag
|
||||
WordPracticeStage.DictationReviewAll,
|
||||
WordPracticeStage.Complete,
|
||||
],
|
||||
[WordPracticeMode.Shuffle]: [
|
||||
WordPracticeStage.Shuffle,
|
||||
WordPracticeStage.Complete,
|
||||
],
|
||||
[WordPracticeMode.Shuffle]: [WordPracticeStage.Shuffle, WordPracticeStage.Complete],
|
||||
[WordPracticeMode.Review]: [
|
||||
WordPracticeStage.IdentifyReview,
|
||||
WordPracticeStage.ListenReview,
|
||||
|
||||
Reference in New Issue
Block a user