This commit is contained in:
Zyronon
2025-10-11 02:19:36 +08:00
parent dccc248a96
commit b36aaf2370
21 changed files with 138 additions and 158 deletions

View File

@@ -0,0 +1,54 @@
[
{
"id": "article_nce1",
"name": "新概念英语1-课文",
"description": "",
"category": "文章学习",
"tags": [
"新概念英语"
],
"url": "NCE_1.json",
"length": 72,
"translateLanguage": "common",
"language": "en"
},
{
"id": "article_nce2",
"name": "新概念英语2-课文",
"description": "新概念英语2-课文",
"category": "文章学习",
"tags": [
"新概念英语"
],
"url": "NCE_2.json",
"length": 96,
"translateLanguage": "common",
"language": "en"
},
{
"id": "article_nce3",
"name": "新概念英语3-课文",
"description": "新概念英语3-课文",
"category": "文章学习",
"tags": [
"新概念英语"
],
"url": "NCE_3.json",
"length": 60,
"translateLanguage": "common",
"language": "en"
},
{
"id": "article_nce4",
"name": "新概念英语4-课文",
"description": "新概念英语4-课文",
"category": "文章学习",
"tags": [
"新概念英语"
],
"url": "NCE_4.json",
"length": 48,
"translateLanguage": "common",
"language": "en"
}
]

View File

@@ -1,8 +1,8 @@
const {SitemapStream, streamToPromise} = require('sitemap')
const {createWriteStream} = require('fs')
const {resolve} = require('path')
const bookList = require('../public/list/book-list.json')
const dictList = require('../public/list/dict-list.json')
const bookList = require('../public/list/article.json')
const dictList = require('../public/list/dictionary.json')
// 你的网站域名
const SITE_URL = 'https://2study.top'

View File

@@ -1,6 +1,6 @@
const fs = require("fs");
const bookList = require('../public/list/book-list.json')
const dictList = require('../public/list/dict-list.json')
const bookList = require('../public/list/article.json')
const dictList = require('../public/list/dictionary.json')
async function pushUrls() {
// 配置区:改成你的

View File

@@ -1,5 +1,9 @@
import http from "@/utils/http.ts";
export function officialList() {
return http('dict/officialList', null, null, 'get')
}
return http('dicts/officialList', null, null, 'get')
}
export function dictListVersion() {
return http<number>('dicts/dictListVersion', null, null, 'get')
}

View File

