移动端支持下载人信息与筛选(仅管理员)

- 详情抽屉显示下载人昵称,取不到时回退为 #id
- 任务列表新增「下载人」筛选,选项从当前分类派生,切分类时清空
- 管理员判定改用后端下发的 isAdmin,不再硬编码 userId === 3
This commit is contained in:
2026-09-20 22:54:55 +08:00
parent abb1cdb952
commit 3a39677bb3
5 changed files with 68 additions and 17 deletions
+2 -2
View File
@@ -16,7 +16,7 @@ const galleryNameType = ref("shortName");
const realAuthCode = computed(() => store.state.AuthCode);
const weekUsed = computed(() => store.state.weekUsed);
const isLion = computed(() => store.state.userId === 3);
const isAdmin = computed(() => store.state.isAdmin);
const connectionText = computed(() => ({
connected: "已连接",
@@ -137,7 +137,7 @@ onMounted(() => {
<span class="setting-label">删除本地授权码</span>
<span class="chevron">›</span>
</button>
<button v-if="isLion" type="button" class="setting-row is-button is-danger"
<button v-if="isAdmin" type="button" class="setting-row is-button is-danger"
@click="store.dispatch('resetUndone')">
<span class="setting-label">重置未完成任务</span>
<span class="chevron">›</span>
+5 -4
View File
@@ -30,7 +30,8 @@ watch(detailGallery, (gallery) => {
}
});
const isLion = computed(() => store.state.userId === 3);
// Admin-only fields come from the backend's isAdmin flag, not a hardcoded id.
const isAdmin = computed(() => store.state.isAdmin);
const isDone = computed(() => (task.value ? task.value.status === "下载完成" : false));
const isCollect = computed(() => Boolean(task.value && task.value.isCollect));
@@ -139,9 +140,9 @@ function submitTask() {
<dd>{{ task.createTimeDisplay || "—" }}</dd>
<dt>原始页面</dt>
<dd><a v-if="task.link" :href="task.link" target="_blank" rel="noopener">打开链接</a><span v-else>—</span></dd>
<template v-if="isLion">
<dt>downloader</dt>
<dd>{{ task.downloader }}</dd>
<template v-if="isAdmin">
<dt>下载人</dt>
<dd>{{ task.downloaderName || `#${task.downloader}` }}</dd>
</template>
</dl>
+28
View File
@@ -29,6 +29,23 @@ const sortType = computed({
set: value => store.commit("_setSortType", value),
});
const isAdmin = computed(() => store.state.isAdmin);
const downloaderFilter = computed({
get: () => store.state.downloaderFilter,
set: value => store.commit("_setDownloaderFilter", value),
});
// Options come from the current category, so the dropdown can never offer a
// downloader that would filter the visible list down to nothing.
const downloaderOptions = computed(() => {
const seen = new Map();
;(store.state.currentTasks || []).forEach(task => {
if (task.downloaderName && !seen.has(task.downloader))
seen.set(task.downloader, task.downloaderName);
});
return [...seen].map(([id, name]) => ({id, name}));
});
const emptyText = computed(() => {
if (store.state.category === "myDownload") return "您未下载过";
if (store.state.category === "myCollect") return "您未收藏过";
@@ -97,6 +114,17 @@ async function refreshTasks() {
<el-option value="shortName" label="简洁名"/>
</el-select>
</label>
<label class="filter" v-if="isAdmin">
<span class="filter-label">下载人</span>
<el-select v-model="downloaderFilter" size="small"
class="filter-select filter-select-downloader"
clearable placeholder="全部">
<el-option v-for="item in downloaderOptions"
:key="item.id"
:value="item.id"
:label="item.name"/>
</el-select>
</label>
<button type="button" class="chip-button" :disabled="refreshing" @click="refreshTasks">
{{ refreshing ? "刷新中" : "刷新" }}
</button>
+29 -11
View File
@@ -325,6 +325,8 @@ const mutations = {
state.AuthCode = data.AuthCode
state.userId = data.userId
state.username = data.username
state.isAdmin = Boolean(data.isAdmin)
state.downloaderFilter = null
state.isAuth = true
ElMessage("验证成功,加载中")
},
@@ -402,6 +404,8 @@ const mutations = {
_setCategory(state, category){
state.category = category
state.visiblePages = 1
// 换分类后旧的下载人筛选可能在新分类里根本不存在,先清掉。
state.downloaderFilter = null
confirmCurrentTask(state)
sortTasks(state)
},
@@ -413,6 +417,12 @@ const mutations = {
_setGalleryNameType(state, galleryNameType){
state.galleryNameType = galleryNameType
},
_setDownloaderFilter(state, downloader){
state.downloaderFilter = downloader === null || downloader === undefined
? null
: Number(downloader)
state.visiblePages = 1
},
_setShowNameType(state, type){
if(type === "shortName")
state.length = state.shortLength
@@ -481,23 +491,23 @@ const state = {
sortType:'shortName', //排序类型 shortName name createTime
currentTasks: [], //当前任务
weekUsed: {}, //每周用量
isAdmin: false, //管理员(后端 validate 下发)
downloaderFilter: null, //管理员按下载人筛选,null 表示全部
}
const getters = {
currentTasks(state){
if(state.isSearch)
return state.searchTask.slice((state.page - 1) * state.length, state.page * state.length)
else
return state.currentTasks.slice((state.page - 1) * state.length, state.page * state.length)
const tasks = activeTaskList(state)
return tasks.slice((state.page - 1) * state.length, state.page * state.length)
},
min(){
return 1
},
visibleTasks(state){
return (state.currentTasks || []).slice(0, state.visiblePages * state.length)
return activeTaskList(state).slice(0, state.visiblePages * state.length)
},
taskTotal(state){
return (state.currentTasks || []).length
return activeTaskList(state).length
},
taskRemaining(state, getters){
return Math.max(0, getters.taskTotal - state.visiblePages * state.length)
@@ -507,11 +517,7 @@ const getters = {
},
max(state){
let max = 0
let tasks
if(state.isSearch)
tasks = state.searchTask
else
tasks = state.currentTasks
const tasks = activeTaskList(state)
if(!tasks)
return 1
@@ -531,6 +537,18 @@ export default new vuex.Store({
getters
})
/**
* 当前视图实际要展示的任务。
* 下载人筛选只在管理员生效,作用于「我的下载/收藏/全部」三种分类的结果。
*/
function activeTaskList(state){
if(state.isSearch)
return state.searchTask
if(state.isAdmin && state.downloaderFilter !== null)
return state.currentTasks.filter(task => task.downloader === state.downloaderFilter)
return state.currentTasks
}
function getShortname(name){
if(name === null)
return null
+4
View File
@@ -220,6 +220,10 @@ body {
width: 90px;
}
.filter-select-downloader {
width: 96px;
}
.chip-button {
flex: none;
height: 30px;