This commit is contained in:
Zyronon
2025-10-12 03:37:34 +08:00
parent b36aaf2370
commit eac6fd6748
13 changed files with 123 additions and 57 deletions

View File

@@ -1,5 +1,4 @@
[
[
{
"id": "cet4",
"name": "CET-4",
@@ -675,9 +674,9 @@
"length": 1665,
"language": "en",
"translateLanguage": "zh-CN"
}
],
[
},
{
"id": "pet-2024",
"name": "PET-2024",
@@ -1873,9 +1872,9 @@
"length": 438,
"language": "en",
"translateLanguage": "zh-CN"
}
],
[
},
{
"id": "gaokao3500",
"name": "高考 3500 词",
@@ -3257,5 +3256,4 @@
"language": "en",
"translateLanguage": "zh-CN"
}
]
]

View File

@@ -1,9 +1,26 @@
import http from "@/utils/http.ts";
export function officialList() {
return http('dicts/officialList', null, null, 'get')
}
import { Dict } from "@/types/types.ts";
export function dictListVersion() {
return http<number>('dicts/dictListVersion', null, null, 'get')
}
export function myDictList() {
return http('dicts/myDictList', null, null, 'get')
}
export function add2MyDict(data) {
return http('dicts/add2MyDict', null, data, 'get')
}
export function addStat(data) {
return http('dicts/addStat', data, null, 'post')
}
export function detail(params?, data?) {
return http<Dict>('dicts/detail', data, params, 'get')
}
export function setDictProp(params?, data?) {
return http<Dict>('dicts/setDictProp', data, params, 'post')
}

View File

@@ -5,13 +5,13 @@ import { nextTick, watch } from 'vue'
const props = withDefaults(defineProps<{
list?: any[],
activeIndex?: number,
activeId?: string,
activeId?: number,
isActive?: boolean
static?: boolean
}>(), {
list: [],
activeIndex: -1,
activeId: '',
activeId: null,
isActive: false,
static: true
})

View File

