update: 批量转存优化

This commit is contained in:
ctwj
2025-08-12 00:27:10 +08:00
parent 25c7c47c96
commit aa3b8585f9
10 changed files with 435 additions and 127 deletions

View File

@@ -16,6 +16,7 @@ type TaskItemRepository interface {
UpdateStatus(id uint, status string) error
UpdateStatusAndOutput(id uint, status, outputData string) error
GetStatsByTaskID(taskID uint) (map[string]int, error)
ResetProcessingItems(taskID uint) error
}
// TaskItemRepositoryImpl 任务项仓库实现
@@ -135,3 +136,10 @@ func (r *TaskItemRepositoryImpl) GetStatsByTaskID(taskID uint) (map[string]int,
return stats, nil
}
// ResetProcessingItems 重置处理中的任务项为pending状态
func (r *TaskItemRepositoryImpl) ResetProcessingItems(taskID uint) error {
return r.db.Model(&entity.TaskItem{}).
Where("task_id = ? AND status = ?", taskID, "processing").
Update("status", "pending").Error
}

View File

@@ -159,13 +159,8 @@ func GetSearchesTrend(c *gin.Context) {
Where("DATE(date) = ?", dateStr).
Count(&searches)
// 如果没有搜索记录,生成模拟数据
if searches == 0 {
// 基于当前时间的随机因子生成模拟搜索量
baseSearches := int64(50 + utils.GetCurrentTime().Day()*2) // 基础搜索量
randomFactor := float64(70+utils.GetCurrentTime().Hour()*i) / 100.0
searches = int64(float64(baseSearches) * randomFactor)
}
// 如果没有搜索记录,返回0
// 移除模拟数据生成逻辑,只返回真实数据
results = append(results, gin.H{
"date": dateStr,

View File

@@ -32,15 +32,17 @@ type BatchTransferResource struct {
Title string `json:"title" binding:"required"`
URL string `json:"url" binding:"required"`
CategoryID uint `json:"category_id,omitempty"`
PanID uint `json:"pan_id,omitempty"`
Tags []uint `json:"tags,omitempty"`
}
// CreateBatchTransferTask 创建批量转存任务
func (h *TaskHandler) CreateBatchTransferTask(c *gin.Context) {
var req struct {
Title string `json:"title" binding:"required"`
Description string `json:"description"`
Resources []BatchTransferResource `json:"resources" binding:"required,min=1"`
Title string `json:"title" binding:"required"`
Description string `json:"description"`
Resources []BatchTransferResource `json:"resources" binding:"required,min=1"`
SelectedAccounts []uint `json:"selected_accounts,omitempty"`
}
if err := c.ShouldBindJSON(&req); err != nil {
@@ -48,7 +50,13 @@ func (h *TaskHandler) CreateBatchTransferTask(c *gin.Context) {
return
}
utils.Info("创建批量转存任务: %s资源数量: %d", req.Title, len(req.Resources))
utils.Info("创建批量转存任务: %s资源数量: %d选择账号数量: %d", req.Title, len(req.Resources), len(req.SelectedAccounts))
// 构建任务配置
taskConfig := map[string]interface{}{
"selected_accounts": req.SelectedAccounts,
}
configJSON, _ := json.Marshal(taskConfig)
// 创建任务
newTask := &entity.Task{
@@ -57,6 +65,7 @@ func (h *TaskHandler) CreateBatchTransferTask(c *gin.Context) {
Type: "transfer",
Status: "pending",
TotalItems: len(req.Resources),
Config: string(configJSON),
CreatedAt: utils.GetCurrentTime(),
UpdatedAt: utils.GetCurrentTime(),
}
@@ -75,6 +84,7 @@ func (h *TaskHandler) CreateBatchTransferTask(c *gin.Context) {
Title: resource.Title,
URL: resource.URL,
CategoryID: resource.CategoryID,
PanID: resource.PanID,
Tags: resource.Tags,
}

View File

@@ -201,6 +201,19 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
return
}
// 获取任务项统计信息,用于计算正确的进度
stats, err := tm.repoMgr.TaskItemRepository.GetStatsByTaskID(task.ID)
if err != nil {
utils.Error("获取任务项统计失败: %v", err)
stats = map[string]int{
"total": 0,
"pending": 0,
"processing": 0,
"completed": 0,
"failed": 0,
}
}
// 获取待处理的任务项
items, err := tm.repoMgr.TaskItemRepository.GetByTaskIDAndStatus(task.ID, "pending")
if err != nil {
@@ -209,12 +222,35 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
return
}
totalItems := len(items)
processedItems := 0
successItems := 0
failedItems := 0
// 计算总任务项数和已完成的项数
totalItems := stats["total"]
completedItems := stats["completed"]
initialFailedItems := stats["failed"]
processingItems := stats["processing"]
utils.Info("任务 %d 共有 %d 个待处理项", task.ID, totalItems)
// 如果当前批次有处理中的任务项重置它们为pending状态服务器重启恢复
if processingItems > 0 {
utils.Info("任务 %d 发现 %d 个处理中的任务项重置为pending状态", task.ID, processingItems)
err = tm.repoMgr.TaskItemRepository.ResetProcessingItems(task.ID)
if err != nil {
utils.Error("重置处理中任务项失败: %v", err)
}
// 重新获取待处理的任务项
items, err = tm.repoMgr.TaskItemRepository.GetByTaskIDAndStatus(task.ID, "pending")
if err != nil {
utils.Error("重新获取任务项失败: %v", err)
tm.markTaskFailed(task.ID, fmt.Sprintf("重新获取任务项失败: %v", err))
return
}
}
currentBatchItems := len(items)
processedItems := completedItems + initialFailedItems // 已经处理的项目数
successItems := completedItems
failedItems := initialFailedItems
utils.Info("任务 %d 统计信息: 总计=%d, 已完成=%d, 已失败=%d, 待处理=%d",
task.ID, totalItems, completedItems, failedItems, currentBatchItems)
for _, item := range items {
select {
@@ -233,9 +269,11 @@ func (tm *TaskManager) processTask(ctx context.Context, task *entity.Task, proce
successItems++
}
// 更新任务进度
progress := float64(processedItems) / float64(totalItems) * 100
tm.updateTaskProgress(task.ID, progress, processedItems, successItems, failedItems)
// 更新任务进度(基于总任务项数)
if totalItems > 0 {
progress := float64(processedItems) / float64(totalItems) * 100
tm.updateTaskProgress(task.ID, progress, processedItems, successItems, failedItems)
}
}
}

View File

@@ -36,6 +36,7 @@ type TransferInput struct {
Title string `json:"title"`
URL string `json:"url"`
CategoryID uint `json:"category_id"`
PanID uint `json:"pan_id"`
Tags []uint `json:"tags"`
}
@@ -63,6 +64,22 @@ func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *ent
return fmt.Errorf("输入数据验证失败: %v", err)
}
// 获取任务配置中的账号信息
var selectedAccounts []uint
task, err := tp.repoMgr.TaskRepository.GetByID(taskID)
if err == nil && task.Config != "" {
var taskConfig map[string]interface{}
if err := json.Unmarshal([]byte(task.Config), &taskConfig); err == nil {
if accounts, ok := taskConfig["selected_accounts"].([]interface{}); ok {
for _, acc := range accounts {
if accID, ok := acc.(float64); ok {
selectedAccounts = append(selectedAccounts, uint(accID))
}
}
}
}
}
// 检查资源是否已存在
exists, existingResource, err := tp.checkResourceExists(input.URL)
if err != nil {
@@ -92,7 +109,7 @@ func (tp *TransferProcessor) Process(ctx context.Context, taskID uint, item *ent
}
// 执行转存操作
resourceID, saveURL, err := tp.performTransfer(ctx, &input)
resourceID, saveURL, err := tp.performTransfer(ctx, &input, selectedAccounts)
if err != nil {
// 转存失败,更新输出数据
output := TransferOutput{
@@ -180,7 +197,7 @@ func (tp *TransferProcessor) checkResourceExists(url string) (bool, *entity.Reso
}
// performTransfer 执行转存操作
func (tp *TransferProcessor) performTransfer(ctx context.Context, input *TransferInput) (uint, string, error) {
func (tp *TransferProcessor) performTransfer(ctx context.Context, input *TransferInput, selectedAccounts []uint) (uint, string, error) {
// 解析URL获取分享信息
shareInfo, err := tp.parseShareURL(input.URL)
if err != nil {
@@ -188,7 +205,7 @@ func (tp *TransferProcessor) performTransfer(ctx context.Context, input *Transfe
}
// 先执行转存操作
saveURL, err := tp.transferToCloud(ctx, shareInfo)
saveURL, err := tp.transferToCloud(ctx, shareInfo, selectedAccounts)
if err != nil {
utils.Error("云端转存失败: %v", err)
return 0, "", fmt.Errorf("转存失败: %v", err)
@@ -206,10 +223,28 @@ func (tp *TransferProcessor) performTransfer(ctx context.Context, input *Transfe
categoryID = &input.CategoryID
}
// 确定平台ID
var panID uint
if input.PanID != 0 {
// 使用指定的平台ID
panID = input.PanID
utils.Info("使用指定的平台ID: %d", panID)
} else {
// 如果没有指定默认使用夸克平台ID
quarkPanID, err := tp.getQuarkPanID()
if err != nil {
utils.Error("获取夸克平台ID失败: %v", err)
return 0, "", fmt.Errorf("获取夸克平台ID失败: %v", err)
}
panID = quarkPanID
utils.Info("使用默认夸克平台ID: %d", panID)
}
resource := &entity.Resource{
Title: input.Title,
URL: input.URL,
CategoryID: categoryID,
PanID: &panID, // 设置平台ID
SaveURL: saveURL, // 直接设置转存链接
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
@@ -278,10 +313,55 @@ func (tp *TransferProcessor) addResourceTags(resourceID uint, tagIDs []uint) err
}
// transferToCloud 执行云端转存
func (tp *TransferProcessor) transferToCloud(ctx context.Context, shareInfo *ShareInfo) (string, error) {
func (tp *TransferProcessor) transferToCloud(ctx context.Context, shareInfo *ShareInfo, selectedAccounts []uint) (string, error) {
// 转存任务独立于自动转存开关,直接执行转存逻辑
// 获取转存相关的配置(如最小存储空间等),但不检查自动转存开关
// 如果指定了账号,使用指定的账号
if len(selectedAccounts) > 0 {
utils.Info("使用指定的账号进行转存,账号数量: %d", len(selectedAccounts))
// 获取指定的账号
var validAccounts []entity.Cks
for _, accountID := range selectedAccounts {
account, err := tp.repoMgr.CksRepository.FindByID(accountID)
if err != nil {
utils.Error("获取账号 %d 失败: %v", accountID, err)
continue
}
if !account.IsValid {
utils.Error("账号 %d 无效", accountID)
continue
}
validAccounts = append(validAccounts, *account)
}
if len(validAccounts) == 0 {
return "", fmt.Errorf("指定的账号都无效或不存在")
}
utils.Info("找到 %d 个有效账号,开始转存处理...", len(validAccounts))
// 使用第一个有效账号进行转存
account := validAccounts[0]
// 创建网盘服务工厂
factory := pan.NewPanFactory()
// 执行转存
result := tp.transferSingleResource(shareInfo, account, factory)
if !result.Success {
return "", fmt.Errorf("转存失败: %s", result.ErrorMsg)
}
return result.SaveURL, nil
}
// 如果没有指定账号,使用原来的逻辑(自动选择)
utils.Info("未指定账号,使用自动选择逻辑")
// 获取夸克平台ID
quarkPanID, err := tp.getQuarkPanID()
if err != nil {

View File

@@ -46,6 +46,37 @@
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
网盘账号 <span class="text-red-500">*</span>
</label>
<n-select
v-model:value="selectedAccounts"
:options="accountOptions"
placeholder="选择网盘账号"
multiple
filterable
:loading="accountsLoading"
@update:value="handleAccountChange"
>
<template #option="{ option: accountOption }">
<div class="flex items-center justify-between w-full">
<div class="flex items-center space-x-2">
<span class="text-sm">{{ accountOption.label }}</span>
<n-tag v-if="accountOption.is_valid" type="success" size="small">有效</n-tag>
<n-tag v-else type="error" size="small">无效</n-tag>
</div>
<div class="text-xs text-gray-500">
{{ formatSpace(accountOption.left_space) }}
</div>
</div>
</template>
</n-select>
<div class="text-xs text-gray-500 mt-1">
请选择要使用的网盘账号系统将使用选中的账号进行转存操作
</div>
</div>
<!-- 操作按钮 -->
<div class="space-y-3 pt-4">
<n-button
@@ -53,7 +84,7 @@
block
size="large"
:loading="processing"
:disabled="!resourceText.trim() || processing"
:disabled="!resourceText.trim() || !selectedAccounts.length || processing"
@click="handleBatchTransfer"
>
<template #icon>
@@ -115,7 +146,7 @@
<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, h } from 'vue'
import { usePanApi, useTaskApi } from '~/composables/useApi'
import { usePanApi, useTaskApi, useCksApi } from '~/composables/useApi'
import { useMessage } from 'naive-ui'
// 数据状态
@@ -142,13 +173,17 @@ const selectedPlatform = ref(null)
const autoValidate = ref(true)
const skipExisting = ref(true)
const autoTransfer = ref(false)
const selectedAccounts = ref<number[]>([])
// 选项数据
const platformOptions = ref<any[]>([])
const accountOptions = ref<any[]>([])
const accountsLoading = ref(false)
// API实例
const panApi = usePanApi()
const taskApi = useTaskApi()
const cksApi = useCksApi()
const message = useMessage()
// 计算属性
@@ -275,6 +310,11 @@ const handleBatchTransfer = async () => {
return
}
if (!selectedAccounts.value || selectedAccounts.value.length === 0) {
message.warning('请选择至少一个网盘账号')
return
}
processing.value = true
results.value = []
@@ -291,7 +331,7 @@ const handleBatchTransfer = async () => {
const taskTitle = `批量转存任务_${new Date().toLocaleString('zh-CN')}`
const taskData = {
title: taskTitle,
description: `批量转存 ${resourceList.length} 个资源`,
description: `批量转存 ${resourceList.length} 个资源,使用 ${selectedAccounts.value.length} 个账号`,
resources: resourceList.map(item => {
const resource: any = {
title: item.title,
@@ -304,7 +344,9 @@ const handleBatchTransfer = async () => {
resource.tags = selectedTags.value
}
return resource
})
}),
// 添加选择的账号信息
selected_accounts: selectedAccounts.value
}
console.log('创建任务数据:', taskData)
@@ -472,15 +514,55 @@ const updateResultsDisplay = () => {
}
}
// 获取网盘账号选项
const getAccountOptions = async () => {
accountsLoading.value = true
try {
const response = await cksApi.getCks() as any
const accounts = Array.isArray(response) ? response : []
accountOptions.value = accounts.map((account: any) => ({
label: `${account.username || '未知用户'} (${account.pan?.name || '未知平台'})`,
value: account.id,
is_valid: account.is_valid,
left_space: account.left_space,
username: account.username,
pan_name: account.pan?.name || '未知平台'
}))
} catch (error) {
console.error('获取网盘账号选项失败:', error)
message.error('获取网盘账号失败')
} finally {
accountsLoading.value = false
}
}
// 处理账号选择变化
const handleAccountChange = (value: number[]) => {
selectedAccounts.value = value
console.log('选择的账号:', value)
}
// 格式化空间大小
const formatSpace = (bytes: number) => {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
// 清空输入
const clearInput = () => {
resourceText.value = ''
results.value = []
selectedAccounts.value = []
}
// 初始化
onMounted(() => {
fetchPlatforms()
getAccountOptions()
})
// 组件销毁时清理定时器

View File

@@ -164,30 +164,7 @@
</div>
</div>
<!-- 操作按钮 -->
<div class="flex flex-col space-y-2 ml-4">
<n-button
size="small"
type="primary"
:loading="item.transferring"
@click="handleSingleTransfer(item)"
>
<template #icon>
<i class="fas fa-exchange-alt"></i>
</template>
{{ item.transferring ? '转存中' : '立即转存' }}
</n-button>
<n-button
size="small"
@click="viewResource(item)"
>
<template #icon>
<i class="fas fa-eye"></i>
</template>
查看详情
</n-button>
</div>
</div>
</div>
</div>
@@ -211,6 +188,73 @@
</div>
</n-card>
<!-- 网盘账号选择模态框 -->
<n-modal v-model:show="showAccountSelectionModal" preset="card" title="选择网盘账号" style="width: 600px">
<div class="space-y-4">
<div class="text-sm text-gray-600 dark:text-gray-400">
请选择要使用的网盘账号进行批量转存操作
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
网盘账号 <span class="text-red-500">*</span>
</label>
<n-select
v-model:value="selectedAccounts"
:options="accountOptions"
placeholder="选择网盘账号"
multiple
filterable
:loading="accountsLoading"
@update:value="handleAccountChange"
>
<template #option="{ option: accountOption }">
<div class="flex items-center justify-between w-full">
<div class="flex items-center space-x-2">
<span class="text-sm">{{ accountOption.label }}</span>
<n-tag v-if="accountOption.is_valid" type="success" size="small">有效</n-tag>
<n-tag v-else type="error" size="small">无效</n-tag>
</div>
<div class="text-xs text-gray-500">
{{ formatSpace(accountOption.left_space) }}
</div>
</div>
</template>
</n-select>
<div class="text-xs text-gray-500 mt-1">
请选择要使用的网盘账号系统将使用选中的账号进行转存操作
</div>
</div>
<div class="bg-yellow-50 dark:bg-yellow-900/20 p-3 rounded border border-yellow-200 dark:border-yellow-800">
<div class="flex items-start space-x-2">
<i class="fas fa-exclamation-triangle text-yellow-500 mt-0.5"></i>
<div class="text-sm text-yellow-800 dark:text-yellow-200">
<p> 转存过程可能需要较长时间</p>
<p> 请确保选中的网盘账号有足够的存储空间</p>
<p> 转存完成后可在"已转存列表"中查看结果</p>
</div>
</div>
</div>
</div>
<template #footer>
<div class="flex justify-end space-x-3">
<n-button @click="showAccountSelectionModal = false">
取消
</n-button>
<n-button
type="primary"
:disabled="selectedAccounts.length === 0"
:loading="batchTransferring"
@click="confirmBatchTransfer"
>
{{ batchTransferring ? '创建任务中...' : '继续' }}
</n-button>
</div>
</template>
</n-modal>
<!-- 转存结果模态框 -->
<n-modal v-model:show="showTransferResult" preset="card" title="转存结果" style="width: 600px">
<div v-if="transferResults.length > 0" class="space-y-4">
@@ -252,14 +296,15 @@
<script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue'
import { useResourceApi, useCategoryApi, useTagApi } from '~/composables/useApi'
import { useResourceApi, useCategoryApi, useTagApi, useCksApi, useTaskApi } from '~/composables/useApi'
import { useMessage } from 'naive-ui'
// 数据状态
const loading = ref(false)
const resources = ref([])
const total = ref(0)
const currentPage = ref(1)
const pageSize = ref(10000)
const pageSize = ref(2000)
// 搜索条件
const searchQuery = ref('')
@@ -274,6 +319,10 @@ const selectedResources = ref([])
const batchTransferring = ref(false)
const showTransferResult = ref(false)
const transferResults = ref([])
const showAccountSelectionModal = ref(false)
const selectedAccounts = ref<number[]>([])
const accountOptions = ref<any[]>([])
const accountsLoading = ref(false)
// 选项数据
const categoryOptions = ref([])
@@ -288,6 +337,9 @@ const statusOptions = [
const resourceApi = useResourceApi()
const categoryApi = useCategoryApi()
const tagApi = useTagApi()
const cksApi = useCksApi()
const taskApi = useTaskApi()
const message = useMessage()
// 计算属性
const isAllSelected = computed(() => {
@@ -376,6 +428,44 @@ const fetchTags = async () => {
}
}
// 获取网盘账号选项
const getAccountOptions = async () => {
accountsLoading.value = true
try {
const response = await cksApi.getCks() as any
const accounts = Array.isArray(response) ? response : []
accountOptions.value = accounts.map((account: any) => ({
label: `${account.username || '未知用户'} (${account.pan?.name || '未知平台'})`,
value: account.id,
is_valid: account.is_valid,
left_space: account.left_space,
username: account.username,
pan_name: account.pan?.name || '未知平台'
}))
} catch (error) {
console.error('获取网盘账号选项失败:', error)
message.error('获取网盘账号失败')
} finally {
accountsLoading.value = false
}
}
// 处理账号选择变化
const handleAccountChange = (value: number[]) => {
selectedAccounts.value = value
console.log('选择的账号:', value)
}
// 格式化空间大小
const formatSpace = (bytes: number) => {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB', 'TB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
// 搜索处理
const handleSearch = () => {
currentPage.value = 1
@@ -421,79 +511,23 @@ const toggleResourceSelection = (id: number, checked: boolean) => {
}
}
// 单个转存
const handleSingleTransfer = async (resource: any) => {
resource.transferring = true
try {
// 这里应该调用实际的转存API
// 由于只是UI展示模拟转存过程
await new Promise(resolve => setTimeout(resolve, 2000))
// 模拟随机成功/失败
const isSuccess = Math.random() > 0.3
if (isSuccess) {
$message.success(`${resource.title} 转存成功`)
// 刷新列表
refreshData()
} else {
$message.error(`${resource.title} 转存失败`)
}
} catch (error) {
console.error('转存失败:', error)
$message.error('转存失败')
} finally {
resource.transferring = false
}
}
// 批量转存
const handleBatchTransfer = async () => {
if (selectedResources.value.length === 0) {
$message.warning('请选择要转存的资源')
message.warning('请选择要转存的资源')
return
}
batchTransferring.value = true
transferResults.value = []
// 先获取网盘账号列表
await getAccountOptions()
try {
const selectedItems = resources.value.filter(r => selectedResources.value.includes(r.id))
// 这里应该调用实际的批量转存API
// 由于只是UI展示模拟批量转存过程
for (const item of selectedItems) {
await new Promise(resolve => setTimeout(resolve, 1000))
const isSuccess = Math.random() > 0.3
transferResults.value.push({
id: item.id,
title: item.title,
url: item.url,
success: isSuccess,
message: isSuccess ? '转存成功' : '转存失败:网络错误'
})
}
showTransferResult.value = true
// 刷新列表
refreshData()
} catch (error) {
console.error('批量转存失败:', error)
$message.error('批量转存失败')
} finally {
batchTransferring.value = false
}
// 显示账号选择模态框
showAccountSelectionModal.value = true
}
// 查看资源详情
const viewResource = (resource: any) => {
console.log('查看资源详情:', resource)
// 这里可以打开资源详情模态框
}
// 获取状态类型
const getStatusType = (resource: any) => {
@@ -514,6 +548,47 @@ const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString()
}
// 确认批量转存
const confirmBatchTransfer = async () => {
if (selectedAccounts.value.length === 0) {
message.warning('请选择至少一个网盘账号')
return
}
batchTransferring.value = true
try {
const selectedItems = resources.value.filter(r => selectedResources.value.includes(r.id))
const taskData = {
title: `批量转存 ${selectedItems.length} 个资源`,
description: `批量转存 ${selectedItems.length} 个资源,使用 ${selectedAccounts.value.length} 个账号`,
resources: selectedItems.map(r => ({
title: r.title,
url: r.url,
category_id: r.category_id || 0,
pan_id: r.pan_id || 0
})),
selected_accounts: selectedAccounts.value
}
const response = await taskApi.createBatchTransferTask(taskData) as any
message.success(`批量转存任务已创建,共 ${selectedItems.length} 个资源`)
// 关闭模态框
showAccountSelectionModal.value = false
selectedAccounts.value = []
// 刷新列表
refreshData()
} catch (error) {
console.error('创建批量转存任务失败:', error)
message.error('创建批量转存任务失败')
} finally {
batchTransferring.value = false
}
}
// 初始化
onMounted(() => {
fetchCategories()

View File

@@ -33,22 +33,22 @@
<!-- 自动处理状态 -->
<div class="flex items-center gap-2 bg-gray-100 dark:bg-gray-700 rounded-lg px-3 py-2">
<div class="w-2 h-2 rounded-full animate-pulse" :class="{
'bg-red-400': !systemConfig?.auto_process_ready_resources,
'bg-green-400': systemConfig?.auto_process_ready_resources
'bg-red-400': !isAutoProcessEnabled,
'bg-green-400': isAutoProcessEnabled
}"></div>
<span class="text-xs text-gray-700 dark:text-gray-300 font-medium">
自动处理已<span>{{ systemConfig?.auto_process_ready_resources ? '开启' : '关闭' }}</span>
自动处理已<span>{{ isAutoProcessEnabled ? '开启' : '关闭' }}</span>
</span>
</div>
<!-- 自动转存状态 -->
<div class="flex items-center gap-2 bg-gray-100 dark:bg-gray-700 rounded-lg px-3 py-2">
<div class="w-2 h-2 rounded-full animate-pulse" :class="{
'bg-red-400': !systemConfig?.auto_transfer_enabled,
'bg-green-400': systemConfig?.auto_transfer_enabled
'bg-red-400': !isAutoTransferEnabled,
'bg-green-400': isAutoTransferEnabled
}"></div>
<span class="text-xs text-gray-700 dark:text-gray-300 font-medium">
自动转存已<span>{{ systemConfig?.auto_transfer_enabled ? '开启' : '关闭' }}</span>
自动转存已<span>{{ isAutoTransferEnabled ? '开启' : '关闭' }}</span>
</span>
</div>
@@ -305,6 +305,7 @@ const systemConfigStore = useSystemConfigStore()
const taskStore = useTaskStore()
// 初始化系统配置管理员页面使用管理员API
// 在setup阶段初始化确保数据可用
await systemConfigStore.initConfig(false, true)
// 版本信息
@@ -344,9 +345,23 @@ onBeforeUnmount(() => {
const systemConfig = computed(() => {
const config = systemConfigStore.config || {}
console.log('顶部导航系统配置:', config)
console.log('自动处理状态:', config.auto_process_ready_resources)
console.log('自动转存状态:', config.auto_transfer_enabled)
return config
})
// 自动处理状态(确保布尔值)
const isAutoProcessEnabled = computed(() => {
const value = systemConfig.value?.auto_process_ready_resources
return value === true || value === 'true' || value === '1'
})
// 自动转存状态(确保布尔值)
const isAutoTransferEnabled = computed(() => {
const value = systemConfig.value?.auto_transfer_enabled
return value === true || value === 'true' || value === '1'
})
// 用户菜单状态
const showUserMenu = ref(false)

View File

@@ -400,7 +400,8 @@ const singleTransfer = async (resource: any) => {
resources: [{
title: resource.title,
url: resource.url,
category_id: resource.category_id || 0
category_id: resource.category_id || 0,
pan_id: resource.pan_id || 0
}]
}
@@ -443,7 +444,8 @@ const confirmBatchTransfer = async () => {
resources: selectedResources.value.map(r => ({
title: r.title,
url: r.url,
category_id: r.category_id || 0
category_id: r.category_id || 0,
pan_id: r.pan_id || 0
}))
}

View File

@@ -1,5 +1,6 @@
import { defineStore } from 'pinia'
import { useApiFetch } from '~/composables/useApiFetch'
import { parseApiResponse } from '~/composables/useApi'
export const useSystemConfigStore = defineStore('systemConfig', {
state: () => ({
@@ -15,9 +16,11 @@ export const useSystemConfigStore = defineStore('systemConfig', {
const response = await useApiFetch(apiUrl)
console.log('Store API响应:', response) // 调试信息
// 正确处理API响应结构
const data = response.data || response
// 使用parseApiResponse正确解析API响应
const data = parseApiResponse(response)
console.log('Store 处理后的数据:', data) // 调试信息
console.log('Store 自动处理状态:', data.auto_process_ready_resources)
console.log('Store 自动转存状态:', data.auto_transfer_enabled)
this.config = data
this.initialized = true