- 统一 theme.css 视觉变量与卡片、状态标签样式 - DashBoard 与 Side 的任务行重排:状态标签与进度对齐,排序/显示条件更明确 - 新增 AppIcon 图标组件,store 同步空名短标题修复
424 lines
14 KiB
Vue
424 lines
14 KiB
Vue
<template>
|
||
<!-- Auth Screen -->
|
||
<div v-show="!loadComplete" class="auth-container">
|
||
<div class="auth-card">
|
||
<div class="auth-brand">
|
||
<span class="auth-mark" aria-hidden="true">L</span>
|
||
<span class="auth-name">LionWebsite</span>
|
||
</div>
|
||
<h1>下载管理器</h1>
|
||
<p class="auth-subtitle">输入授权码以继续</p>
|
||
<div class="auth-form">
|
||
<el-input v-model="AuthCode"
|
||
placeholder="请输入授权码"
|
||
size="large"
|
||
show-password
|
||
@keydown.enter="validate" />
|
||
<el-checkbox v-model="isRemember">记住授权码</el-checkbox>
|
||
<el-button @click="validate" type="primary" size="large" class="auth-btn">验证</el-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Dashboard Main -->
|
||
<div v-show="loadComplete" class="dashboard">
|
||
<div class="control-card">
|
||
<div class="card-head">
|
||
<h3 class="section-title">本周用量</h3>
|
||
<el-button @click="queryWeekUsedAmount" size="small" text>刷新</el-button>
|
||
</div>
|
||
<div class="usage-line">
|
||
<span class="usage-value">{{ weekUsed.weekUsedAmount ?? '—' }}</span>
|
||
<span class="usage-note">上次重置 {{ weekUsed.lastResetAmountTime ?? '—' }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="dashboard-grid">
|
||
<div class="control-card">
|
||
<div class="card-head">
|
||
<h3 class="section-title">查询任务</h3>
|
||
</div>
|
||
<div class="card-row">
|
||
<el-select v-model="type" @change="resetLocalQuery" size="small" style="width: 92px">
|
||
<el-option value="link" label="链接"/>
|
||
<el-option value="keyword" label="关键字"/>
|
||
</el-select>
|
||
<el-input v-model="param"
|
||
size="small"
|
||
placeholder="输入链接或关键字"
|
||
style="flex: 1; min-width: 150px"
|
||
@keydown.enter="type === 'link' ? queryRemoteTask() : queryLocalTask()" />
|
||
<el-button @click="queryRemoteTask" v-show="type === 'link'" size="small" type="primary">远程查询</el-button>
|
||
<el-button @click="queryLocalTask" size="small">当前页查询</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="control-card">
|
||
<div class="card-head">
|
||
<h3 class="section-title">管理</h3>
|
||
</div>
|
||
<div class="card-row">
|
||
<el-button @click="isAlterAuthCode = true" size="small">修改授权码</el-button>
|
||
<el-button @click="deleteAuthCode" size="small">删除本地授权码</el-button>
|
||
<el-button @click="isConfig = true" size="small">配置</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="control-card">
|
||
<div class="card-head">
|
||
<h3 class="section-title">系统</h3>
|
||
</div>
|
||
<div class="card-row">
|
||
<el-button @click="isQuerying = true" size="small" type="primary">在线搜索</el-button>
|
||
<el-button @click="reconnect" size="small">重连节点</el-button>
|
||
<el-button v-if="isLion" @click="resetUndone" size="small" type="danger" plain>重置任务</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="control-card">
|
||
<div class="card-head">
|
||
<h3 class="section-title">外观</h3>
|
||
</div>
|
||
<div class="setting-row">
|
||
<span>夜间模式</span>
|
||
<el-switch v-model="isDark" @change="toggleStyle" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-show="thumbnailGallery.thumb_link !== undefined" class="control-card">
|
||
<div class="card-head">
|
||
<h3 class="section-title">缩略图</h3>
|
||
</div>
|
||
<div class="thumbnail-body">
|
||
<p class="gallery-name">{{ thumbnailGallery.shortName }}</p>
|
||
<div class="thumbnail-frame">
|
||
<el-image :src="thumbnailGallery.thumb_link"
|
||
:preview-src-list="[thumbnailGallery.thumb_link]"
|
||
style="width: 100%; height: 420px"
|
||
fit="contain" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<el-dialog title="任务详情" v-model="chosenGallery" width="560px">
|
||
<div class="detail-layout">
|
||
<el-image v-show="chosenGallery.thumb_link !== undefined"
|
||
class="detail-thumb"
|
||
fit="contain"
|
||
:src="chosenGallery.thumb_link !== undefined ? thumbnailUrl(chosenGallery.thumb_link) : ''" />
|
||
<dl class="detail-list">
|
||
<dt>名字</dt>
|
||
<dd>{{ chosenGallery.name }}</dd>
|
||
<dt>页数</dt>
|
||
<dd>{{ chosenGallery.pages }}</dd>
|
||
<dt>语言</dt>
|
||
<dd>{{ chosenGallery.language }}</dd>
|
||
<dt>大小</dt>
|
||
<dd>{{ chosenGallery.fileSize }}</dd>
|
||
<dt>状态</dt>
|
||
<dd>{{ chosenGallery.status }}</dd>
|
||
<template v-if="chosenGallery.availableResolution">
|
||
<dt>目标分辨率</dt>
|
||
<dd>
|
||
<el-select v-model="targetResolution" style="width: 220px">
|
||
<el-option v-for="(fileSize, resolution) in chosenGallery.availableResolution"
|
||
:key="resolution"
|
||
:value="resolution"
|
||
:label="resolution + ' ' + fileSize" />
|
||
</el-select>
|
||
</dd>
|
||
</template>
|
||
</dl>
|
||
</div>
|
||
<template #footer>
|
||
<el-button @click="postTask" v-if="chosenGallery.availableResolution" type="primary">提交下载</el-button>
|
||
<el-button @click="downloadChosenGallery" v-else-if="chosenGallery.status === '下载完成'" type="primary">下载文件</el-button>
|
||
<el-button @click="readOnlineGallery(chosenGallery)">在线预览</el-button>
|
||
<el-button v-if="chosenGallery.status === '下载完成'" type="danger" plain @click="deleteGallery">删除</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<el-dialog title="修改授权码" v-model="isAlterAuthCode">
|
||
<el-form label-position="top">
|
||
<el-form-item label="当前授权码">
|
||
<span class="mono-value">{{ realAuthCode }}</span>
|
||
</el-form-item>
|
||
<el-form-item label="新的授权码">
|
||
<el-input v-model="newAuthCode" show-password></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="再次输入授权码">
|
||
<el-input v-model="tempAuthCode" show-password></el-input>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="isAlterAuthCode = false">取消</el-button>
|
||
<el-button type="primary" @click="alterAuthCode">提交</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<HentaiSearch v-model:is-querying="isQuerying" @close="isQuerying = false"></HentaiSearch>
|
||
|
||
<el-dialog title="配置" v-model="isConfig">
|
||
<el-form label-position="top" class="config-form">
|
||
<h4 class="section-title">外观</h4>
|
||
<div class="setting-row">
|
||
<span>夜间模式跟随系统</span>
|
||
<el-switch v-model="darkConfig.followSystem"></el-switch>
|
||
</div>
|
||
|
||
<h4 class="section-title">在线预览</h4>
|
||
<el-form-item label="每页图片数量">
|
||
<el-input-number v-model="lengthPerPage" :min="1" :max="30" :step="1" style="width: 160px" />
|
||
<span class="field-hint">1 – 30</span>
|
||
</el-form-item>
|
||
|
||
<h4 class="section-title">默认设置</h4>
|
||
<el-form-item label="分类">
|
||
<el-select v-model="category" default-first-option style="width: 220px">
|
||
<el-option label="全部" value="total"/>
|
||
<el-option label="我的下载" value="myDownload"/>
|
||
<el-option label="我的收藏" value="myCollect"/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="排序方式">
|
||
<el-select v-model="sortType" default-first-option style="width: 220px">
|
||
<el-option label="名字" value="name"/>
|
||
<el-option label="简洁名字" value="shortName"/>
|
||
<el-option label="任务创建时间" value="createTime"/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="显示类型">
|
||
<el-select v-model="galleryNameType" default-first-option style="width: 220px">
|
||
<el-option label="名字" value="name"/>
|
||
<el-option label="简洁名字" value="shortName"/>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button type="primary" @click="saveConfig">保存</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
</template>
|
||
|
||
<script setup>
|
||
import store from "../store";
|
||
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("")
|
||
let isRemember = ref(false)
|
||
let isAlterAuthCode = ref(false)
|
||
let newAuthCode = ref("")
|
||
let tempAuthCode = ref("")
|
||
|
||
let isQuerying = ref(false)
|
||
let isConfig = ref(false)
|
||
let isDark = ref(false)
|
||
let keyword = ref("")
|
||
let darkConfig = ref({})
|
||
let lengthPerPage = ref(0)
|
||
let category = ref("")
|
||
let sortType = ref("")
|
||
let galleryNameType = ref("")
|
||
|
||
let type = ref("link")
|
||
let param = ref("")
|
||
|
||
let targetResolution = ref("")
|
||
|
||
let realAuthCode = computed(() => {
|
||
return store.state.AuthCode
|
||
})
|
||
|
||
let chosenGallery = computed(() => {
|
||
return store.state.chosenGallery
|
||
})
|
||
|
||
let loadComplete = computed(() => {
|
||
return store.state.loadComplete
|
||
})
|
||
let weekUsed = computed(() => {
|
||
return store.state.weekUsed
|
||
})
|
||
|
||
let thumbnailGallery = computed(() => {
|
||
return store.state.thumbnailGallery
|
||
})
|
||
|
||
let isLion = computed(() => {
|
||
return store.state.userId === 3
|
||
})
|
||
|
||
watch(chosenGallery, () => {
|
||
param.value = ''
|
||
targetResolution.value = ''
|
||
})
|
||
|
||
function reconnect(){
|
||
store.dispatch("reconnect")
|
||
}
|
||
|
||
function alterAuthCode(){
|
||
if(newAuthCode.value.trim() === "" || tempAuthCode.value.trim() === "" || newAuthCode.value !== tempAuthCode.value)
|
||
ElMessage("请检查授权码输入是否错误")
|
||
else {
|
||
store.dispatch("alterAuthCode", newAuthCode.value)
|
||
isAlterAuthCode.value = false
|
||
newAuthCode.value = ""
|
||
tempAuthCode.value = ""
|
||
}
|
||
}
|
||
|
||
function queryWeekUsedAmount(){
|
||
store.dispatch("loadWeekUsedAmount")
|
||
}
|
||
|
||
function postTask(){
|
||
if(!validateLink(chosenGallery.value.link)){
|
||
ElMessage("链接错误")
|
||
return
|
||
}
|
||
if(targetResolution.value === ''){
|
||
ElMessage("请选择分辨率再提交")
|
||
return
|
||
}
|
||
store.dispatch("postGalleryTask",
|
||
{link: chosenGallery.value.link,
|
||
targetResolution: targetResolution.value})
|
||
}
|
||
|
||
function queryRemoteTask(){
|
||
if(!validateLink(param.value)){
|
||
ElMessage("链接错误")
|
||
return
|
||
}
|
||
if(param.value.includes("e-hentai"))
|
||
param.value = param.value.replace("e-hentai", "exhentai")
|
||
store.dispatch("queryGalleryTask", param.value)
|
||
}
|
||
function queryLocalTask(){
|
||
switch (type.value){
|
||
case "link":
|
||
store.commit("_searchLocalByLink", param.value)
|
||
break
|
||
case "keyword":
|
||
store.commit("_searchLocalByKeyword", param.value)
|
||
break
|
||
}
|
||
}
|
||
|
||
function resetLocalQuery(){
|
||
store.commit("_searchLocalByKeyword", "")
|
||
param.value = ""
|
||
}
|
||
|
||
function deleteGallery(){
|
||
store.dispatch("deleteGallery", chosenGallery.value.gid)
|
||
}
|
||
|
||
function downloadChosenGallery(){
|
||
if(chosenGallery.value.download)
|
||
window.open(chosenGallery.value.download)
|
||
else
|
||
ElMessage({message: "下载地址不存在,请刷新任务后重试", type: "error"})
|
||
}
|
||
|
||
function validate(){
|
||
if(AuthCode.value.trim() === ""){
|
||
ElMessage("请输入授权码后再验证")
|
||
}
|
||
else{
|
||
store.dispatch("validate", AuthCode.value)
|
||
if(isRemember.value)
|
||
localStorage.setItem("auth", AuthCode.value)
|
||
}
|
||
}
|
||
|
||
function readOnlineGallery(gallery){
|
||
store.dispatch("readOnlineGallery", gallery)
|
||
}
|
||
|
||
function thumbnailUrl(path) {
|
||
return "https://downloader.lionwebsite.xyz/GalleryManage/ehThumbnail?" + new URLSearchParams({
|
||
path,
|
||
AuthCode: store.state.AuthCode
|
||
}).toString()
|
||
}
|
||
|
||
function resetUndone(){
|
||
store.dispatch("resetUndone").then()
|
||
}
|
||
function deleteAuthCode(){
|
||
localStorage.removeItem('auth')
|
||
ElMessage("删除授权码完成")
|
||
}
|
||
function toggleStyle(){
|
||
if(isDark.value)
|
||
document.documentElement.classList.add('dark')
|
||
else
|
||
document.documentElement.classList.remove('dark')
|
||
}
|
||
|
||
onMounted(() => {
|
||
const auth = localStorage.getItem("auth")
|
||
adjustForStyle()
|
||
store.state.lengthPerPage = localStorage.getItem("lengthPerPage")
|
||
store.state.lengthPerPage = store.state.lengthPerPage === null ? 30: Number(store.state.lengthPerPage)
|
||
category.value = store.state.category = localStorage.getItem("category") === null ? "myDownload" : localStorage.getItem("category")
|
||
sortType.value = store.state.sortType = localStorage.getItem("sortType") === null ? "createTime" : localStorage.getItem("sortType")
|
||
galleryNameType.value = store.state.galleryNameType = localStorage.getItem("galleryNameType") === null ? "shortName" : localStorage.getItem("galleryNameType")
|
||
lengthPerPage.value = store.state.lengthPerPage
|
||
|
||
if(auth !== null){
|
||
store.dispatch("validate", auth)
|
||
AuthCode.value = localStorage.getItem("auth")
|
||
}
|
||
})
|
||
|
||
function adjustForStyle(){
|
||
let darkConfigStr = localStorage.getItem("darkConfig")
|
||
if(darkConfigStr !== null) {
|
||
darkConfig.value = JSON.parse(darkConfigStr)
|
||
if (darkConfig.value.followSystem && isSystemDark())
|
||
document.documentElement.classList.add('dark')
|
||
else
|
||
document.documentElement.classList.remove('dark')
|
||
}else {
|
||
document.documentElement.classList.remove('dark')
|
||
darkConfig.value = {'followSystem': false}
|
||
}
|
||
isDark.value = document.documentElement.classList.contains('dark')
|
||
}
|
||
function isSystemDark(){
|
||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||
}
|
||
function saveConfig(){
|
||
if(lengthPerPage.value < 0 || lengthPerPage.value > 30) {
|
||
ElMessage("分页页数设置错误,范围1~30")
|
||
lengthPerPage.value = 30
|
||
}
|
||
else {
|
||
store.state.lengthPerPage = Number(lengthPerPage.value)
|
||
localStorage.setItem("lengthPerPage", lengthPerPage.value)
|
||
}
|
||
localStorage.setItem("darkConfig", JSON.stringify(darkConfig.value))
|
||
localStorage.setItem("category", category.value)
|
||
localStorage.setItem("sortType", sortType.value)
|
||
localStorage.setItem("galleryNameType", galleryNameType.value)
|
||
|
||
store.commit("_setCategory", category.value)
|
||
store.commit("_setSortType", sortType.value)
|
||
store.commit("_setGalleryNameType", galleryNameType.value)
|
||
|
||
isConfig.value = false
|
||
adjustForStyle()
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
</style>
|