fix(modules/web): 重构阅读API;封面图片优先使用源站资源
- 添加图片代理接口API;API地址测试(3s超时)
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
"ComputedRef": true,
|
"ComputedRef": true,
|
||||||
"EffectScope": true,
|
"EffectScope": true,
|
||||||
"ElMessage": true,
|
"ElMessage": true,
|
||||||
|
"ElMessageBox": true,
|
||||||
"InjectionKey": true,
|
"InjectionKey": true,
|
||||||
"PropType": true,
|
"PropType": true,
|
||||||
"Ref": true,
|
"Ref": true,
|
||||||
@@ -78,8 +79,6 @@
|
|||||||
"watch": true,
|
"watch": true,
|
||||||
"watchEffect": true,
|
"watchEffect": true,
|
||||||
"watchPostEffect": true,
|
"watchPostEffect": true,
|
||||||
"watchSyncEffect": true,
|
"watchSyncEffect": true
|
||||||
"toValue": true,
|
|
||||||
"WritableComputedRef": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,25 +1,10 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
const SECOND = 1000;
|
const SECOND = 1000;
|
||||||
const remoteIp = ref(localStorage.getItem("remoteIp"));
|
|
||||||
|
|
||||||
const ajax = axios.create({
|
const ajax = axios.create({
|
||||||
// baseURL: import.meta.env.VITE_API || location.origin,
|
baseURL: import.meta.env.VITE_API || localStorage.getItem("remoteIp") || location.origin,
|
||||||
timeout: 120 * SECOND,
|
timeout: 120 * SECOND,
|
||||||
});
|
});
|
||||||
|
|
||||||
ajax.interceptors.request.use((config) => {
|
|
||||||
config.baseURL = baseUrl();
|
|
||||||
return config;
|
|
||||||
});
|
|
||||||
|
|
||||||
export default ajax;
|
export default ajax;
|
||||||
|
|
||||||
export const setRemoteIp = (ip) => {
|
|
||||||
remoteIp.value = ip;
|
|
||||||
localStorage.setItem("remoteIp", ip);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const baseUrl = () => {
|
|
||||||
return remoteIp.value || location.origin;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,14 +1,48 @@
|
|||||||
import ajax, { baseUrl } from "./axios";
|
import ajax from "./axios";
|
||||||
import { ElMessage } from "element-plus/es";
|
import { ElMessage } from "element-plus/es";
|
||||||
|
|
||||||
/** https://github.com/gedoor/legado/tree/master/app/src/main/java/io/legado/app/api */
|
/** https://github.com/gedoor/legado/tree/master/app/src/main/java/io/legado/app/api */
|
||||||
/** https://github.com/gedoor/legado/tree/master/app/src/main/java/io/legado/app/web */
|
/** https://github.com/gedoor/legado/tree/master/app/src/main/java/io/legado/app/web */
|
||||||
|
|
||||||
const getUrl = () => {
|
let legado_http_origin
|
||||||
const { hostname, port } = new URL(baseUrl());
|
let legado_webSocket_origin
|
||||||
return `${hostname}:${Number(port) + 1}`;
|
|
||||||
|
const setLeagdoHttpUrl = (http_url) => {
|
||||||
|
let legado_webSocket_port;
|
||||||
|
const { protocol, hostname, port } = new URL(http_url);
|
||||||
|
if (!protocol.startsWith("http"))
|
||||||
|
throw new Error("unexpect protocol:" + http_url);
|
||||||
|
ajax.defaults.baseURL = http_url;
|
||||||
|
legado_http_origin = http_url;
|
||||||
|
if (port !== "") {
|
||||||
|
legado_webSocket_port = Number(port) + 1;
|
||||||
|
} else {
|
||||||
|
legado_webSocket_port = protocol.startsWith("https:") ? "444" : "81";
|
||||||
|
}
|
||||||
|
legado_webSocket_origin =
|
||||||
|
`${protocol.startsWith("https:") ? "wss://" : "ws://"}${hostname}:${legado_webSocket_port}`;
|
||||||
|
|
||||||
|
console.info("legado_server_config:");
|
||||||
|
console.table({legado_http_origin, legado_webSocket_origin});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 手动初始化 阅读web服务地址
|
||||||
|
setLeagdoHttpUrl(ajax.defaults.baseURL);
|
||||||
|
|
||||||
|
const testLeagdoHttpUrlConnection = async (http_url) => {
|
||||||
|
const {data = {}} = await ajax.get("/getReadConfig", {
|
||||||
|
baseURL: http_url,
|
||||||
|
timeout: 3000
|
||||||
|
})
|
||||||
|
// 返回结果应该是JSON 并有键值isSuccess
|
||||||
|
try {
|
||||||
|
if ("isSuccess" in data) return
|
||||||
|
throw new Error("ReadConfig后端返回格式错误" )
|
||||||
|
} catch {
|
||||||
|
throw new Error("ReadConfig后端返回格式错误" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const isSourecEditor = /source/i.test(location.href);
|
const isSourecEditor = /source/i.test(location.href);
|
||||||
const APIExceptionHandler = (error) => {
|
const APIExceptionHandler = (error) => {
|
||||||
if (isSourecEditor) {
|
if (isSourecEditor) {
|
||||||
@@ -19,8 +53,10 @@ const APIExceptionHandler = (error) => {
|
|||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
};
|
};
|
||||||
|
|
||||||
ajax.interceptors.response.use((response) => response, APIExceptionHandler);
|
ajax.interceptors.response.use((response) => response, APIExceptionHandler);
|
||||||
|
|
||||||
|
// 书架API
|
||||||
// Http
|
// Http
|
||||||
const getReadConfig = () => ajax.get("/getReadConfig");
|
const getReadConfig = () => ajax.get("/getReadConfig");
|
||||||
const saveReadConfig = (config) => ajax.post("/saveReadConfig", config);
|
const saveReadConfig = (config) => ajax.post("/saveReadConfig", config);
|
||||||
@@ -32,8 +68,8 @@ const saveBookProgressWithBeacon = (bookProgress) => {
|
|||||||
if (!bookProgress) return;
|
if (!bookProgress) return;
|
||||||
// 常规请求可能会被取消 使用Fetch keep-alive 或者 navigator.sendBeacon
|
// 常规请求可能会被取消 使用Fetch keep-alive 或者 navigator.sendBeacon
|
||||||
navigator.sendBeacon(
|
navigator.sendBeacon(
|
||||||
`${baseUrl()}/saveBookProgress`,
|
`${legado_http_origin}/saveBookProgress`,
|
||||||
JSON.stringify(bookProgress),
|
JSON.stringify(bookProgress)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -44,22 +80,23 @@ const getChapterList = (/** @type {string} */ bookUrl) =>
|
|||||||
|
|
||||||
const getBookContent = (
|
const getBookContent = (
|
||||||
/** @type {string} */ bookUrl,
|
/** @type {string} */ bookUrl,
|
||||||
/** @type {number} */ chapterIndex,
|
/** @type {number} */ chapterIndex
|
||||||
) =>
|
) =>
|
||||||
ajax.get(
|
ajax.get(
|
||||||
"/getBookContent?url=" +
|
"/getBookContent?url=" +
|
||||||
encodeURIComponent(bookUrl) +
|
encodeURIComponent(bookUrl) +
|
||||||
"&index=" +
|
"&index=" +
|
||||||
chapterIndex,
|
chapterIndex
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// webSocket
|
||||||
const search = (
|
const search = (
|
||||||
/** @type {string} */ searchKey,
|
/** @type {string} */ searchKey,
|
||||||
/** @type {(data: string) => void} */ onReceive,
|
/** @type {(data: string) => void} */ onReceive,
|
||||||
/** @type {() => void} */ onFinish,
|
/** @type {() => void} */ onFinish
|
||||||
) => {
|
) => {
|
||||||
// webSocket
|
|
||||||
const url = `ws://${getUrl()}/searchBook`;
|
const url = `${legado_webSocket_origin}/searchBook`;
|
||||||
const socket = new WebSocket(url);
|
const socket = new WebSocket(url);
|
||||||
|
|
||||||
socket.onopen = () => {
|
socket.onopen = () => {
|
||||||
@@ -77,6 +114,7 @@ const deleteBook = (book) => ajax.post("/deleteBook", book);
|
|||||||
|
|
||||||
const isBookSource = /bookSource/i.test(location.href);
|
const isBookSource = /bookSource/i.test(location.href);
|
||||||
|
|
||||||
|
// 源编辑API
|
||||||
// Http
|
// Http
|
||||||
const getSources = () =>
|
const getSources = () =>
|
||||||
isBookSource ? ajax.get("/getBookSources") : ajax.get("/getRssSources");
|
isBookSource ? ajax.get("/getBookSources") : ajax.get("/getRssSources");
|
||||||
@@ -96,14 +134,15 @@ const deleteSource = (data) =>
|
|||||||
? ajax.post("/deleteBookSources", data)
|
? ajax.post("/deleteBookSources", data)
|
||||||
: ajax.post("/deleteRssSources", data);
|
: ajax.post("/deleteRssSources", data);
|
||||||
|
|
||||||
|
// webSocket
|
||||||
const debug = (
|
const debug = (
|
||||||
/** @type {string} */ sourceUrl,
|
/** @type {string} */ sourceUrl,
|
||||||
/** @type {string} */ searchKey,
|
/** @type {string} */ searchKey,
|
||||||
/** @type {(data: string) => void} */ onReceive,
|
/** @type {(data: string) => void} */ onReceive,
|
||||||
/** @type {() => void} */ onFinish,
|
/** @type {() => void} */ onFinish
|
||||||
) => {
|
) => {
|
||||||
// webSocket
|
|
||||||
const url = `ws://${getUrl()}/${
|
const url = `${legado_webSocket_origin}/${
|
||||||
isBookSource ? "bookSource" : "rssSource"
|
isBookSource ? "bookSource" : "rssSource"
|
||||||
}Debug`;
|
}Debug`;
|
||||||
|
|
||||||
@@ -123,6 +162,32 @@ const debug = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从阅读获取需要特定处理的书籍封面
|
||||||
|
* @param {string} coverUrl
|
||||||
|
*/
|
||||||
|
const getProxyCoverUrl = (coverUrl) => {
|
||||||
|
if(coverUrl.startsWith(legado_http_origin)) return coverUrl
|
||||||
|
return legado_http_origin + "/cover?path=" + encodeURIComponent(coverUrl);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 从阅读获取需要特定处理的图片
|
||||||
|
* @param {string} src
|
||||||
|
* @param {`{number}`} width
|
||||||
|
*/
|
||||||
|
const getProxyImageUrl = (src, width) => {
|
||||||
|
if (src.startsWith(legado_http_origin)) return src
|
||||||
|
return (
|
||||||
|
legado_http_origin +
|
||||||
|
"/image?path=" +
|
||||||
|
encodeURIComponent(src) +
|
||||||
|
"&url=" +
|
||||||
|
encodeURIComponent(sessionStorage.getItem("bookUrl")) +
|
||||||
|
"&width=" +
|
||||||
|
width
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getReadConfig,
|
getReadConfig,
|
||||||
saveReadConfig,
|
saveReadConfig,
|
||||||
@@ -140,4 +205,11 @@ export default {
|
|||||||
saveSource,
|
saveSource,
|
||||||
deleteSource,
|
deleteSource,
|
||||||
debug,
|
debug,
|
||||||
|
|
||||||
|
getProxyCoverUrl,
|
||||||
|
getProxyImageUrl,
|
||||||
|
|
||||||
|
testLeagdoHttpUrlConnection,
|
||||||
|
setLeagdoHttpUrl,
|
||||||
|
legado_http_origin,
|
||||||
};
|
};
|
||||||
|
|||||||
Vendored
+2
-4
@@ -1,12 +1,12 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
/* prettier-ignore */
|
/* prettier-ignore */
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
// noinspection JSUnusedGlobalSymbols
|
|
||||||
// Generated by unplugin-auto-import
|
// Generated by unplugin-auto-import
|
||||||
export {}
|
export {}
|
||||||
declare global {
|
declare global {
|
||||||
const EffectScope: typeof import('vue')['EffectScope']
|
const EffectScope: typeof import('vue')['EffectScope']
|
||||||
const ElMessage: typeof import('element-plus/es')['ElMessage']
|
const ElMessage: typeof import('element-plus/es')['ElMessage']
|
||||||
|
const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
|
||||||
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
||||||
const computed: typeof import('vue')['computed']
|
const computed: typeof import('vue')['computed']
|
||||||
const createApp: typeof import('vue')['createApp']
|
const createApp: typeof import('vue')['createApp']
|
||||||
@@ -62,7 +62,6 @@ declare global {
|
|||||||
const toRaw: typeof import('vue')['toRaw']
|
const toRaw: typeof import('vue')['toRaw']
|
||||||
const toRef: typeof import('vue')['toRef']
|
const toRef: typeof import('vue')['toRef']
|
||||||
const toRefs: typeof import('vue')['toRefs']
|
const toRefs: typeof import('vue')['toRefs']
|
||||||
const toValue: typeof import('vue')['toValue']
|
|
||||||
const triggerRef: typeof import('vue')['triggerRef']
|
const triggerRef: typeof import('vue')['triggerRef']
|
||||||
const unref: typeof import('vue')['unref']
|
const unref: typeof import('vue')['unref']
|
||||||
const useAttrs: typeof import('vue')['useAttrs']
|
const useAttrs: typeof import('vue')['useAttrs']
|
||||||
@@ -82,6 +81,5 @@ declare global {
|
|||||||
// for type re-export
|
// for type re-export
|
||||||
declare global {
|
declare global {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
|
export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue'
|
||||||
import('vue')
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
class="cover"
|
class="cover"
|
||||||
:src="getCover(book.coverUrl)"
|
:src="getCover(book.coverUrl)"
|
||||||
:key="book.coverUrl"
|
:key="book.coverUrl"
|
||||||
|
@error.once="proxyImage"
|
||||||
alt=""
|
alt=""
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
@@ -49,16 +50,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { dateFormat } from "../utils/utils";
|
import { dateFormat, isLegadoUrl } from "../utils/utils";
|
||||||
import { baseUrl } from "@/api/axios.js";
|
import API from "@api";
|
||||||
|
|
||||||
const props = defineProps(["books", "isSearch"]);
|
const props = defineProps(["books", "isSearch"]);
|
||||||
const emit = defineEmits(["bookClick"]);
|
const emit = defineEmits(["bookClick"]);
|
||||||
const handleClick = (book) => emit("bookClick", book);
|
const handleClick = (book) => emit("bookClick", book);
|
||||||
const getCover = (coverUrl) => {
|
const getCover = (coverUrl) => {
|
||||||
return /^data:/.test(coverUrl)
|
return isLegadoUrl(coverUrl)
|
||||||
? coverUrl
|
? API.getProxyCoverUrl(coverUrl) : coverUrl
|
||||||
: baseUrl() + "/cover?path=" + encodeURIComponent(coverUrl);
|
|
||||||
};
|
};
|
||||||
|
const proxyImage = (event) => {
|
||||||
|
event.target.src = API.getProxyCoverUrl(event.target.src);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const subJustify = computed(() =>
|
const subJustify = computed(() =>
|
||||||
props.isSearch ? "space-between" : "flex-start",
|
props.isSearch ? "space-between" : "flex-start",
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { getImageFromLegado, isLegadoUrl } from "@/utils/utils";
|
import { isLegadoUrl } from "@/utils/utils";
|
||||||
|
import API from "@api";
|
||||||
import jump from "@/plugins/jump";
|
import jump from "@/plugins/jump";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -33,11 +34,11 @@ const props = defineProps({
|
|||||||
const getImageSrc = (content) => {
|
const getImageSrc = (content) => {
|
||||||
const imgPattern = /<img[^>]*src="([^"]*(?:"[^>]+\})?)"[^>]*>/;
|
const imgPattern = /<img[^>]*src="([^"]*(?:"[^>]+\})?)"[^>]*>/;
|
||||||
const src = content.match(imgPattern)[1];
|
const src = content.match(imgPattern)[1];
|
||||||
if (isLegadoUrl(src)) return getImageFromLegado(src);
|
if (isLegadoUrl(src)) return API.getProxyImageUrl(src, useBookStore().config.readWidth)
|
||||||
return src;
|
return src;
|
||||||
};
|
};
|
||||||
const proxyImage = (event) => {
|
const proxyImage = (event) => {
|
||||||
event.target.src = getImageFromLegado(event.target.src);
|
event.target.src = API.getProxyImageUrl(event.target.src, useBookStore().config.readWidth);
|
||||||
};
|
};
|
||||||
|
|
||||||
const calculateWordCount = (paragraph) => {
|
const calculateWordCount = (paragraph) => {
|
||||||
|
|||||||
@@ -4,5 +4,3 @@ import router from "@/router";
|
|||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
createApp(App).use(store).use(router).mount("#app");
|
createApp(App).use(store).use(router).mount("#app");
|
||||||
|
|
||||||
import("./pages/bookshelf/config");
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import API from "@api";
|
|
||||||
import { useBookStore } from "@/store";
|
|
||||||
import "@/assets/bookshelf.css";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载配置
|
|
||||||
*/
|
|
||||||
API.getReadConfig().then((res) => {
|
|
||||||
var data = res.data.data;
|
|
||||||
if (data) {
|
|
||||||
const bookStore = useBookStore();
|
|
||||||
let config = JSON.parse(data);
|
|
||||||
let defaultConfig = bookStore.config;
|
|
||||||
config = Object.assign(defaultConfig, config);
|
|
||||||
bookStore.setConfig(config);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -4,5 +4,3 @@ import bookRouter from "@/router";
|
|||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
createApp(App).use(store).use(bookRouter).mount("#app");
|
createApp(App).use(store).use(bookRouter).mount("#app");
|
||||||
|
|
||||||
import("./config");
|
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ export const useBookStore = defineStore("book", {
|
|||||||
durChapterTitle: title,
|
durChapterTitle: title,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
theme: (state) => {
|
||||||
|
return state.config.theme
|
||||||
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setConnectStatus(connectStatus) {
|
setConnectStatus(connectStatus) {
|
||||||
@@ -57,11 +60,6 @@ export const useBookStore = defineStore("book", {
|
|||||||
setConnectType(connectType) {
|
setConnectType(connectType) {
|
||||||
this.connectType = connectType;
|
this.connectType = connectType;
|
||||||
},
|
},
|
||||||
resetConnect() {
|
|
||||||
this.connectStatus = "正在连接后端服务器……";
|
|
||||||
this.connectType = "";
|
|
||||||
this.clearBooks();
|
|
||||||
},
|
|
||||||
setNewConnect(newConnect) {
|
setNewConnect(newConnect) {
|
||||||
this.newConnect = newConnect;
|
this.newConnect = newConnect;
|
||||||
},
|
},
|
||||||
@@ -84,7 +82,7 @@ export const useBookStore = defineStore("book", {
|
|||||||
this.readingBook = readingBook;
|
this.readingBook = readingBook;
|
||||||
},
|
},
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
Object.assign(this.config, config);
|
this.config = Object.assign({}, this.config, config);
|
||||||
},
|
},
|
||||||
setReadSettingsVisible(visible) {
|
setReadSettingsVisible(visible) {
|
||||||
this.readSettingsVisible = visible;
|
this.readSettingsVisible = visible;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { formatDate } from "@vueuse/shared";
|
import { formatDate } from "@vueuse/shared";
|
||||||
import { baseUrl } from "@/api/axios.js";
|
|
||||||
|
|
||||||
export const isLegadoUrl = (/** @type {string} */ url) =>
|
export const isLegadoUrl = (/** @type {string} */ url) =>
|
||||||
/,\s*\{/.test(url) ||
|
/,\s*\{/.test(url) ||
|
||||||
@@ -8,20 +7,7 @@ export const isLegadoUrl = (/** @type {string} */ url) =>
|
|||||||
url.startsWith("data:") ||
|
url.startsWith("data:") ||
|
||||||
url.startsWith("blob:")
|
url.startsWith("blob:")
|
||||||
);
|
);
|
||||||
/**
|
|
||||||
* @param {string} src
|
|
||||||
*/
|
|
||||||
export function getImageFromLegado(src) {
|
|
||||||
return (
|
|
||||||
baseUrl() +
|
|
||||||
"/image?path=" +
|
|
||||||
encodeURIComponent(src) +
|
|
||||||
"&url=" +
|
|
||||||
encodeURIComponent(sessionStorage.getItem("bookUrl")) +
|
|
||||||
"&width=" +
|
|
||||||
useBookStore().config.readWidth
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export const dateFormat = (/** @type {number} */ t) => {
|
export const dateFormat = (/** @type {number} */ t) => {
|
||||||
let time = new Date().getTime();
|
let time = new Date().getTime();
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
readingRecent.name,
|
readingRecent.name,
|
||||||
readingRecent.author,
|
readingRecent.author,
|
||||||
readingRecent.chapterIndex,
|
readingRecent.chapterIndex,
|
||||||
readingRecent.chapterPos,
|
readingRecent.chapterPos
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
:class="{ 'no-point': readingRecent.url == '' }"
|
:class="{ 'no-point': readingRecent.url == '' }"
|
||||||
@@ -40,65 +40,52 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="setting-wrapper">
|
<div class="setting-wrapper">
|
||||||
<div class="setting-title">基本设定</div>
|
<div class="setting-title">基本设定</div>
|
||||||
<div class="setting-ip">
|
<div class="setting-item">
|
||||||
<el-input
|
<el-tag
|
||||||
class="setting-input"
|
:type="connectType"
|
||||||
size="small"
|
size="large"
|
||||||
:disabled="ipInput.disable"
|
class="setting-connect"
|
||||||
v-model="ipInput.ip"
|
:class="{ 'no-point': newConnect }"
|
||||||
@keydown.enter="setIP"
|
@click="setIP"
|
||||||
/>
|
>
|
||||||
<el-tag
|
{{ connectStatus }}
|
||||||
type="primary"
|
</el-tag>
|
||||||
class="setting-toggle"
|
</div>
|
||||||
@click="toggleIpConfig"
|
|
||||||
>
|
|
||||||
{{ ipInput.disable ? "修改" : "取消" }}
|
|
||||||
</el-tag>
|
|
||||||
</div>
|
|
||||||
<div class="setting-item">
|
|
||||||
<el-tag :type="connectType" size="large" class="setting-connect">
|
|
||||||
{{ connectStatus }}
|
|
||||||
</el-tag>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="bottom-icons">
|
||||||
|
<a
|
||||||
|
href="https://github.com/gedoor/legado_web_bookshelf"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<div class="bottom-icon">
|
||||||
|
<img :src="githubUrl" alt="" />
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bottom-icons">
|
<div class="shelf-wrapper" ref="shelfWrapper">
|
||||||
<a
|
<book-items
|
||||||
href="https://github.com/gedoor/legado_web_bookshelf"
|
:books="books"
|
||||||
target="_blank"
|
@bookClick="handleBookClick"
|
||||||
>
|
:isSearch="isSearching"
|
||||||
<div class="bottom-icon">
|
></book-items>
|
||||||
<img :src="githubUrl" alt="" />
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="shelf-wrapper" ref="shelfWrapper">
|
|
||||||
<book-items
|
|
||||||
:books="books"
|
|
||||||
@bookClick="handleBookClick"
|
|
||||||
:isSearch="isSearching"
|
|
||||||
></book-items>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import "@/assets/bookshelf.css";
|
||||||
import "@/assets/fonts/shelffont.css";
|
import "@/assets/fonts/shelffont.css";
|
||||||
import { useBookStore } from "@/store";
|
import { useBookStore } from "@/store";
|
||||||
import githubUrl from "@/assets/imgs/github.png";
|
import githubUrl from "@/assets/imgs/github.png";
|
||||||
import { useLoading } from "@/hooks/loading";
|
import { useLoading } from "@/hooks/loading";
|
||||||
import { Search } from "@element-plus/icons-vue";
|
import { Search } from "@element-plus/icons-vue";
|
||||||
import API from "@api";
|
import API from "@api";
|
||||||
import { baseUrl, setRemoteIp } from "@/api/axios.js";
|
|
||||||
|
|
||||||
const store = useBookStore();
|
const store = useBookStore();
|
||||||
const { connectStatus, connectType, newConnect, shelf } = storeToRefs(store);
|
const { connectStatus, connectType, newConnect, shelf, theme } = storeToRefs(store);
|
||||||
|
|
||||||
const theme = computed(() => {
|
|
||||||
return store.config.theme;
|
|
||||||
});
|
|
||||||
const isNight = computed(() => theme.value == 6);
|
const isNight = computed(() => theme.value == 6);
|
||||||
|
|
||||||
const readingRecent = ref({
|
const readingRecent = ref({
|
||||||
@@ -111,7 +98,7 @@ const readingRecent = ref({
|
|||||||
const shelfWrapper = ref(null);
|
const shelfWrapper = ref(null);
|
||||||
const { showLoading, closeLoading, loadingWrapper, isLoading } = useLoading(
|
const { showLoading, closeLoading, loadingWrapper, isLoading } = useLoading(
|
||||||
shelfWrapper,
|
shelfWrapper,
|
||||||
"正在获取书籍信息",
|
"正在获取书籍信息"
|
||||||
);
|
);
|
||||||
|
|
||||||
const books = shallowRef([]);
|
const books = shallowRef([]);
|
||||||
@@ -159,22 +146,59 @@ const searchBook = () => {
|
|||||||
if (books.value.length == 0) {
|
if (books.value.length == 0) {
|
||||||
ElMessage.info("搜索结果为空");
|
ElMessage.info("搜索结果为空");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ipInput = reactive({
|
|
||||||
ip: baseUrl(),
|
|
||||||
disable: true,
|
|
||||||
});
|
|
||||||
const toggleIpConfig = () => {
|
|
||||||
ipInput.ip = baseUrl();
|
|
||||||
ipInput.disable = !ipInput.disable;
|
|
||||||
};
|
|
||||||
const setIP = () => {
|
const setIP = () => {
|
||||||
setRemoteIp(ipInput.ip);
|
ElMessageBox.prompt(
|
||||||
ipInput.disable = true;
|
"请输入 IP 和端口 ( 如:127.0.0.1:9527 或者通过内网穿透的地址)",
|
||||||
loadShelf();
|
"提示",
|
||||||
|
{
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
inputPattern:
|
||||||
|
/^[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?:([1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-6][0-5][0-5][0-3][0-5])$/,
|
||||||
|
inputErrorMessage: "url 形式不正确",
|
||||||
|
beforeClose: (action, instance, done) => {
|
||||||
|
if (action === "confirm") {
|
||||||
|
store.setNewConnect(true);
|
||||||
|
instance.confirmButtonLoading = true;
|
||||||
|
instance.confirmButtonText = "校验中……";
|
||||||
|
// instance.inputValue
|
||||||
|
API.testLeagdoHttpUrlConnection("http://" + instance.inputValue)
|
||||||
|
//API.getBookShelf()
|
||||||
|
.then(function (response) {
|
||||||
|
instance.confirmButtonLoading = false;
|
||||||
|
// that.$store.commit(
|
||||||
|
// "increaseBookNum",
|
||||||
|
// response.data.data.length
|
||||||
|
// );
|
||||||
|
//store.addBooks(response.data.data);
|
||||||
|
const ip = instance.inputValue;
|
||||||
|
store.setConnectType("success");
|
||||||
|
store.setConnectStatus("已连接 " + ip);
|
||||||
|
store.clearSearchBooks();
|
||||||
|
store.setNewConnect(false);
|
||||||
|
API.setLeagdoHttpUrl("http://" + ip);
|
||||||
|
//持久化
|
||||||
|
localStorage.setItem("remoteIp", ip);
|
||||||
|
fetchBookShelfData()
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
instance.confirmButtonLoading = false;
|
||||||
|
instance.confirmButtonText = "确定";
|
||||||
|
ElMessage.error("访问失败,请检查您输入的 url");
|
||||||
|
store.setNewConnect(false);
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -221,16 +245,24 @@ onMounted(() => {
|
|||||||
readingRecent.value.chapterIndex = 0;
|
readingRecent.value.chapterIndex = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
loadShelf();
|
API.testLeagdoHttpUrlConnection()
|
||||||
|
.then(loadReadConfig)
|
||||||
|
.then(loadShelf)
|
||||||
|
.catch(function (error) {
|
||||||
|
store.setConnectType("danger");
|
||||||
|
store.setConnectStatus("连接异常");
|
||||||
|
ElMessage.error("后端连接失败异常,请检查阅读WEB服务或者设置其它可用IP")
|
||||||
|
store.setNewConnect(false);
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const loadShelf = () => {
|
const loadShelf = () => {
|
||||||
store.resetConnect();
|
|
||||||
loadingWrapper(
|
loadingWrapper(
|
||||||
store
|
store
|
||||||
.saveBookProgress()
|
.saveBookProgress()
|
||||||
//确保各种网络情况下同步请求先完成
|
//确保各种网络情况下同步请求先完成
|
||||||
.finally(fetchBookShelfData),
|
.finally(fetchBookShelfData)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -245,22 +277,29 @@ const fetchBookShelfData = () => {
|
|||||||
var x = a["durChapterTime"] || 0;
|
var x = a["durChapterTime"] || 0;
|
||||||
var y = b["durChapterTime"] || 0;
|
var y = b["durChapterTime"] || 0;
|
||||||
return y - x;
|
return y - x;
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error(response.data.errorMsg);
|
ElMessage.error(response.data.errorMsg ?? "后端返回格式错误!");
|
||||||
}
|
}
|
||||||
store.setConnectStatus("已连接 ");
|
store.setConnectStatus("已连接 " + API.legado_http_origin);
|
||||||
store.setNewConnect(false);
|
store.setNewConnect(false);
|
||||||
})
|
})
|
||||||
.catch(function (error) {
|
|
||||||
store.setConnectType("danger");
|
|
||||||
store.setConnectStatus("连接失败");
|
|
||||||
ElMessage.error("后端连接失败");
|
|
||||||
store.setNewConnect(false);
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载阅读配置
|
||||||
|
*/
|
||||||
|
const loadReadConfig = () => {
|
||||||
|
return API.getReadConfig().then((res) => {
|
||||||
|
var data = res.data.data;
|
||||||
|
if (data) {
|
||||||
|
let config = JSON.parse(data);
|
||||||
|
store.setConfig(config);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -340,31 +379,15 @@ const fetchBookShelfData = () => {
|
|||||||
font-family: FZZCYSK;
|
font-family: FZZCYSK;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-ip {
|
|
||||||
margin-top: 16px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-input {
|
|
||||||
width: 216px;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-point {
|
.no-point {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.setting-toggle {
|
|
||||||
font-size: 10px;
|
|
||||||
cursor: pointer;
|
|
||||||
//margin-top: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-connect {
|
.setting-connect {
|
||||||
font-size: 10px;
|
font-size: 8px;
|
||||||
margin-top: 4px;
|
margin-top: 16px;
|
||||||
// color: #6B7C87;
|
// color: #6B7C87;
|
||||||
//cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user