feat(project): 初始化项目结构和配置
This commit is contained in:
Executable
+210
@@ -0,0 +1,210 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import API from '@api'
|
||||
import type {
|
||||
BaseBook,
|
||||
Book,
|
||||
BookChapter,
|
||||
BookProgress,
|
||||
SeachBook,
|
||||
} from '@/book'
|
||||
import type { webReadConfig } from '@/web'
|
||||
import { ElMessage } from 'element-plus/es'
|
||||
|
||||
const default_config: webReadConfig = {
|
||||
theme: 0,
|
||||
font: 0,
|
||||
fontSize: 18,
|
||||
readWidth: 800,
|
||||
infiniteLoading: false,
|
||||
customFontName: '',
|
||||
jumpDuration: 1000,
|
||||
spacing: {
|
||||
paragraph: 1,
|
||||
line: 0.8,
|
||||
letter: 0,
|
||||
},
|
||||
}
|
||||
let webReadConfigLoadedDate: Date | undefined
|
||||
|
||||
export const useBookStore = defineStore('book', {
|
||||
state: () => {
|
||||
return {
|
||||
searchBooks: [] as SeachBook[],
|
||||
shelf: [] as Book[],
|
||||
catalog: [] as BookChapter[],
|
||||
readingBook: { chapterPos: 0, chapterIndex: 0 } as BaseBook & {
|
||||
chapterPos: number
|
||||
chapterIndex: number
|
||||
isSeachBook?: boolean
|
||||
},
|
||||
popCataVisible: false,
|
||||
contentLoading: true,
|
||||
showContent: false,
|
||||
config: default_config,
|
||||
miniInterface: false,
|
||||
readSettingsVisible: false,
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
bookProgress: (state): BookProgress | undefined => {
|
||||
if (state.catalog.length == 0) return
|
||||
const { chapterIndex, chapterPos, name, author } = state.readingBook
|
||||
const title = state.catalog[chapterIndex]?.title
|
||||
if (!title) return
|
||||
return {
|
||||
name,
|
||||
author,
|
||||
durChapterIndex: chapterIndex,
|
||||
durChapterPos: chapterPos,
|
||||
durChapterTime: new Date().getTime(),
|
||||
durChapterTitle: title,
|
||||
}
|
||||
},
|
||||
theme: state => {
|
||||
return state.config.theme
|
||||
},
|
||||
isNight: state => state.config.theme == 6,
|
||||
},
|
||||
actions: {
|
||||
/** 从后端加载书架书籍,优先返回内存缓存 */
|
||||
async loadBookShelf(): Promise<Book[]> {
|
||||
const fetchBookshellf_promise = API.getBookShelf().then(resp => {
|
||||
console.log('API.getBookShelf数据返回')
|
||||
const { isSuccess, data, errorMsg } = resp.data
|
||||
if (isSuccess === true) {
|
||||
if (
|
||||
this.shelf.length !== data.length &&
|
||||
this.shelf.length > 0 &&
|
||||
data.length > 0
|
||||
) {
|
||||
ElMessage.info(`书架数据已更新`)
|
||||
}
|
||||
this.shelf = data.sort((a, b) => {
|
||||
const x = a['durChapterTime'] || 0
|
||||
const y = b['durChapterTime'] || 0
|
||||
return y - x
|
||||
})
|
||||
} else {
|
||||
if (errorMsg.includes('还没有添加小说') && this.shelf.length > 0) {
|
||||
ElMessage.info('当前书架上的书籍已经被删除')
|
||||
return (this.shelf = [])
|
||||
}
|
||||
ElMessage.error(errorMsg ?? '后端返回格式错误!')
|
||||
}
|
||||
console.log('书架数据已更新')
|
||||
return this.shelf
|
||||
})
|
||||
|
||||
if (this.shelf.length > 0) {
|
||||
// bookshelf data fetched before:do not await
|
||||
console.log('返回缓存书架数据')
|
||||
return this.shelf
|
||||
} else {
|
||||
console.log('从阅读后端获取书架数据...')
|
||||
return await fetchBookshellf_promise
|
||||
}
|
||||
},
|
||||
/** 从后端加载书籍目录,优先返回内存缓存 */
|
||||
async loadWebCatalog(
|
||||
book: typeof this.readingBook,
|
||||
): Promise<BookChapter[]> {
|
||||
const { bookUrl, name, chapterIndex } = book
|
||||
const fetchChapterList_promise = API.getChapterList(
|
||||
bookUrl as string,
|
||||
).then(res => {
|
||||
const { isSuccess, data, errorMsg } = res.data
|
||||
if (isSuccess === false) {
|
||||
ElMessage.error(errorMsg)
|
||||
throw new Error()
|
||||
}
|
||||
if (
|
||||
bookUrl === this.readingBook.bookUrl &&
|
||||
data.length !== this.catalog.length &&
|
||||
data.length > 0 &&
|
||||
this.catalog.length > 0
|
||||
) {
|
||||
ElMessage.info(`书籍${name}: 章节目录已更新`)
|
||||
}
|
||||
this.catalog = data
|
||||
console.log(`书籍${name}: 章节目录已更新`)
|
||||
return this.catalog
|
||||
})
|
||||
if (
|
||||
bookUrl === this.readingBook.bookUrl &&
|
||||
this.catalog.length > 0 &&
|
||||
this.catalog.length - 1 >= chapterIndex
|
||||
) {
|
||||
console.log(`返回书籍《${name}》 缓存的章节目录`)
|
||||
return this.catalog
|
||||
} else {
|
||||
console.log(`从阅读后端获取书籍《${name}》 章节目录数据...`)
|
||||
return await fetchChapterList_promise
|
||||
}
|
||||
},
|
||||
setPopCataVisible(visible: boolean) {
|
||||
this.popCataVisible = visible
|
||||
},
|
||||
setContentLoading(loading: boolean) {
|
||||
this.contentLoading = loading
|
||||
},
|
||||
setReadingBook(readingBook: typeof this.readingBook) {
|
||||
this.readingBook = readingBook
|
||||
},
|
||||
/** 只从从后端加载一次web阅读配置 */
|
||||
async loadWebConfig() {
|
||||
if (webReadConfigLoadedDate === undefined) {
|
||||
const _config = await API.getReadConfig()
|
||||
webReadConfigLoadedDate = new Date()
|
||||
console.log(
|
||||
`${this.$id}.loadWebConfig: ${webReadConfigLoadedDate.toLocaleString()}成功加载阅读配置`,
|
||||
)
|
||||
return this.setConfig(_config)
|
||||
}
|
||||
console.log(
|
||||
`${this.$id}.loadWebConfig: 已于${webReadConfigLoadedDate.toLocaleString()}成功加载`,
|
||||
)
|
||||
},
|
||||
setConfig(config?: webReadConfig) {
|
||||
this.config = Object.assign({}, this.config, config)
|
||||
},
|
||||
setReadSettingsVisible(visible: boolean) {
|
||||
this.readSettingsVisible = visible
|
||||
},
|
||||
setShowContent(visible: boolean) {
|
||||
this.showContent = visible
|
||||
},
|
||||
setMiniInterface(mini: boolean) {
|
||||
this.miniInterface = mini
|
||||
},
|
||||
async setSearchBooks(books: SeachBook[]) {
|
||||
books.forEach(book => {
|
||||
const isSeachBook = this.shelf.every(
|
||||
item => item.bookUrl !== book.bookUrl,
|
||||
)
|
||||
if (isSeachBook === true) {
|
||||
this.searchBooks.push(book)
|
||||
}
|
||||
})
|
||||
},
|
||||
clearSearchBooks() {
|
||||
this.searchBooks = []
|
||||
},
|
||||
/** 1.保存进度到app 2.修改内存中的数据*/
|
||||
async saveBookProgress() {
|
||||
if (!this.bookProgress) return Promise.resolve()
|
||||
const { bookUrl } = this.readingBook
|
||||
const shelfRaw = toRaw(this.shelf)
|
||||
const findIndex = shelfRaw.findIndex(book => book.bookUrl === bookUrl)
|
||||
if (findIndex > -1) {
|
||||
this.shelf[findIndex] = Object.assign(
|
||||
{},
|
||||
shelfRaw[findIndex],
|
||||
this.bookProgress,
|
||||
)
|
||||
}
|
||||
// 直接关闭浏览器时 http请求可能被取消
|
||||
// return API.saveBookProgress(this.bookProgress)
|
||||
return API.saveBookProgressWithBeacon(this.bookProgress)
|
||||
},
|
||||
},
|
||||
})
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useConnectionStore = defineStore('connection', {
|
||||
state: () => {
|
||||
return {
|
||||
connectStatus: '正在连接后端服务器……',
|
||||
connectType: 'primary' as 'primary' | 'success' | 'danger',
|
||||
newConnect: false,
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setConnectStatus(connectStatus: string) {
|
||||
if (this.newConnect === true) return
|
||||
this.connectStatus = connectStatus
|
||||
},
|
||||
setConnectType(connectType: 'primary' | 'success' | 'danger') {
|
||||
if (this.newConnect === true) return
|
||||
this.connectType = connectType
|
||||
},
|
||||
setNewConnect(newConnect: boolean) {
|
||||
this.newConnect = newConnect
|
||||
},
|
||||
},
|
||||
})
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
export * from './bookStore'
|
||||
export * from './sourceStore'
|
||||
export * from './connectionStore'
|
||||
export default createPinia()
|
||||
Executable
+134
@@ -0,0 +1,134 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import {
|
||||
emptyBookSource,
|
||||
emptyRssSource,
|
||||
getSourceUniqueKey,
|
||||
convertSourcesToMap,
|
||||
} from '@utils/souce'
|
||||
import type { BookSoure, RssSource, Source } from '@/source'
|
||||
|
||||
const isBookSource = /bookSource/i.test(location.href)
|
||||
const emptySource = isBookSource ? emptyBookSource : emptyRssSource
|
||||
|
||||
export const useSourceStore = defineStore('source', {
|
||||
state: () => {
|
||||
return {
|
||||
bookSources: shallowRef([] as BookSoure[]), // 临时存放所有书源,
|
||||
rssSources: shallowRef([] as RssSource[]), // 临时存放所有订阅源
|
||||
savedSources: [] as Source[], // 批量保存到阅读app成功的源
|
||||
currentSource: JSON.parse(JSON.stringify(emptySource)) as Source, // 当前编辑的源
|
||||
currentTab: localStorage.getItem('tabName') || 'editTab',
|
||||
editTabSource: {} as Source, // 生成序列化的json数据
|
||||
isDebuging: false,
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
sources: (state): Source[] =>
|
||||
isBookSource ? state.bookSources : state.rssSources,
|
||||
sourcesMap: function (): Map<string, Source> {
|
||||
return convertSourcesToMap(this.sources)
|
||||
},
|
||||
savedSourcesMap: (state): Map<string, Source> =>
|
||||
convertSourcesToMap(state.savedSources),
|
||||
currentSourceUrl: state =>
|
||||
isBookSource
|
||||
? (state.currentSource as BookSoure).bookSourceUrl
|
||||
: (state.currentSource as RssSource).sourceUrl,
|
||||
searchKey: (state): string =>
|
||||
isBookSource
|
||||
? (state.currentSource as BookSoure)?.ruleSearch?.checkKeyWord || '我的'
|
||||
: '',
|
||||
},
|
||||
actions: {
|
||||
startDebug() {
|
||||
this.currentTab = 'editDebug'
|
||||
this.isDebuging = true
|
||||
},
|
||||
debugFinish() {
|
||||
this.isDebuging = false
|
||||
},
|
||||
|
||||
//拉取源后保存
|
||||
saveSources(data: Source[]) {
|
||||
if (isBookSource) {
|
||||
this.bookSources = markRaw(data) as BookSoure[]
|
||||
} else {
|
||||
this.rssSources = markRaw(data) as RssSource[]
|
||||
}
|
||||
},
|
||||
//批量推送
|
||||
setPushReturnSources(returnSoures: Source[]) {
|
||||
this.savedSources = returnSoures
|
||||
},
|
||||
//删除源
|
||||
deleteSources(data: Source[]) {
|
||||
const sources: Source[] = isBookSource
|
||||
? this.bookSources
|
||||
: this.rssSources
|
||||
data.forEach(source => {
|
||||
const index = sources.indexOf(source)
|
||||
if (index > -1) sources.splice(index, 1)
|
||||
})
|
||||
},
|
||||
//保存当前编辑源
|
||||
saveCurrentSource() {
|
||||
const source = this.currentSource,
|
||||
map = this.sourcesMap
|
||||
map.set(getSourceUniqueKey(source), JSON.parse(JSON.stringify(source)))
|
||||
this.saveSources(Array.from(map.values()))
|
||||
},
|
||||
// 更改当前编辑的源qq
|
||||
changeCurrentSource(source: Source) {
|
||||
this.currentSource = JSON.parse(JSON.stringify(source))
|
||||
},
|
||||
// update editTab tabName and editTab info
|
||||
changeTabName(tabName: string) {
|
||||
this.currentTab = tabName
|
||||
localStorage.setItem('tabName', tabName)
|
||||
},
|
||||
changeEditTabSource(source: Source) {
|
||||
this.editTabSource = JSON.parse(JSON.stringify(source))
|
||||
},
|
||||
editHistory(history: Source) {
|
||||
let historyObj
|
||||
if (localStorage.getItem('history')) {
|
||||
historyObj = JSON.parse(localStorage.getItem('history')!)
|
||||
historyObj.new.push(history)
|
||||
if (historyObj.new.length > 50) {
|
||||
historyObj.new.shift()
|
||||
}
|
||||
if (historyObj.old.length > 50) {
|
||||
historyObj.old.shift()
|
||||
}
|
||||
localStorage.setItem('history', JSON.stringify(historyObj))
|
||||
} else {
|
||||
const arr = { new: [history], old: [] }
|
||||
localStorage.setItem('history', JSON.stringify(arr))
|
||||
}
|
||||
},
|
||||
editHistoryUndo() {
|
||||
if (localStorage.getItem('history')) {
|
||||
const historyObj = JSON.parse(localStorage.getItem('history')!)
|
||||
historyObj.old.push(this.currentSource)
|
||||
if (historyObj.new.length) {
|
||||
this.currentSource = historyObj.new.pop()
|
||||
}
|
||||
localStorage.setItem('history', JSON.stringify(historyObj))
|
||||
}
|
||||
},
|
||||
clearAllHistory() {
|
||||
localStorage.setItem('history', JSON.stringify({ new: [], old: [] }))
|
||||
},
|
||||
clearEdit() {
|
||||
this.editTabSource = {} as Source
|
||||
this.currentSource = JSON.parse(JSON.stringify(emptySource)) //复制一份新对象
|
||||
},
|
||||
|
||||
// clear all source
|
||||
clearAllSource() {
|
||||
this.bookSources = []
|
||||
this.rssSources = []
|
||||
this.savedSources = []
|
||||
},
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user