@@ -17,6 +17,8 @@ const map = {
export const ENV = Object.assign(map['DEV'], common)
export const IS_OFFICIAL = import.meta.env.DEV
export const IS_LOGIN = true
export const CAN_REQUEST = IS_LOGIN && IS_OFFICIAL
export const RESOURCE_PATH = ENV.API + 'static'
export const DICT_LIST = {

View File

@@ -53,7 +53,6 @@ export function useEventListener(type: string, listener: EventListenerOrEventLis
}
})
const remove = () => {
console.log('onUnmounted')
if (isMobile()) {
let s = document.querySelector('#typing-listener')
if (s) {

View File

@@ -155,6 +155,8 @@ const weekList = $computed(() => {
const {data: recommendBookList, isFetching} = useFetch(resourceWrap(DICT_LIST.ARTICLE.RECOMMENDED)).json()
</script>
<template>

View File

@@ -20,7 +20,9 @@ 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 { DICT_LIST } from "@/config/env.ts";
import { CAN_REQUEST, DICT_LIST } from "@/config/env.ts";
import { detail } from "@/apis";
import { run } from "vue-tsc";
const runtimeStore = useRuntimeStore()
const settingStore = useSettingStore()
@@ -57,7 +59,7 @@ async function addMyStudyList() {
}
studyLoading = true
base.changeBook(sbook)
await base.changeBook(sbook)
studyLoading = false
window.umami?.track('startStudyArticle', {
@@ -83,17 +85,28 @@ async function init() {
} else {
if (!runtimeStore.editDict?.articles?.length
&& !runtimeStore.editDict?.custom
&& ![DictId.articleCollect].includes(runtimeStore.editDict.id)
&& ![DictId.articleCollect].includes(runtimeStore.editDict.en_name || runtimeStore.editDict.id)
) {
loading = true
let r = await _getDictDataByUrl(runtimeStore.editDict, DictType.article)
loading = false
runtimeStore.editDict = r
}
if (base.article.bookList.find(book => book.id === runtimeStore.editDict.id)) {
if (CAN_REQUEST) {
let res = await detail({id: runtimeStore.editDict.id})
if (res.success) {
runtimeStore.editDict.statistics = res.data.statistics
if (res.data.articles.length){
runtimeStore.editDict.articles = res.data.articles
}
}
}
}
if (runtimeStore.editDict.articles.length) {
selectArticle = runtimeStore.editDict.articles[0]
}
console.log('runtimeStore.editDict', runtimeStore.editDict)
}
}
}

View File

@@ -34,9 +34,12 @@ import { useRoute, useRouter } from "vue-router";
import PracticeLayout from "@/components/PracticeLayout.vue";
import ArticleAudio from "@/pages/article/components/ArticleAudio.vue";
import VolumeSetting from "@/pages/article/components/VolumeSetting.vue";
import { DICT_LIST, PracticeSaveArticleKey } from "@/config/env.ts";
import { CAN_REQUEST, DICT_LIST, PracticeSaveArticleKey } from "@/config/env.ts";
import { addStat, setDictProp } from "@/apis";
import { useRuntimeStore } from "@/stores/runtime.ts";
const store = useBaseStore()
const runtimeStore = useRuntimeStore()
const settingStore = useSettingStore()
const statStore = usePracticeStore()
const {toggleTheme} = useTheme()
@@ -106,10 +109,10 @@ async function init() {
let dictId = route.params.id
if (dictId) {
//先在自己的词典列表里面找,如果没有再在资源列表里面找
dict = store.article.bookList.find(v => v.id === dictId)
dict = store.article.bookList.find(v => v.id == dictId)
let r = await fetch(resourceWrap(DICT_LIST.ARTICLE.ALL))
let book_list = await r.json()
if (!dict) dict = book_list.flat().find(v => v.id === dictId) as Dict
if (!dict) dict = book_list.find(v => v.id === dictId) as Dict
if (dict && dict.id) {
//如果是不是自定义词典,就请求数据
if (!dict.custom) dict = await _getDictDataByUrl(dict, DictType.article)
@@ -117,7 +120,7 @@ async function init() {
router.push('/articles')
return Toast.warning('没有文章可学习!')
}
store.changeBook(dict)
await store.changeBook(dict)
articleData.list = cloneDeep(store.sbook.articles)
getCurrentPractice()
loading = false
@@ -143,6 +146,7 @@ onMounted(() => {
})
onUnmounted(() => {
runtimeStore.disableEventListener = false
clearInterval(timer)
savePracticeData(true, false)
})
@@ -234,21 +238,29 @@ function setArticle(val: Article) {
})
}
function complete() {
async function complete() {
clearInterval(timer)
setTimeout(() => {
localStorage.removeItem(PracticeSaveArticleKey.key)
}, 1500)
//todo 有空了改成实时保存
let data: Partial<Statistics> & { title: string, id: string } = {
id: articleData.article.id,
let data: Partial<Statistics> & { title: string, articleId: number } = {
articleId: articleData.article.id,
title: articleData.article.title,
spend: statStore.spend,
startDate: statStore.startDate,
total: statStore.total,
wrong: statStore.wrong,
}
if (CAN_REQUEST) {
let res = await addStat({...data, type: 'article'})
if (!res.success) {
Toast.error(res.msg)
}
}
let reportData = {
name: store.sbook.name,
index: store.sbook.lastLearnIndex,
@@ -271,7 +283,6 @@ function getCurrentPractice() {
emitter.emit(EventKey.resetWord)
let currentArticle = articleData.list[store.sbook.lastLearnIndex]
let article = getDefaultArticle(currentArticle)
// console.log('article', article)
if (article.sections.length) {
setArticle(article)
} else {
@@ -320,11 +331,18 @@ function nextWord(word: ArticleWord) {
}
}
function changeArticle(val: ArticleItem) {
async function changeArticle(val: ArticleItem) {
let rIndex = articleData.list.findIndex(v => v.id === val.item.id)
if (rIndex > -1) {
store.sbook.lastLearnIndex = rIndex
getCurrentPractice()
if (CAN_REQUEST) {
let res = await setDictProp(null, store.sbook)
if (!res.success) {
Toast.error(res.msg)
}
}
}
}

View File

@@ -51,11 +51,12 @@ const {data: dict_list, isFetching} = useFetch(resourceWrap(DICT_LIST.WORD.ALL))
const groupedByCategoryAndTag = $computed(() => {
let data = []
if (!dict_list.value) return data
const groupByCategory = groupBy(dict_list.value.flat(), 'category')
const groupByCategory = groupBy(dict_list.value, 'category')
for (const [key, value] of Object.entries(groupByCategory)) {
data.push([key, groupByDictTags(value)])
}
[data[2], data[3]] = [data[3], data[2]];
console.log('data',data)
return data
})
@@ -65,7 +66,7 @@ let searchKey = $ref('')
const searchList = computed<any[]>(() => {
if (searchKey) {
let s = searchKey.toLowerCase()
return dict_list.value.flat().filter((item) => {
return dict_list.value.filter((item) => {
return item.id.toLowerCase().includes(s)
|| item.name.toLowerCase().includes(s)
|| item.category.toLowerCase().includes(s)

View File

@@ -4,8 +4,9 @@ import { _getStudyProgress, checkAndUpgradeSaveDict, shakeCommonDict } from "@/u
import { shallowReactive } from "vue";
import { getDefaultDict } from "@/types/func.ts";
import { get, set } from 'idb-keyval'
import { IS_OFFICIAL, SAVE_DICT_KEY } from "@/config/env.ts";
import { dictListVersion } from "@/apis";
import { CAN_REQUEST, IS_LOGIN, IS_OFFICIAL, SAVE_DICT_KEY } from "@/config/env.ts";
import { add2MyDict, dictListVersion, myDictList } from "@/apis";
import Toast from "@/components/base/toast/Toast.ts";
export interface BaseState {
simpleWords: string[],
@@ -36,18 +37,6 @@ export const DefaultBaseState = (): BaseState => ({
getDefaultDict({id: DictId.wordCollect, name: '收藏'}),
getDefaultDict({id: DictId.wordWrong, name: '错词'}),
getDefaultDict({id: DictId.wordKnown, name: '已掌握', description: '已掌握后的单词不会出现在练习中'}),
// getDefaultDict({
// id: 'nce-new-2',
// name: '新概念英语(新版)-2',
// description: '新概念英语新版第二册',
// category: '青少年英语',
// tags: ['新概念英语'],
// url: 'nce-new-2_v2.json',
// length: 862,
// translateLanguage: 'common',
// language: 'en',
// type: DictType.word
// }),
],
studyIndex: -1,
},
@@ -137,7 +126,16 @@ export const useBaseStore = defineStore('base', {
let configStr: string = await get(SAVE_DICT_KEY.key)
let data = checkAndUpgradeSaveDict(configStr)
if (IS_OFFICIAL) {
data.dictListVersion = await dictListVersion()
let r = await dictListVersion()
if (r.success) {
data.dictListVersion = r.data
}
}
if (CAN_REQUEST) {
let res = await myDictList()
if (res.success) {
Object.assign(data, res.data)
}
}
this.setState(data)
set(SAVE_DICT_KEY.key, JSON.stringify({val: shakeCommonDict(this.$state), version: SAVE_DICT_KEY.version}))
@@ -169,7 +167,13 @@ export const useBaseStore = defineStore('base', {
}
},
//改变书籍
changeBook(val: Dict) {
async changeBook(val: Dict) {
if (CAN_REQUEST) {
let r = await add2MyDict({id: val.id, switch: true})
if (!r.success) {
return Toast.error(r.msg)
}
}
//把其他的书籍里面的文章数据都删掉,全保存在内存里太卡了
this.article.bookList.slice(1).map(v => {
if (!v.custom) {

View File

@@ -35,7 +35,7 @@ export function getDefaultArticleWord(val: Partial<ArticleWord> = {}): ArticleWo
export function getDefaultArticle(val: Partial<Article> = {}): Article {
return {
id: '',
id: null,
title: '',
titleTranslate: '',
text: '',

View File

@@ -66,7 +66,7 @@ export interface Sentence {
}
export interface Article {
id: string,
id?: number,
title: string,
titleTranslate: string,
text: string,
@@ -150,6 +150,7 @@ export type DictResource = {
name: string
description: string
url: string
en_name?: string
length: number
category: string
tags: string[]

View File

@@ -1,4 +1,4 @@
import axios, { AxiosInstance, AxiosResponse } from 'axios'
import axios, { AxiosInstance } from 'axios'
import { ENV } from "@/config/env.ts";
import Toast from "@/components/base/toast/Toast.ts";
@@ -21,7 +21,6 @@ axiosInstance.interceptors.request.use(
axiosInstance.interceptors.response.use(
// 响应正常的处理
(response) => {
// console.log(response)
// console.log(response.data)
const {data} = response
if (response.status !== 200) {
@@ -30,33 +29,42 @@ axiosInstance.interceptors.response.use(
}
if (data === null) {
return Promise.resolve({
code: '009900',
code: 500,
msg: '系统出现错误',
data: {},
success: false,
})
}
if (typeof data !== 'object') {
return Promise.resolve({
data,
success: true,
code: 200
})
}
return Promise.resolve(data)
},
// 请求出错的处理
(error) => {
console.log(error)
if (error.response === undefined && error.status === undefined) {
return Promise.resolve({
code: '009900',
code: 500,
msg: '服务器响应超时',
data: null,
success: false,
})
}
if (error.response.status >= 500) {
return Promise.resolve({
code: '009900',
code: 500,
msg: '服务器出现错误',
data: null,
success: false,
})
}
if (error.response.status === 401) {
return Promise.resolve({
code: '009900',
code: 500,
msg: '用户名或密码不正确',
data: null,
})
@@ -66,18 +74,21 @@ axiosInstance.interceptors.response.use(
return Promise.resolve({
code: data.code,
msg: data.msg,
success: false,
})
}
return Promise.resolve({
code: '009900',
code: 500,
success: false,
msg: data.msg,
data: null,
})
},
)
type AxiosResponse<T> = { code: number, data: T, success: boolean, msg: string }
async function request<T>(url, data = {}, params = {}, method): Promise<T> {
async function request<T>(url, data = {}, params = {}, method): Promise<AxiosResponse<T>> {
return axiosInstance({
url: '/v1/' + url,
method,