소스 검색

fix(web): connectStatus not change when connected backend abort connection; fix relative URL parse; fix loadWebCatalog ElMessage bug

Xwite 2 년 전
부모
커밋
554626b297

+ 34 - 29
modules/web/src/api/api.ts

@@ -22,9 +22,10 @@ export let legado_http_entry_point = ''
 export let legado_webSocket_entry_point = ''
 
 let wsOnError: typeof WebSocket.prototype.onerror = () => {}
-export const setWebsocketOnError = (
-  callback: typeof WebSocket.prototype.onerror,
-) => {
+let wsOnMessage: typeof WebSocket.prototype.onmessage = () => {}
+export const setWebsocketOnMessage = (callback: typeof wsOnMessage) =>
+  (wsOnMessage = callback)
+export const setWebsocketOnError = (callback: typeof wsOnError) => {
   //WebSocket.prototype.onerror = callback
   wsOnError = callback
 }
@@ -33,15 +34,15 @@ export const setApiEntryPoint = (
   http_entry_point: string,
   webSocket_entry_point: string,
 ) => {
-  legado_http_entry_point = http_entry_point
-  legado_webSocket_entry_point = webSocket_entry_point
-  ajax.defaults.baseURL = http_entry_point.toString()
+  legado_http_entry_point = new URL(http_entry_point).toString()
+  legado_webSocket_entry_point = new URL(webSocket_entry_point).toString()
+  ajax.defaults.baseURL = legado_http_entry_point
 }
 
 // 书架API
 // Http
 const getReadConfig = async (http_url = legado_http_entry_point) => {
-  const { data } = await ajax.get<LeagdoApiResponse<string>>('/getReadConfig', {
+  const { data } = await ajax.get<LeagdoApiResponse<string>>('getReadConfig', {
     baseURL: http_url.toString(),
     timeout: 3000,
   })
@@ -52,27 +53,27 @@ const getReadConfig = async (http_url = legado_http_entry_point) => {
   }
 }
 const saveReadConfig = (config: webReadConfig) =>
-  ajax.post<LeagdoApiResponse<string>>('/saveReadConfig', config)
+  ajax.post<LeagdoApiResponse<string>>('saveReadConfig', config)
 
 /** @deprecated: 使用`API.saveBookProgressWithBeacon`以确保在页面或者直接关闭的情况下保存进度 */
 const saveBookProgress = (bookProgress: BookProgress) =>
-  ajax.post('/saveBookProgress', bookProgress)
+  ajax.post('saveBookProgress', bookProgress)
 
 /**主要在直接关闭浏览器情况下可靠发送书籍进度 */
 const saveBookProgressWithBeacon = (bookProgress: BookProgress) => {
   if (!bookProgress) return
   // 常规请求可能会被取消 使用Fetch keep-alive 或者 navigator.sendBeacon
   navigator.sendBeacon(
-    new URL('/saveBookProgress', legado_http_entry_point),
+    new URL('saveBookProgress', legado_http_entry_point),
     JSON.stringify(bookProgress),
   )
 }
 
-const getBookShelf = () => ajax.get<LeagdoApiResponse<Book[]>>('/getBookshelf')
+const getBookShelf = () => ajax.get<LeagdoApiResponse<Book[]>>('getBookshelf')
 
 const getChapterList = (/** @type {string} */ bookUrl: string) =>
   ajax.get<LeagdoApiResponse<BookChapter[]>>(
-    '/getChapterList?url=' + encodeURIComponent(bookUrl),
+    'getChapterList?url=' + encodeURIComponent(bookUrl),
   )
 
 const getBookContent = (
@@ -80,7 +81,7 @@ const getBookContent = (
   /** @type {number} */ chapterIndex: number,
 ) =>
   ajax.get<LeagdoApiResponse<string>>(
-    '/getBookContent?url=' +
+    'getBookContent?url=' +
       encodeURIComponent(bookUrl) +
       '&index=' +
       chapterIndex,
@@ -93,16 +94,17 @@ const search = (
   onFinish: () => void,
 ) => {
   const socket = new WebSocket(
-    new URL('/searchBook', legado_webSocket_entry_point),
+    new URL('searchBook', legado_webSocket_entry_point),
   )
   socket.onerror = wsOnError
 
   socket.onopen = () => {
     socket.send(`{"key":"${searchKey}"}`)
   }
-  socket.onmessage = ({ data }) => {
+  socket.onmessage = event => {
     try {
-      onReceive(JSON.parse(data))
+      onReceive(JSON.parse(event.data))
+      wsOnMessage?.call(socket, event)
     } catch {
       onFinish()
     }
@@ -114,31 +116,31 @@ const search = (
 }
 
 const saveBook = (book: BaseBook) =>
-  ajax.post<LeagdoApiResponse<string>>('/saveBook', book)
+  ajax.post<LeagdoApiResponse<string>>('saveBook', book)
 const deleteBook = (book: BaseBook) =>
-  ajax.post<LeagdoApiResponse<string>>('/deleteBook', book)
+  ajax.post<LeagdoApiResponse<string>>('deleteBook', book)
 
 const isBookSource = /bookSource/i.test(location.href)
 
 // 源编辑API
 // Http
 const getSources = () =>
-  isBookSource ? ajax.get('/getBookSources') : ajax.get('/getRssSources')
+  isBookSource ? ajax.get('getBookSources') : ajax.get('getRssSources')
 
 const saveSource = (data: Source) =>
   isBookSource
-    ? ajax.post<LeagdoApiResponse<string>>('/saveBookSource', data)
-    : ajax.post<LeagdoApiResponse<string>>('/saveRssSource', data)
+    ? ajax.post<LeagdoApiResponse<string>>('saveBookSource', data)
+    : ajax.post<LeagdoApiResponse<string>>('saveRssSource', data)
 
 const saveSources = (data: Source[]) =>
   isBookSource
-    ? ajax.post<LeagdoApiResponse<Source[]>>('/saveBookSources', data)
-    : ajax.post<LeagdoApiResponse<Source[]>>('/saveRssSources', data)
+    ? ajax.post<LeagdoApiResponse<Source[]>>('saveBookSources', data)
+    : ajax.post<LeagdoApiResponse<Source[]>>('saveRssSources', data)
 
 const deleteSource = (data: Source[]) =>
   isBookSource
-    ? ajax.post<LeagdoApiResponse<string>>('/deleteBookSources', data)
-    : ajax.post<LeagdoApiResponse<string>>('/deleteRssSources', data)
+    ? ajax.post<LeagdoApiResponse<string>>('deleteBookSources', data)
+    : ajax.post<LeagdoApiResponse<string>>('deleteRssSources', data)
 
 // webSocket
 const debug = (
@@ -148,7 +150,7 @@ const debug = (
   /** @type {() => void} */ onFinish: () => void,
 ) => {
   const url = new URL(
-    `/${isBookSource ? 'bookSource' : 'rssSource'}Debug`,
+    `${isBookSource ? 'bookSource' : 'rssSource'}Debug`,
     legado_webSocket_entry_point,
   )
 
@@ -157,7 +159,10 @@ const debug = (
   socket.onopen = () => {
     socket.send(JSON.stringify({ tag: sourceUrl, key: searchKey }))
   }
-  socket.onmessage = ({ data }) => onReceive(data)
+  socket.onmessage = event => {
+    onReceive(event.data)
+    wsOnMessage?.call(socket, event)
+  }
 
   socket.onclose = () => {
     onFinish()
@@ -171,7 +176,7 @@ const debug = (
 const getProxyCoverUrl = (coverUrl: string) => {
   if (coverUrl.startsWith(legado_http_entry_point)) return coverUrl
   return new URL(
-    '/cover?path=' + encodeURIComponent(coverUrl),
+    'cover?path=' + encodeURIComponent(coverUrl),
     legado_http_entry_point,
   ).toString()
 }
@@ -188,7 +193,7 @@ const getProxyImageUrl = (
 ) => {
   if (src.startsWith(legado_http_entry_point)) return src
   return new URL(
-    '/image?path=' +
+    'image?path=' +
       encodeURIComponent(src) +
       '&url=' +
       encodeURIComponent(bookUrl) +

+ 21 - 2
modules/web/src/api/index.ts

@@ -1,9 +1,21 @@
 import type { AxiosResponse } from 'axios'
 import type { LeagdoApiResponse } from './api'
-import API, { setWebsocketOnError, setApiEntryPoint } from './api'
+import API, {
+  setWebsocketOnError,
+  setApiEntryPoint,
+  legado_http_entry_point,
+  setWebsocketOnMessage,
+} from './api'
 import ajax from './axios'
 import { validatorHttpUrl } from '@/utils/utils'
 
+import { createApp } from 'vue'
+import App from '@/App.vue'
+import store, { useConnectionStore } from '@/store'
+
+createApp(App).use(store)
+const connectionStore = useConnectionStore()
+
 const LeagdoApiResponseKeys: string[] = Array.of('isSuccess', 'errorMsg')
 
 const notification = ElMessage
@@ -31,6 +43,8 @@ const responseCheckInterceptor = (resp: AxiosResponse) => {
     notification.warning({ message: '后端返回内容格式错误', grouping: true })
     throw new Error()
   }
+  connectionStore.setConnectType('primary')
+  connectionStore.setConnectStatus('已连接 ' + legado_http_entry_point)
   return resp
 }
 
@@ -39,13 +53,18 @@ const axiosErrorInterceptor = (err: unknown) => {
     message: '后端连接失败,请检查阅读WEB服务或者设置其它可用链接',
     grouping: true,
   })
+  connectionStore.setConnectType('danger')
+  connectionStore.setConnectStatus('连接异常')
   throw err
 }
 // http全局
 ajax.interceptors.response.use(responseCheckInterceptor, axiosErrorInterceptor)
 // websocket
 setWebsocketOnError(axiosErrorInterceptor)
-
+setWebsocketOnMessage(() => {
+  connectionStore.setConnectType('primary')
+  connectionStore.setConnectStatus('已连接 ' + legado_http_entry_point)
+})
 /**
  * 按照阅读的默认规则 解析阅读HTTP WebSocket API入口地址
  * @returns [http_url, webSocekt_url]

+ 1 - 0
modules/web/src/auto-imports.d.ts

@@ -70,6 +70,7 @@ declare global {
   const unref: typeof import('vue')['unref']
   const useAttrs: typeof import('vue')['useAttrs']
   const useBookStore: typeof import('./store/bookStore')['useBookStore']
+  const useConnectionStore: typeof import('./store/connectionStore')['useConnectionStore']
   const useCssModule: typeof import('vue')['useCssModule']
   const useCssVars: typeof import('vue')['useCssVars']
   const useId: typeof import('vue')['useId']

+ 3 - 20
modules/web/src/store/bookStore.ts

@@ -29,9 +29,6 @@ let webReadConfigLoadedDate: Date | undefined
 export const useBookStore = defineStore('book', {
   state: () => {
     return {
-      connectStatus: '正在连接后端服务器……',
-      connectType: 'primary' as 'primary' | 'success' | 'danger',
-      newConnect: true,
       searchBooks: [] as SeachBook[],
       shelf: [] as Book[],
       catalog: [] as BookChapter[],
@@ -69,15 +66,6 @@ export const useBookStore = defineStore('book', {
     isNight: state => state.config.theme == 6,
   },
   actions: {
-    setConnectStatus(connectStatus: string) {
-      this.connectStatus = connectStatus
-    },
-    setConnectType(connectType: 'primary' | 'success' | 'danger') {
-      this.connectType = connectType
-    },
-    setNewConnect(newConnect: boolean) {
-      this.newConnect = newConnect
-    },
     /** 从后端加载书架书籍,优先返回内存缓存 */
     async loadBookShelf(): Promise<Book[]> {
       const fetchBookshellf_promise = API.getBookShelf().then(resp => {
@@ -116,16 +104,13 @@ export const useBookStore = defineStore('book', {
         return await fetchBookshellf_promise
       }
     },
-    clearBooks() {
-      this.shelf = []
-    },
     /** 从后端加载书籍目录,优先返回内存缓存 */
     async loadWebCatalog(
       book: typeof this.readingBook,
     ): Promise<BookChapter[]> {
       const { bookUrl, name, chapterIndex } = book
       const fetchChapterList_promise = API.getChapterList(
-        book.bookUrl as string,
+        bookUrl as string,
       ).then(res => {
         const { isSuccess, data, errorMsg } = res.data
         if (isSuccess === false) {
@@ -133,13 +118,14 @@ export const useBookStore = defineStore('book', {
           throw new Error()
         }
         if (
+          bookUrl === this.readingBook.bookUrl &&
           data.length !== this.catalog.length &&
           data.length > 0 &&
           this.catalog.length > 0
         ) {
           ElMessage.info(`书籍${name}: 章节目录已更新`)
         }
-        this.setCatalog(data)
+        this.catalog = data
         console.log(`书籍${name}: 章节目录已更新`)
         return this.catalog
       })
@@ -155,9 +141,6 @@ export const useBookStore = defineStore('book', {
         return await fetchChapterList_promise
       }
     },
-    setCatalog(catalog: BookChapter[]) {
-      this.catalog = catalog
-    },
     setPopCataVisible(visible: boolean) {
       this.popCataVisible = visible
     },

+ 24 - 0
modules/web/src/store/connectionStore.ts

@@ -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
+    },
+  },
+})

+ 1 - 0
modules/web/src/store/index.ts

@@ -2,4 +2,5 @@ import { createPinia } from 'pinia'
 
 export * from './bookStore'
 export * from './sourceStore'
+export * from './connectionStore'
 export default createPinia()

+ 11 - 28
modules/web/src/views/BookShelf.vue

@@ -175,9 +175,9 @@ const searchBook = () => {
 }
 
 //连接状态
-const connectStatus = computed(() => store.connectStatus)
-const connectType = computed(() => store.connectType)
-const newConnect = computed(() => store.newConnect)
+const connectionStore = useConnectionStore()
+const { connectStatus, connectType, newConnect } = storeToRefs(connectionStore)
+
 const setLegadoRetmoteUrl = () => {
   ElMessageBox.prompt(
     '请输入 后端地址 ( 如:http://127.0.0.1:9527 或者通过内网穿透的地址)',
@@ -190,34 +190,30 @@ const setLegadoRetmoteUrl = () => {
       inputErrorMessage: '输入的格式不对',
       beforeClose: (action, instance, done) => {
         if (action === 'confirm') {
-          store.setNewConnect(true)
+          connectionStore.setNewConnect(true)
           instance.confirmButtonLoading = true
           instance.confirmButtonText = '校验中……'
           // instance.inputValue
           const url = new URL(instance.inputValue).toString()
           API.getReadConfig(url)
-            //API.getBookShelf()
             .then(function (config) {
+              connectionStore.setNewConnect(false)
               applyReadConfig(config)
               instance.confirmButtonLoading = false
-              store.setConnectType('success')
               store.clearSearchBooks()
-              store.setNewConnect(false)
               setApiEntryPoint(...parseLeagdoHttpUrlWithDefault(url))
               if (url === location.origin) {
                 localStorage.removeItem(baseURL_localStorage_key)
               } else {
                 localStorage.setItem(baseURL_localStorage_key, url)
               }
-              store.setConnectStatus('已连接 ' + url.toString())
-              fetchBookShelfData()
+              store.loadBookShelf()
               done()
             })
             .catch(function (error) {
+              connectionStore.setNewConnect(false)
               instance.confirmButtonLoading = false
               instance.confirmButtonText = '确定'
-              ElMessage.error('访问失败,请检查您输入的 url')
-              store.setNewConnect(false)
               throw error
             })
         } else {
@@ -287,25 +283,12 @@ const toDetail = (
 }
 
 const loadShelf = async () => {
-  try {
-    await store.loadWebConfig()
-    await store.saveBookProgress()
-    //确保各种网络情况下同步请求先完成
-    await fetchBookShelfData()
-  } catch (error: unknown) {
-    store.setConnectType('danger')
-    store.setConnectStatus('连接异常')
-    store.setNewConnect(false)
-    throw error
-  }
-}
-
-const fetchBookShelfData = async () => {
+  await store.loadWebConfig()
+  await store.saveBookProgress()
+  //确保各种网络情况下同步请求先完成
   await store.loadBookShelf()
-  store.setConnectType('primary')
-  store.setConnectStatus('已连接 ' + legado_http_entry_point)
-  store.setNewConnect(false)
 }
+
 onMounted(() => {
   //获取最近阅读书籍
   const readingRecentStr = localStorage.getItem('readingRecent')