重构 store 并修复多个 bug

- state.collectGallery.slice(0) → splice(0),修复收藏列表数据残留 bug
   - action 中 state 引用改为 context.state,遵循 Vuex 规范
   - 修复 _searchLocalByLink 中 gid 判空条件(null → undefined)
   - 修复 deleteTask splice 后索引跳过下一个元素
   - 简化 getShortname 用正则替代繁琐的手动遍历
   - 移除 loadWeekUsedAmount 冗余的成功提示弹窗
   - 添加 Axios 全局错误拦截器,统一处理网络请求失败
   - websocket 初始值 {} → null,修正类型
   - resetUndone 硬编码授权码改为从 state 获取
   - computed 移除副作用,改用 watch 实现
   - HentaiSearch 加载指示器 DOM 操作改为响应式 v-show
   - 提取重复的 validateLink 到共享工具函数
   - OnlineReader 全量 watch(store.state) → 精准监听 isReading
This commit is contained in:
chuzhongzai
2026-06-07 16:18:44 +08:00
parent e19ae3d802
commit e25afb7445
8 changed files with 273 additions and 90 deletions
+8 -13
View File
@@ -140,9 +140,10 @@
<script setup>
import store from "../store";
import {computed, ref, onMounted} from "vue";
import {computed, ref, onMounted, watch} from "vue";
import {ElMessage} from "element-plus"
import HentaiSearch from "./HentaiSearch.vue";
import {validateLink} from "../utils/validate.js";
//授权码相关
let AuthCode = ref("")
@@ -172,8 +173,6 @@ let realAuthCode = computed(() => {
})
let chosenGallery = computed(() => {
param.value = ''
targetResolution.value = ''
return store.state.chosenGallery
})
@@ -192,6 +191,12 @@ let isLion = computed(() => {
return store.state.userId === 3
})
// 查询弹窗打开时重置表单
watch(chosenGallery, () => {
param.value = ''
targetResolution.value = ''
})
//重连节点
function reconnect(){
store.dispatch("reconnect")
@@ -269,16 +274,6 @@ function validate(){
}
}
//验证链接
function validateLink(rawLink){
if(rawLink.trim() === "")
return false
if(rawLink.includes("hentai"))
return rawLink.includes("/g/")
else
return false
}
//在线阅读以及展示缩略图
function readOnlineGallery(gallery){
store.dispatch("readOnlineGallery", gallery)
+7 -14
View File
@@ -4,6 +4,7 @@ import {ref, watch} from "vue";
import axios from "axios";
import {ElMessage} from "element-plus";
import store from "../store/index.js";
import {validateLink} from "../utils/validate.js";
let props = defineProps(['isQuerying'])
let emit = defineEmits(['close'])
@@ -13,6 +14,7 @@ let queryPage = ref({})
let galleries = ref([])
let param = ref()
let isShowUp = ref()
let isLoading = ref(false)
watch(props, () => {
isShowUp.value = props.isQuerying
@@ -27,11 +29,10 @@ function queryGalleries(link) {
tempParam = keyword.value
}
tempParam = tempParam.replace(" ", "+")
document.getElementById("loading").style.display = "inline-block";
isLoading.value = true
axios.get("https://downloader.lionwebsite.xyz/query?keyword=" + tempParam)
.then((res) => {
document.getElementById("loading").style.display = "none";
if (res.data.result === "success") {
let tempGalleries = JSON.parse(res.data.data)
queryPage.value.first = 'first' in res.data ? res.data.first : undefined
@@ -48,6 +49,8 @@ function queryGalleries(link) {
} else {
ElMessage({message: res.data.data, type: "error"})
}
}).finally(() => {
isLoading.value = false
})
}
@@ -59,15 +62,6 @@ function queryRemoteTask(){
store.dispatch("queryGalleryTask", param.value)
}
function validateLink(rawLink){
if(rawLink.trim() === "")
return false
if(rawLink.includes("hentai"))
return rawLink.includes("/g/")
else
return false
}
function close(){
emit("close")
}
@@ -76,7 +70,7 @@ function close(){
<template>
<el-dialog title="在线搜索" v-model="isShowUp" top="0" style="margin-bottom: 0" @close="close">
<div style="text-align: center">
<el-input v-model="keyword"></el-input> <el-button @click="queryGalleries(null)">查询</el-button> <div id="loading"/>
<el-input v-model="keyword"></el-input> <el-button @click="queryGalleries(null)">查询</el-button> <div class="loading" v-show="isLoading"/>
</div>
<el-scrollbar height="75vh" ref="scrollBar">
<div style="height: 251px; width: 100%; " v-for="gallery in galleries">
@@ -113,14 +107,13 @@ function close(){
width: 200px;
}
#loading {
.loading {
width: 25px;
height: 25px;
border: 2px solid #ccc;
border-top-color: #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
display: none;
}
@keyframes spin {
+2 -2
View File
@@ -18,8 +18,8 @@ let lengthPerPage = computed(() => {
let readingGallery = computed(() => {
return store.state.readingGallery
})
watch((store.state), (value) => {
if(value.isReading && !isReading.value) {
watch(() => store.state.isReading, (isReadingVal) => {
if(isReadingVal && !isReading.value) {
alterPage()
isReading.value = true
}
+10 -4
View File
@@ -96,7 +96,7 @@
<script setup>
import store from "../store";
import {computed, ref} from "vue";
import {computed, ref, watch} from "vue";
import OnlineReader from "./OnlineReader.vue";
//输入
@@ -133,18 +133,24 @@ let min = computed(() => {
return store.getters.min
})
let max = computed(() => {
if(targetPage.value > store.getters.max)
store.commit("_changePage", store.getters.max)
return store.getters.max
})
let page = computed(() => {
targetPage.value = store.state.page
return store.state.page
})
let isLion = computed(() => {
return store.state.userId === 3
})
// 同步 store.page → local targetPage,并在 page 超出 max 时修正
watch(page, (newPage) => {
targetPage.value = newPage
})
watch(max, (newMax) => {
if(targetPage.value > newMax)
store.commit("_changePage", newMax)
})
let emptyText = computed(() => {
let action = category.value === 'myDownload' ? '下载': '收藏'
return '您未' + action + "过"
+37 -57
View File
@@ -5,11 +5,20 @@ import qs from "qs"
const BaseUrl = "https://downloader.lionwebsite.xyz/"
const GalleryManageUrl = BaseUrl + "GalleryManage"
// Axios 全局错误处理
axios.interceptors.response.use(
response => response,
error => {
ElMessage({message: "网络请求失败: " + (error.message || ""), type: "error"})
return Promise.reject(error)
}
)
const actions = {
reconnect(context) {
axios.post(GalleryManageUrl + "/reconnect", {
params: {
AuthCode: state.AuthCode
AuthCode: context.state.AuthCode
}
}).then(res => {
if (res.data.result === "success") {
@@ -22,7 +31,7 @@ const actions = {
updateGalleryTasks(context){
axios.get(GalleryManageUrl, {
params:{
AuthCode: state.AuthCode,
AuthCode: context.state.AuthCode,
type: 'all'
}
}).then((res) => {
@@ -32,7 +41,7 @@ const actions = {
},
postGalleryTask(context, data){
axios.post(GalleryManageUrl + '?' + qs.stringify({
AuthCode: state.AuthCode,
AuthCode: context.state.AuthCode,
...data
}, {indices:false})).then((res) => {
if(res.data.result === "success") {
@@ -52,7 +61,7 @@ const actions = {
params:{
param: link,
type: 'link',
AuthCode: state.AuthCode
AuthCode: context.state.AuthCode
}
}).then((res) => {
if(res.data.result === 'success')
@@ -78,12 +87,12 @@ const actions = {
})
},
initWebsocket(context){
state.websocket = new WebSocket("wss://downloader.lionwebsite.xyz/ws/")
state.websocket.onopen = () => {
state.websocket.send("DownloaderWebsocket")
context.state.websocket = new WebSocket("wss://downloader.lionwebsite.xyz/ws/")
context.state.websocket.onopen = () => {
context.state.websocket.send("DownloaderWebsocket")
}
state.websocket.onmessage = (event) => {
context.state.websocket.onmessage = (event) => {
let message = JSON.parse(event.data)
switch (message.type){
@@ -98,19 +107,18 @@ const actions = {
loadWeekUsedAmount(context){
axios.get(GalleryManageUrl + "/weekUsedAmount", {
params: {
AuthCode: state.AuthCode
AuthCode: context.state.AuthCode
}
}).then((res) => {
if(res.data.result === "success"){
context.state.weekUsed = JSON.parse(res.data.data)
ElMessage("查询用量成功")
}else
ElMessage("查询用量失败")
})
},
collectGallery(context, gid){
axios.post(GalleryManageUrl + "/collect?" +qs.stringify( {
gid, AuthCode:state.AuthCode
gid, AuthCode:context.state.AuthCode
})).then((res) => {
ElMessage(res.data.data)
if(res.data.result === 'success')
@@ -119,7 +127,7 @@ const actions = {
},
disCollectGallery(context, gid){
axios.post(GalleryManageUrl + "/disCollect?" + qs.stringify({
gid, AuthCode:state.AuthCode
gid, AuthCode:context.state.AuthCode
})).then((res) => {
ElMessage(res.data.data)
if(res.data.result === 'success')
@@ -129,7 +137,7 @@ const actions = {
deleteGallery(context, gid){
axios.delete(GalleryManageUrl, {
params:{
AuthCode:state.AuthCode, gid
AuthCode:context.state.AuthCode, gid
}}).then((res) => {
if(res.data.result === "success"){
ElMessage("删除成功")
@@ -154,11 +162,11 @@ const actions = {
})
},
alterAuthCode(context, AuthCode){
axios.put(BaseUrl + "AuthCode?" + qs.stringify({'AuthCode': state.AuthCode, 'newAuthCode': AuthCode}))
axios.put(BaseUrl + "AuthCode?" + qs.stringify({'AuthCode': context.state.AuthCode, 'newAuthCode': AuthCode}))
.then((res) => {
if(res.data.result === 'success') {
ElMessage("修改成功")
if(localStorage.getItem("auth") === state.AuthCode)
if(localStorage.getItem("auth") === context.state.AuthCode)
localStorage.setItem("auth", AuthCode)
context.state.AuthCode = AuthCode
}
@@ -166,8 +174,8 @@ const actions = {
ElMessage(res.data.data)
})
},
resetUndone(){
axios.post(GalleryManageUrl + "/reset?AuthCode=big+lion").then((res) => {
resetUndone(context){
axios.post(GalleryManageUrl + "/reset?AuthCode=" + context.state.AuthCode).then((res) => {
ElMessage(res.data.data)
})
}
@@ -195,7 +203,7 @@ const mutations = {
},
_updateGalleryTasks(state, tasks){
state.totalGalleryTask.splice(0)
state.collectGallery.slice(0)
state.collectGallery.splice(0)
state.downloadGallery.splice(0)
tasks.forEach((task) => {
@@ -281,21 +289,18 @@ const mutations = {
},
_searchLocalByLink(state, link){
let tasks = state.currentTasks
let i = 0
let gid = null
let gid = link.split("/")[4]
let name = null
gid = link.split("/")[4]
if(gid === null)
for (i = 0; i < tasks.length; i++) {
if(gid === undefined)
for (let i = 0; i < tasks.length; i++) {
if (tasks[i].link === link) {
state.page = Math.floor(i / state.length) + 1
name = tasks[i].name
break
}
}
else for (i = 0; i < tasks.length; i++)
else for (let i = 0; i < tasks.length; i++)
if (tasks[i].gid === gid) {
state.page = Math.floor(i / state.length) + 1
name = state.sortType === "shortName" ? tasks[i].shortName: tasks[i].name
@@ -382,7 +387,7 @@ const mutations = {
}
const state = {
websocket: {}, //websocket
websocket: null, //websocket
totalGalleryTask: [], //存放数据的数组
chosenGallery: false, //准备下载
@@ -459,37 +464,11 @@ function getShortname(name){
console.log(name)
return null
}
if (name.includes("[")) {
let lastIndex = name.lastIndexOf("[")
name = name.substring(0, lastIndex)
while (name.includes("[") && name.includes("]") && name.indexOf("[") < name.indexOf("]")) {
let start = name.indexOf("[")
let end = name.indexOf("]") + 1
let temp = name.substring(start, end)
temp = name.replace(temp, "")
if(temp.trim() === ""){
name = name.replace("[", "").replace("]", "")
break
}
else
name = temp
}
while (name.includes("(") && name.includes(")") && name.indexOf("(") < name.indexOf(")")) {
let start = name.indexOf("(")
let end = name.indexOf(")") + 1
let temp = name.substring(start, end)
temp = name.replace(temp, "")
if(temp.trim() === ""){
name = name.replace("(", "").replace(")", "")
break
}
else
name = temp
}
return name.trim()
} else {
return name
}
if (!name.includes("[")) return name
// 截取最后一个 [ 之前的部分,然后移除所有 [...] 和 (...) 标签
name = name.substring(0, name.lastIndexOf("["))
return name.replace(/\s*\[[^\]]*\]\s*/g, '').replace(/\s*\([^\)]*\)\s*/g, '').trim()
}
function confirmCurrentTask(state){
@@ -530,6 +509,7 @@ function deleteTask(tasks, key, value){
for(let i=0; i<tasks[j].length; i++)
if(tasks[j][i][key] === value){
tasks[j].splice(i, 1)
i--
break
}
}
+8
View File
@@ -0,0 +1,8 @@
export function validateLink(rawLink){
if(rawLink.trim() === "")
return false
if(rawLink.includes("hentai"))
return rawLink.includes("/g/")
else
return false
}