@@ -1,3 +1,5 @@
import { useBaseStore } from "@/stores/base.ts";
export const GITHUB = 'https://github.com/zyronon/TypeWords'
export const ProjectName = 'Type Words'
export const Host = '2study.top'
@@ -8,20 +10,23 @@ const common = {
word_dict_list_version: 1
}
const map = {
dev: {
api: 'http://localhost/',
DEV: {
API: 'http://localhost/',
}
}
export const env = Object.assign(map['dev'], common)
export const ENV = Object.assign(map['DEV'], common)
export const IS_OFFICIAL = import.meta.env.DEV
export const RESOURCE_PATH = ENV.API + 'static'
export const DICT_LIST = {
WORD: {
ALL: '/list/dict-list.json',
RECOMMENDED: '/list/recommend-dict-list.json',
ALL: '/list/dictionary.json',
RECOMMENDED: '/list/recommend_dictionary.json',
},
ARTICLE: {
ALL: '/list/book-list.json',
RECOMMENDED: '/list/book-list.json',
ALL: '/list/article.json',
RECOMMENDED: '/list/article.json',
}
}

View File

@@ -1,14 +1,15 @@
import {createApp} from 'vue'
import { createApp } from 'vue'
import './assets/css/style.scss'
import 'virtual:uno.css';
import App from './App.vue'
import {createPinia} from "pinia"
import { createPinia } from "pinia"
import router from "@/router.ts";
import VueVirtualScroller from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import './types/global.d.ts'
import loadingDirective from './directives/loading.tsx'
const pinia = createPinia()
const app = createApp(App)

View File

@@ -2,7 +2,7 @@
import { useBaseStore } from "@/stores/base.ts";
import { useRouter } from "vue-router";
import BasePage from "@/components/BasePage.vue";
import { _getDictDataByUrl, msToHourMinute, total, useNav } from "@/utils";
import { _getDictDataByUrl, msToHourMinute, resourceWrap, total, useNav } from "@/utils";
import { DictResource, DictType } from "@/types/types.ts";
import { useRuntimeStore } from "@/stores/runtime.ts";
import BaseIcon from "@/components/BaseIcon.vue";
@@ -153,7 +153,7 @@ const weekList = $computed(() => {
return list
})
const {data: recommendBookList, isFetching} = useFetch(DICT_LIST.ARTICLE.RECOMMENDED).json()
const {data: recommendBookList, isFetching} = useFetch(resourceWrap(DICT_LIST.ARTICLE.RECOMMENDED)).json()
</script>

View File

@@ -11,7 +11,7 @@ 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, total, useNav } from "@/utils";
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";
@@ -110,7 +110,7 @@ const {
toggleArticleCollect
} = useArticleOptions()
const {data: book_list} = useFetch(DICT_LIST.ARTICLE.ALL).json()
const {data: book_list} = useFetch(resourceWrap(DICT_LIST.ARTICLE.ALL)).json()
function reset() {
MessageBox.confirm(

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { useNav } from "@/utils";
import { resourceWrap, useNav } from "@/utils";
import BasePage from "@/components/BasePage.vue";
import { DictResource } from "@/types/types.ts";
import { useRuntimeStore } from "@/stores/runtime.ts";
@@ -31,7 +31,7 @@ async function getDictDetail(val: DictResource) {
let showSearchInput = $ref(false)
let searchKey = $ref('')
const {data: bookList, isFetching} = useFetch(DICT_LIST.ARTICLE.ALL).json()
const {data: bookList, isFetching} = useFetch(resourceWrap(DICT_LIST.ARTICLE.ALL)).json()
const searchList = computed<any[]>(() => {
if (searchKey) {

View File

@@ -18,7 +18,7 @@ import {
import { useDisableEventListener, useOnKeyboardEventListener, useStartKeyboardEventListener } from "@/hooks/event.ts";
import useTheme from "@/hooks/theme.ts";
import Toast from '@/components/base/toast/Toast.ts'
import { _getDictDataByUrl, _nextTick, cloneDeep, msToMinute, total } from "@/utils";
import { _getDictDataByUrl, _nextTick, cloneDeep, msToMinute, resourceWrap, total } from "@/utils";
import { usePracticeStore } from "@/stores/practice.ts";
import { useArticleOptions } from "@/hooks/dict.ts";
import { genArticleSectionData, usePlaySentenceAudio } from "@/hooks/article.ts";
@@ -107,7 +107,7 @@ async function init() {
if (dictId) {
//先在自己的词典列表里面找,如果没有再在资源列表里面找
dict = store.article.bookList.find(v => v.id === dictId)
let r = await fetch(DICT_LIST.ARTICLE.ALL)
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.id) {

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import { groupBy, useNav } from "@/utils";
import { groupBy, resourceWrap, useNav } from "@/utils";
import BasePage from "@/components/BasePage.vue";
import { DictResource } from "@/types/types.ts";
import { useRuntimeStore } from "@/stores/runtime.ts";
@@ -46,7 +46,7 @@ function groupByDictTags(dictList: DictResource[]) {
}, {})
}
const {data: dict_list, isFetching} = useFetch(DICT_LIST.WORD.ALL).json()
const {data: dict_list, isFetching} = useFetch(resourceWrap(DICT_LIST.WORD.ALL)).json()
const groupedByCategoryAndTag = $computed(() => {
let data = []

View File

@@ -10,7 +10,7 @@ import { Dict, PracticeData, ShortcutKey, TaskWords, Word } from "@/types/types.
import { useDisableEventListener, useOnKeyboardEventListener, useStartKeyboardEventListener } from "@/hooks/event.ts";
import useTheme from "@/hooks/theme.ts";
import { getCurrentStudyWord, useWordOptions } from "@/hooks/dict.ts";
import { _getDictDataByUrl, cloneDeep, shuffle } from "@/utils";
import { _getDictDataByUrl, cloneDeep, resourceWrap, shuffle } from "@/utils";
import { useRoute, useRouter } from "vue-router";
import Footer from "@/pages/word/components/Footer.vue";
import Panel from "@/components/Panel.vue";
@@ -64,7 +64,7 @@ async function loadDict() {
if (dictId) {
//先在自己的词典列表里面找,如果没有再在资源列表里面找
dict = store.word.bookList.find(v => v.id === dictId)
let r = await fetch(DICT_LIST.WORD.ALL)
let r = await fetch(resourceWrap(DICT_LIST.WORD.ALL))
let dict_list = await r.json()
if (!dict) dict = dict_list.flat().find(v => v.id === dictId) as Dict
if (dict && dict.id) {

View File

@@ -2,7 +2,7 @@
import { useBaseStore } from "@/stores/base.ts";
import { useRouter } from "vue-router";
import BaseIcon from "@/components/BaseIcon.vue";
import { _getAccomplishDate, _getDictDataByUrl, useNav } from "@/utils";
import { _getAccomplishDate, _getDictDataByUrl, resourceWrap, useNav } from "@/utils";
import BasePage from "@/components/BasePage.vue";
import { DictResource } from "@/types/types.ts";
import { watch } from "vue";
@@ -154,7 +154,11 @@ function saveLastPracticeIndex(e) {
currentStudy = getCurrentStudyWord()
}
const {data: recommendDictList, isFetching} = useFetch(DICT_LIST.WORD.RECOMMENDED).json()
const {
data: recommendDictList,
isFetching
} = useFetch(resourceWrap(DICT_LIST.WORD.RECOMMENDED)).json()
</script>
<template>

View File

@@ -1,10 +1,11 @@
import {defineStore} from 'pinia'
import {Dict, DictId, Word} from "../types/types.ts"
import {_getAccomplishDate, _getStudyProgress, checkAndUpgradeSaveDict} from "@/utils";
import {shallowReactive} from "vue";
import {getDefaultDict} from "@/types/func.ts";
import {get, set} from 'idb-keyval'
import { SAVE_DICT_KEY } from "@/config/env.ts";
import { defineStore } from 'pinia'
import { Dict, DictId, Word } from "../types/types.ts"
import { _getStudyProgress, checkAndUpgradeSaveDict, shakeCommonDict } from "@/utils";
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";
export interface BaseState {
simpleWords: string[],
@@ -16,7 +17,8 @@ export interface BaseState {
article: {
bookList: Dict[],
studyIndex: number,
}
},
dictListVersion: number
}
export const DefaultBaseState = (): BaseState => ({
@@ -33,7 +35,7 @@ export const DefaultBaseState = (): BaseState => ({
bookList: [
getDefaultDict({id: DictId.wordCollect, name: '收藏'}),
getDefaultDict({id: DictId.wordWrong, name: '错词'}),
getDefaultDict({id: DictId.wordKnown, name: '已掌握',description:'已掌握后的单词不会出现在练习中'}),
getDefaultDict({id: DictId.wordKnown, name: '已掌握', description: '已掌握后的单词不会出现在练习中'}),
// getDefaultDict({
// id: 'nce-new-2',
// name: '新概念英语(新版)-2',
@@ -54,7 +56,8 @@ export const DefaultBaseState = (): BaseState => ({
getDefaultDict({id: DictId.articleCollect, name: '收藏'})
],
studyIndex: -1,
}
},
dictListVersion: 1
})
export const useBaseStore = defineStore('base', {
@@ -128,17 +131,16 @@ export const useBaseStore = defineStore('base', {
})
this.$patch(obj)
},
async init(outData?: any) {
async init() {
return new Promise(async resolve => {
try {
if (outData) {
this.setState(outData)
} else {
let configStr: string = await get(SAVE_DICT_KEY.key)
let data = checkAndUpgradeSaveDict(configStr)
this.setState(data)
let configStr: string = await get(SAVE_DICT_KEY.key)
let data = checkAndUpgradeSaveDict(configStr)
if (IS_OFFICIAL) {
data.dictListVersion = await dictListVersion()
}
set(SAVE_DICT_KEY.key, JSON.stringify({val: this.$state, version: SAVE_DICT_KEY.version}))
this.setState(data)
set(SAVE_DICT_KEY.key, JSON.stringify({val: shakeCommonDict(this.$state), version: SAVE_DICT_KEY.version}))
} catch (e) {
console.error('读取本地dict数据失败', e)
}

View File

@@ -156,6 +156,7 @@ export type DictResource = {
translateLanguage: TranslateLanguageType
//todo 可以考虑删除了
type?: DictType
version?: number
language: LanguageType
}

View File

@@ -1,19 +0,0 @@
import {Dict, DictResource} from "@/types/types.ts";
import {getDictFile} from "@/utils/index.ts";
import {cloneDeep} from "@/utils";
import {nanoid} from "nanoid";
import {getDefaultDict} from "@/types/func.ts";
export async function getArticleBookDataByUrl(val: DictResource) {
let dictResourceUrl = `./dicts/${val.language}/${val.type}/${val.translateLanguage}/${val.url}`;
let s = await getDictFile(dictResourceUrl)
let articles = cloneDeep(s.map(v => {
v.id = nanoid(6)
return v
}))
return cloneDeep({
...getDefaultDict(),
...val,
articles
})
}

View File

@@ -1,9 +1,9 @@
import axios, { AxiosInstance } from 'axios'
import { env } from "@/config/env.ts";
import axios, { AxiosInstance, AxiosResponse } from 'axios'
import { ENV } from "@/config/env.ts";
import Toast from "@/components/base/toast/Toast.ts";
export const axiosInstance: AxiosInstance = axios.create({
baseURL: env.api,
baseURL: ENV.API,
timeout: 15000,
})
@@ -77,7 +77,7 @@ axiosInstance.interceptors.response.use(
)
async function request(url, data = {}, params = {}, method) {
async function request<T>(url, data = {}, params = {}, method): Promise<T> {
return axiosInstance({
url: '/v1/' + url,
method,

View File

@@ -1,11 +1,11 @@
import { BaseState, DefaultBaseState } from "@/stores/base.ts";
import { BaseState, DefaultBaseState, useBaseStore } from "@/stores/base.ts";
import { getDefaultSettingState, SettingState } from "@/stores/setting.ts";
import { Dict, DictId, DictResource, DictType } from "@/types/types.ts";
import { useRouter } from "vue-router";
import { useRuntimeStore } from "@/stores/runtime.ts";
import dayjs from 'dayjs'
import axios from "axios";
import { env, SAVE_DICT_KEY, SAVE_SETTING_KEY } from "@/config/env.ts";
import { ENV, IS_OFFICIAL, RESOURCE_PATH, SAVE_DICT_KEY, SAVE_SETTING_KEY } from "@/config/env.ts";
import { nextTick } from "vue";
import Toast from '@/components/base/toast/Toast.ts'
import { getDefaultDict, getDefaultWord } from "@/types/func.ts";
@@ -129,16 +129,6 @@ export function isMobile(): boolean {
return ('ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0);
}
export async function getDictFile(url: string) {
try {
const r = await fetch(url);
return await r.json();
} catch (err) {
console.log('getDictFile_error', err);
return null;
}
}
export function useNav() {
const router = useRouter()
const runtimeStore = useRuntimeStore()
@@ -174,81 +164,6 @@ export function msToMinute(ms) {
return `${Math.floor(dayjs.duration(ms).asMinutes())}分钟`;
}
export function _fetch(url: string) {
return new Promise<any[]>(async (resolve, reject) => {
await fetch(url).then(async r => {
let v = await r.json()
resolve(v)
}).catch(r => {
console.log('err', r)
reject(r)
})
})
}
export async function _checkDictWords(dict: Dict) {
if ([DictType.collect,
DictType.known,
DictType.wrong].includes(dict.dictType)) {
} else {
//TODO 需要和其他需要下载的地方统一
//如果不是自定义词典并且有url地址才去下载
if (!dict.custom && dict.fileName) {
// let rrr = await axios('http://localhost/static/dict/en/zh/Top50Prepositions-v1.json')
// console.log('r', rrr)
// return
let url = `http://localhost/index.php/v1/support/getDictFile?id=${dict.id}&v=${dict.version}`
// let res: any = await axios(`http://localhost/index.php/v1/support/getDictFile?id=2`)
let res: any
try {
res = await axios(url)
} catch (err) {
console.log('err', err)
}
console.log('res', res)
//说明重定向了
let r
if (res && res.request.responseURL !== url) {
r = res.data
} else {
let dictLocalUrl = `./dicts/${dict.language}/${dict.type}/${dict.translateLanguage}/${dict.url}`;
let r3 = await fetch(dictLocalUrl)
try {
r = await r3.json()
} catch (e) {
}
console.log('r', r)
}
// // dict.words = Object.freeze(v)
// dict.words = v
dict = Object.assign(dict, r)
}
}
}
export async function getWordDictList() {
let url = `${env.api}/v1/support/getWordDictListFile?v=${env.word_dict_list_version}`
let res: any = await axios(url)
// let res: any = await axios(`http://localhost/index.php/v1/support/getDictFile?id=2`)
console.log('res', res)
//说明重定向了
let r
if (res.request.responseURL !== url) {
r = res.data
} else {
let dictLocalUrl = `./word_dict_list.json`;
let r3 = await fetch(dictLocalUrl)
try {
let r1 = await r3.json()
r = r1.data
} catch (e) {
}
}
return r
}
//获取完成天数
export function _getAccomplishDays(total: number, dayNumber: number) {
return Math.ceil(total / dayNumber)
@@ -311,7 +226,7 @@ export async function _getDictDataByUrl(val: DictResource, type: DictType = Dict
if (type === DictType.article) {
dictResourceUrl = `/dicts/${val.language}/article/${val.url}`;
}
let s = await getDictFile(dictResourceUrl)
let s = await fetch(resourceWrap(dictResourceUrl, val.version)).then(r => r.json())
if (s) {
if (type === DictType.word) {
return getDefaultDict({...val, words: s})
@@ -525,3 +440,16 @@ export function total(arr, key) {
return a
}, 0);
}
export function resourceWrap(resource: string, version?: number) {
if (IS_OFFICIAL) {
if (resource.includes('.json')) resource = resource.replace('.json', '');
if (!resource.includes('http')) resource = RESOURCE_PATH + resource
if (version === undefined) {
const store = useBaseStore()
return `${resource}_v${store.dictListVersion}.json`
}
return `${resource}_v${version}.json`
}
return resource;
}