mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-25 11:29:37 +08:00
update: ui
This commit is contained in:
@@ -394,6 +394,7 @@ func (h *TaskHandler) CreateExpansionTask(c *gin.Context) {
|
|||||||
var req struct {
|
var req struct {
|
||||||
PanAccountID uint `json:"pan_account_id" binding:"required"`
|
PanAccountID uint `json:"pan_account_id" binding:"required"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
DataSource map[string]interface{} `json:"dataSource"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
@@ -420,10 +421,14 @@ func (h *TaskHandler) CreateExpansionTask(c *gin.Context) {
|
|||||||
accountName = fmt.Sprintf("账号%d", cks.ID)
|
accountName = fmt.Sprintf("账号%d", cks.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建任务配置(存储账号ID)
|
// 构建任务配置(存储账号ID和数据源)
|
||||||
taskConfig := map[string]interface{}{
|
taskConfig := map[string]interface{}{
|
||||||
"pan_account_id": req.PanAccountID,
|
"pan_account_id": req.PanAccountID,
|
||||||
}
|
}
|
||||||
|
// 如果有数据源配置,添加到taskConfig中
|
||||||
|
if req.DataSource != nil && len(req.DataSource) > 0 {
|
||||||
|
taskConfig["data_source"] = req.DataSource
|
||||||
|
}
|
||||||
configJSON, _ := json.Marshal(taskConfig)
|
configJSON, _ := json.Marshal(taskConfig)
|
||||||
|
|
||||||
// 创建任务标题,包含账号名称
|
// 创建任务标题,包含账号名称
|
||||||
@@ -451,6 +456,10 @@ func (h *TaskHandler) CreateExpansionTask(c *gin.Context) {
|
|||||||
expansionInput := task.ExpansionInput{
|
expansionInput := task.ExpansionInput{
|
||||||
PanAccountID: req.PanAccountID,
|
PanAccountID: req.PanAccountID,
|
||||||
}
|
}
|
||||||
|
// 如果有数据源配置,添加到输入数据中
|
||||||
|
if req.DataSource != nil && len(req.DataSource) > 0 {
|
||||||
|
expansionInput.DataSource = req.DataSource
|
||||||
|
}
|
||||||
|
|
||||||
inputJSON, _ := json.Marshal(expansionInput)
|
inputJSON, _ := json.Marshal(expansionInput)
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ func (ep *ExpansionProcessor) GetTaskType() string {
|
|||||||
// ExpansionInput 扩容任务输入数据结构
|
// ExpansionInput 扩容任务输入数据结构
|
||||||
type ExpansionInput struct {
|
type ExpansionInput struct {
|
||||||
PanAccountID uint `json:"pan_account_id"`
|
PanAccountID uint `json:"pan_account_id"`
|
||||||
|
DataSource map[string]interface{} `json:"data_source,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExpansionOutput 扩容任务输出数据结构
|
// ExpansionOutput 扩容任务输出数据结构
|
||||||
@@ -93,8 +94,8 @@ func (ep *ExpansionProcessor) Process(ctx context.Context, taskID uint, item *en
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行扩容操作(这里留空,直接返回成功)
|
// 执行扩容操作(传入数据源)
|
||||||
if err := ep.performExpansion(ctx, input.PanAccountID); err != nil {
|
if err := ep.performExpansion(ctx, input.PanAccountID, input.DataSource); err != nil {
|
||||||
output := ExpansionOutput{
|
output := ExpansionOutput{
|
||||||
Success: false,
|
Success: false,
|
||||||
Message: "扩容失败",
|
Message: "扩容失败",
|
||||||
@@ -177,11 +178,11 @@ func (ep *ExpansionProcessor) checkAccountType(panAccountID uint) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// performExpansion 执行扩容操作
|
// performExpansion 执行扩容操作
|
||||||
func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID uint) error {
|
func (ep *ExpansionProcessor) performExpansion(ctx context.Context, panAccountID uint, dataSource map[string]interface{}) error {
|
||||||
// 扩容逻辑暂时留空,直接返回成功
|
// 扩容逻辑暂时留空,直接返回成功
|
||||||
// TODO: 实现具体的扩容逻辑
|
// TODO: 实现具体的扩容逻辑
|
||||||
|
|
||||||
utils.Info("执行扩容操作,账号ID: %d", panAccountID)
|
utils.Info("执行扩容操作,账号ID: %d, 数据源: %v", panAccountID, dataSource)
|
||||||
|
|
||||||
// 模拟扩容操作延迟
|
// 模拟扩容操作延迟
|
||||||
// time.Sleep(2 * time.Second)
|
// time.Sleep(2 * time.Second)
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<div class="space-y-6 h-full">
|
||||||
|
|
||||||
<!-- 输入区域 -->
|
<!-- 输入区域 -->
|
||||||
<n-card title="批量转存资源列表">
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<!-- 左侧:资源输入 -->
|
<!-- 左侧:资源输入 -->
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
@@ -59,15 +58,15 @@
|
|||||||
:loading="accountsLoading"
|
:loading="accountsLoading"
|
||||||
@update:value="handleAccountChange"
|
@update:value="handleAccountChange"
|
||||||
>
|
>
|
||||||
<template #option="{ option: accountOption }">
|
<template #option="scope">
|
||||||
<div class="flex items-center justify-between w-full">
|
<div class="flex items-center justify-between w-full" v-if="scope && scope.option">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<span class="text-sm">{{ accountOption.label }}</span>
|
<span class="text-sm">{{ scope.option.label || '未知账号' }}</span>
|
||||||
<n-tag v-if="accountOption.is_valid" type="success" size="small">有效</n-tag>
|
<n-tag v-if="scope.option.is_valid" type="success" size="small">有效</n-tag>
|
||||||
<n-tag v-else type="error" size="small">无效</n-tag>
|
<n-tag v-else type="error" size="small">无效</n-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs text-gray-500">
|
<div class="text-xs text-gray-500">
|
||||||
{{ formatSpace(accountOption.left_space) }}
|
{{ formatSpace(scope.option.left_space || 0) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -106,7 +105,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
|
||||||
|
|
||||||
<!-- 处理结果 -->
|
<!-- 处理结果 -->
|
||||||
<n-card v-if="results.length > 0" title="转存结果">
|
<n-card v-if="results.length > 0" title="转存结果">
|
||||||
@@ -202,15 +200,15 @@ const invalidUrls = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const successCount = computed(() => {
|
const successCount = computed(() => {
|
||||||
return results.value.filter((r: any) => r.status === 'success').length
|
return results.value ? results.value.filter((r: any) => r && r.status === 'success').length : 0
|
||||||
})
|
})
|
||||||
|
|
||||||
const failedCount = computed(() => {
|
const failedCount = computed(() => {
|
||||||
return results.value.filter((r: any) => r.status === 'failed').length
|
return results.value ? results.value.filter((r: any) => r && r.status === 'failed').length : 0
|
||||||
})
|
})
|
||||||
|
|
||||||
const processingCount = computed(() => {
|
const processingCount = computed(() => {
|
||||||
return results.value.filter((r: any) => r.status === 'processing').length
|
return results.value ? results.value.filter((r: any) => r && r.status === 'processing').length : 0
|
||||||
})
|
})
|
||||||
|
|
||||||
// 结果表格列
|
// 结果表格列
|
||||||
@@ -243,9 +241,10 @@ const resultColumns = [
|
|||||||
pending: { color: 'warning', text: '等待中', icon: 'fas fa-clock' }
|
pending: { color: 'warning', text: '等待中', icon: 'fas fa-clock' }
|
||||||
}
|
}
|
||||||
const status = statusMap[row.status as keyof typeof statusMap] || statusMap.failed
|
const status = statusMap[row.status as keyof typeof statusMap] || statusMap.failed
|
||||||
return h('n-tag', { type: status.color }, {
|
const safeStatus = status || statusMap.failed
|
||||||
icon: () => h('i', { class: status.icon }),
|
return h('n-tag', { type: safeStatus.color }, {
|
||||||
default: () => status.text
|
icon: () => h('i', { class: safeStatus.icon }),
|
||||||
|
default: () => safeStatus.text
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -264,7 +263,7 @@ const resultColumns = [
|
|||||||
tooltip: true
|
tooltip: true
|
||||||
},
|
},
|
||||||
render: (row: any) => {
|
render: (row: any) => {
|
||||||
if (row.saveUrl) {
|
if (row && row.saveUrl) {
|
||||||
return h('a', {
|
return h('a', {
|
||||||
href: row.saveUrl,
|
href: row.saveUrl,
|
||||||
target: '_blank',
|
target: '_blank',
|
||||||
@@ -355,6 +354,10 @@ const handleBatchTransfer = async () => {
|
|||||||
const taskResponse = await taskApi.createBatchTransferTask(taskData) as any
|
const taskResponse = await taskApi.createBatchTransferTask(taskData) as any
|
||||||
console.log('任务创建响应:', taskResponse)
|
console.log('任务创建响应:', taskResponse)
|
||||||
|
|
||||||
|
if (!taskResponse || !taskResponse.task_id) {
|
||||||
|
throw new Error('创建任务失败:响应数据无效')
|
||||||
|
}
|
||||||
|
|
||||||
currentTaskId.value = taskResponse.task_id
|
currentTaskId.value = taskResponse.task_id
|
||||||
|
|
||||||
// 第四步:启动任务
|
// 第四步:启动任务
|
||||||
@@ -523,14 +526,17 @@ const getAccountOptions = async () => {
|
|||||||
const response = await cksApi.getCks() as any
|
const response = await cksApi.getCks() as any
|
||||||
const accounts = Array.isArray(response) ? response : []
|
const accounts = Array.isArray(response) ? response : []
|
||||||
|
|
||||||
accountOptions.value = accounts.map((account: any) => ({
|
accountOptions.value = accounts.map((account: any) => {
|
||||||
|
if (!account) return null
|
||||||
|
return {
|
||||||
label: `${account.username || '未知用户'} (${account.pan?.name || '未知平台'})`,
|
label: `${account.username || '未知用户'} (${account.pan?.name || '未知平台'})`,
|
||||||
value: account.id,
|
value: account.id,
|
||||||
is_valid: account.is_valid,
|
is_valid: account.is_valid || false,
|
||||||
left_space: account.left_space,
|
left_space: account.left_space || 0,
|
||||||
username: account.username,
|
username: account.username || '未知用户',
|
||||||
pan_name: account.pan?.name || '未知平台'
|
pan_name: account.pan?.name || '未知平台'
|
||||||
}))
|
}
|
||||||
|
}).filter(option => option !== null) as any[]
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取网盘账号选项失败:', error)
|
console.error('获取网盘账号选项失败:', error)
|
||||||
message.error('获取网盘账号失败')
|
message.error('获取网盘账号失败')
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-4">
|
<div class="flex flex-col gap-2 h-full">
|
||||||
<!-- 搜索和筛选 -->
|
<!-- 搜索和筛选 -->
|
||||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
<div class="flex-0 grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||||
<n-input
|
<n-input
|
||||||
v-model:value="searchQuery"
|
v-model:value="searchQuery"
|
||||||
placeholder="搜索已转存资源..."
|
placeholder="搜索已转存资源..."
|
||||||
@@ -34,28 +34,111 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 调试信息 -->
|
<!-- 调试信息 -->
|
||||||
<div class="text-sm text-gray-500 mb-2">
|
<div class="flex-0 text-sm text-gray-500">
|
||||||
数据数量: {{ resources.length }}, 总数: {{ total }}, 加载状态: {{ loading }}
|
数据数量: {{ resources.length }}, 总数: {{ total }}, 加载状态: {{ loading }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 数据表格 -->
|
<!-- 资源列表 -->
|
||||||
<n-data-table
|
<div class="flex-1 h-1">
|
||||||
:columns="columns"
|
<div v-if="loading" class="flex items-center justify-center py-8">
|
||||||
:data="resources"
|
<n-spin size="large" />
|
||||||
:loading="loading"
|
</div>
|
||||||
:pagination="pagination"
|
|
||||||
:remote="true"
|
<div v-else-if="resources.length === 0" class="text-center py-8">
|
||||||
|
<i class="fas fa-inbox text-4xl text-gray-400 mb-4"></i>
|
||||||
|
<p class="text-gray-500">暂无已转存的资源</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="h-full">
|
||||||
|
<!-- 虚拟列表 -->
|
||||||
|
<n-virtual-list
|
||||||
|
:items="resources"
|
||||||
|
:item-size="120"
|
||||||
|
class="h-full"
|
||||||
|
>
|
||||||
|
<template #default="{ item }">
|
||||||
|
<div class="p-4 border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800">
|
||||||
|
<div class="flex items-start space-x-4">
|
||||||
|
<!-- 资源信息 -->
|
||||||
|
<div class="flex-1 min-w-0">
|
||||||
|
<div class="flex items-center space-x-2 mb-2">
|
||||||
|
<!-- ID -->
|
||||||
|
<span class="text-xs bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400 px-2 py-1 rounded">
|
||||||
|
ID: {{ item.id }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 标题 -->
|
||||||
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white line-clamp-1 flex-1">
|
||||||
|
{{ item.title || '未命名资源' }}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm mt-3">
|
||||||
|
<!-- 分类 -->
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="fas fa-folder mr-1 text-gray-400"></i>
|
||||||
|
<span class="text-gray-600 dark:text-gray-400">分类:</span>
|
||||||
|
<span class="ml-2">{{ item.category_name || '未分类' }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 转存时间 -->
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="fas fa-calendar mr-1 text-gray-400"></i>
|
||||||
|
<span class="text-gray-600 dark:text-gray-400">转存时间:</span>
|
||||||
|
<span class="ml-2">{{ formatDate(item.updated_at) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 浏览数 -->
|
||||||
|
<div class="flex items-center">
|
||||||
|
<i class="fas fa-eye mr-1 text-gray-400"></i>
|
||||||
|
<span class="text-gray-600 dark:text-gray-400">浏览数:</span>
|
||||||
|
<span class="ml-2">{{ item.view_count || 0 }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 转存链接 -->
|
||||||
|
<div class="mt-3">
|
||||||
|
<div class="flex items-start space-x-2">
|
||||||
|
<span class="text-xs text-gray-400">转存链接:</span>
|
||||||
|
<a
|
||||||
|
v-if="item.save_url"
|
||||||
|
:href="item.save_url"
|
||||||
|
target="_blank"
|
||||||
|
class="text-xs text-green-500 hover:text-green-700 break-all"
|
||||||
|
>
|
||||||
|
{{ item.save_url.length > 60 ? item.save_url.substring(0, 60) + '...' : item.save_url }}
|
||||||
|
</a>
|
||||||
|
<span v-else class="text-xs text-gray-500">暂无转存链接</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</n-virtual-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="flex-0">
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<n-pagination
|
||||||
|
v-model:page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:item-count="total"
|
||||||
|
:page-sizes="[10000, 20000, 50000, 100000]"
|
||||||
|
show-size-picker
|
||||||
|
show-quick-jumper
|
||||||
@update:page="handlePageChange"
|
@update:page="handlePageChange"
|
||||||
@update:page-size="handlePageSizeChange"
|
@update:page-size="handlePageSizeChange"
|
||||||
:row-key="(row: any) => row.id"
|
|
||||||
virtual-scroll
|
|
||||||
max-height="500"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, computed, onMounted, h } from 'vue'
|
import { ref, reactive, computed, onMounted } from 'vue'
|
||||||
import { useResourceApi, usePanApi } from '~/composables/useApi'
|
import { useResourceApi, usePanApi } from '~/composables/useApi'
|
||||||
import { useMessage } from 'naive-ui'
|
import { useMessage } from 'naive-ui'
|
||||||
|
|
||||||
@@ -81,78 +164,17 @@ const panApi = usePanApi()
|
|||||||
// 获取平台数据
|
// 获取平台数据
|
||||||
const { data: platformsData } = await useAsyncData('transferredPlatforms', () => panApi.getPans())
|
const { data: platformsData } = await useAsyncData('transferredPlatforms', () => panApi.getPans())
|
||||||
|
|
||||||
// 平台选项
|
|
||||||
const platformOptions = computed(() => {
|
|
||||||
const data = platformsData.value as any
|
|
||||||
const platforms = data?.data || data || []
|
|
||||||
return platforms.map((platform: any) => ({
|
|
||||||
label: platform.remark || platform.name,
|
|
||||||
value: platform.id
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
|
|
||||||
// 获取平台名称
|
// 获取平台名称
|
||||||
const getPlatformName = (platformId: number) => {
|
const getPlatformName = (platformId: number) => {
|
||||||
const platform = (platformsData.value as any)?.data?.find((plat: any) => plat.id === platformId)
|
const platform = (platformsData.value as any)?.data?.find((plat: any) => plat.id === platformId)
|
||||||
return platform?.remark || platform?.name || '未知平台'
|
return platform?.remark || platform?.name || '未知平台'
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页配置
|
// 格式化日期
|
||||||
const pagination = reactive({
|
const formatDate = (dateString: string) => {
|
||||||
page: 1,
|
if (!dateString) return '未知时间'
|
||||||
pageSize: 10000,
|
return new Date(dateString).toLocaleDateString()
|
||||||
itemCount: 0,
|
}
|
||||||
pageSizes: [10000, 20000, 50000, 100000],
|
|
||||||
showSizePicker: true,
|
|
||||||
showQuickJumper: true,
|
|
||||||
prefix: ({ itemCount }: any) => `共 ${itemCount} 条`
|
|
||||||
})
|
|
||||||
|
|
||||||
// 表格列配置
|
|
||||||
const columns: any[] = [
|
|
||||||
{
|
|
||||||
title: 'ID',
|
|
||||||
key: 'id',
|
|
||||||
width: 60,
|
|
||||||
fixed: 'left' as const
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '标题',
|
|
||||||
key: 'title',
|
|
||||||
width: 200,
|
|
||||||
ellipsis: {
|
|
||||||
tooltip: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '分类',
|
|
||||||
key: 'category_name',
|
|
||||||
width: 80
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '转存链接',
|
|
||||||
key: 'save_url',
|
|
||||||
width: 200,
|
|
||||||
ellipsis: {
|
|
||||||
tooltip: true
|
|
||||||
},
|
|
||||||
render: (row: any) => {
|
|
||||||
return h('a', {
|
|
||||||
href: row.save_url,
|
|
||||||
target: '_blank',
|
|
||||||
class: 'text-green-500 hover:text-green-700'
|
|
||||||
}, row.save_url.length > 30 ? row.save_url.substring(0, 30) + '...' : row.save_url)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '转存时间',
|
|
||||||
key: 'updated_at',
|
|
||||||
width: 130,
|
|
||||||
render: (row: any) => {
|
|
||||||
return new Date(row.updated_at).toLocaleDateString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// 获取已转存资源
|
// 获取已转存资源
|
||||||
const fetchTransferredResources = async () => {
|
const fetchTransferredResources = async () => {
|
||||||
@@ -183,24 +205,20 @@ const fetchTransferredResources = async () => {
|
|||||||
console.log('使用嵌套data格式,数量:', result.data.data.length)
|
console.log('使用嵌套data格式,数量:', result.data.data.length)
|
||||||
resources.value = result.data.data
|
resources.value = result.data.data
|
||||||
total.value = result.data.total || 0
|
total.value = result.data.total || 0
|
||||||
pagination.itemCount = result.data.total || 0
|
|
||||||
} else {
|
} else {
|
||||||
// 处理直接的data结构:{data: [...], total: ...}
|
// 处理直接的data结构:{data: [...], total: ...}
|
||||||
console.log('使用直接data格式,数量:', result.data.length)
|
console.log('使用直接data格式,数量:', result.data.length)
|
||||||
resources.value = result.data
|
resources.value = result.data
|
||||||
total.value = result.total || 0
|
total.value = result.total || 0
|
||||||
pagination.itemCount = result.total || 0
|
|
||||||
}
|
}
|
||||||
} else if (Array.isArray(result)) {
|
} else if (Array.isArray(result)) {
|
||||||
console.log('使用数组格式,数量:', result.length)
|
console.log('使用数组格式,数量:', result.length)
|
||||||
resources.value = result
|
resources.value = result
|
||||||
total.value = result.length
|
total.value = result.length
|
||||||
pagination.itemCount = result.length
|
|
||||||
} else {
|
} else {
|
||||||
console.log('未知格式,设置空数组')
|
console.log('未知格式,设置空数组')
|
||||||
resources.value = []
|
resources.value = []
|
||||||
total.value = 0
|
total.value = 0
|
||||||
pagination.itemCount = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('最终 resources.value:', resources.value)
|
console.log('最终 resources.value:', resources.value)
|
||||||
@@ -223,22 +241,18 @@ const fetchTransferredResources = async () => {
|
|||||||
// 搜索处理
|
// 搜索处理
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
currentPage.value = 1
|
currentPage.value = 1
|
||||||
pagination.page = 1
|
|
||||||
fetchTransferredResources()
|
fetchTransferredResources()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分页处理
|
// 分页处理
|
||||||
const handlePageChange = (page: number) => {
|
const handlePageChange = (page: number) => {
|
||||||
currentPage.value = page
|
currentPage.value = page
|
||||||
pagination.page = page
|
|
||||||
fetchTransferredResources()
|
fetchTransferredResources()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePageSizeChange = (size: number) => {
|
const handlePageSizeChange = (size: number) => {
|
||||||
pageSize.value = size
|
pageSize.value = size
|
||||||
pagination.pageSize = size
|
|
||||||
currentPage.value = 1
|
currentPage.value = 1
|
||||||
pagination.page = 1
|
|
||||||
fetchTransferredResources()
|
fetchTransferredResources()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,3 +261,12 @@ onMounted(() => {
|
|||||||
fetchTransferredResources()
|
fetchTransferredResources()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.line-clamp-1 {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-4">
|
<div class="h-full flex flex-col gap-2">
|
||||||
<!-- 搜索和筛选 -->
|
<!-- 搜索和筛选 -->
|
||||||
<div class="grid grid-cols-1 md:grid-cols-5 gap-4">
|
<div class="flex-0 grid grid-cols-1 md:grid-cols-5 gap-4">
|
||||||
<n-input
|
<n-input
|
||||||
v-model:value="searchQuery"
|
v-model:value="searchQuery"
|
||||||
placeholder="搜索未转存资源..."
|
placeholder="搜索未转存资源..."
|
||||||
@@ -41,8 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 批量操作 -->
|
<!-- 批量操作 -->
|
||||||
<n-card>
|
<div class="flex-0 flex items-center justify-between">
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<n-checkbox
|
<n-checkbox
|
||||||
@@ -78,10 +77,9 @@
|
|||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
|
||||||
|
|
||||||
<!-- 资源列表 -->
|
<!-- 资源列表 -->
|
||||||
<n-card>
|
<div class="flex-1 h-1">
|
||||||
<div v-if="loading" class="flex items-center justify-center py-8">
|
<div v-if="loading" class="flex items-center justify-center py-8">
|
||||||
<n-spin size="large" />
|
<n-spin size="large" />
|
||||||
</div>
|
</div>
|
||||||
@@ -91,13 +89,12 @@
|
|||||||
<p class="text-gray-500">暂无未转存的夸克资源</p>
|
<p class="text-gray-500">暂无未转存的夸克资源</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else class="h-full">
|
||||||
<!-- 虚拟列表 -->
|
<!-- 虚拟列表 -->
|
||||||
<n-virtual-list
|
<n-virtual-list
|
||||||
:items="resources"
|
:items="resources"
|
||||||
:item-size="120"
|
:item-size="120"
|
||||||
style="max-height: 500px"
|
class="h-full"
|
||||||
container-style="height: 500px;"
|
|
||||||
>
|
>
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<div class="p-4 border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800">
|
<div class="p-4 border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800">
|
||||||
@@ -167,9 +164,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</n-virtual-list>
|
</n-virtual-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 分页 -->
|
</div>
|
||||||
<div class="mt-4 flex justify-center">
|
<div class="flex-0">
|
||||||
|
<div class="flex justify-center">
|
||||||
<n-pagination
|
<n-pagination
|
||||||
v-model:page="currentPage"
|
v-model:page="currentPage"
|
||||||
v-model:page-size="pageSize"
|
v-model:page-size="pageSize"
|
||||||
@@ -182,7 +181,6 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
|
||||||
|
|
||||||
<!-- 网盘账号选择模态框 -->
|
<!-- 网盘账号选择模态框 -->
|
||||||
<n-modal v-model:show="showAccountSelectionModal" preset="card" title="选择网盘账号" style="width: 600px">
|
<n-modal v-model:show="showAccountSelectionModal" preset="card" title="选择网盘账号" style="width: 600px">
|
||||||
|
|||||||
80
web/components/AdminPageLayout.vue
Normal file
80
web/components/AdminPageLayout.vue
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h-full flex flex-col gap-3">
|
||||||
|
<!-- 顶部标题和按钮区域 -->
|
||||||
|
<div class="flex-0 w-full flex">
|
||||||
|
<div v-if="isSubPage" class="flex-0 mr-4 flex items-center">
|
||||||
|
<n-button @click="goBack" type="text" size="small">
|
||||||
|
<template #icon>
|
||||||
|
<i class="fas fa-arrow-left"></i>
|
||||||
|
</template>
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
<!-- 页面头部内容 -->
|
||||||
|
<div class="flex-1 w-1 flex items-center justify-between">
|
||||||
|
<slot name="page-header"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 通知提示区域 -->
|
||||||
|
<div v-if="hasNoticeSection" class="flex-shrink-0">
|
||||||
|
<slot name="notice-section"></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 过滤栏区域 -->
|
||||||
|
<div v-if="hasFilterBar" class="flex-shrink-0">
|
||||||
|
<slot name="filter-bar"></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 内容区 - 自适应剩余高度 -->
|
||||||
|
<div class="flex-1 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden flex flex-col">
|
||||||
|
<!-- 内容区header -->
|
||||||
|
<div v-if="hasContentHeader" class="px-6 py-4 bg-gray-50 dark:bg-gray-700 border-b border-gray-200 dark:border-gray-700 whitespace-nowrap">
|
||||||
|
<slot name="content-header"></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 内容区content - 自适应剩余高度 -->
|
||||||
|
<div class="flex-1 h-1 content-slot overflow-hidden">
|
||||||
|
<slot name="content"></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 内容区footer -->
|
||||||
|
<div v-if="hasContentFooter" class="flex-shrink-0 bg-gray-50 dark:bg-gray-700 border-t border-gray-200 dark:border-gray-700">
|
||||||
|
<slot name="content-footer"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const router = useRouter()
|
||||||
|
const $slots = useSlots()
|
||||||
|
const hasNoticeSection = computed(() => $slots['notice-section'] !== undefined)
|
||||||
|
const hasFilterBar = computed(() => $slots['filter-bar'] !== undefined)
|
||||||
|
const hasContentHeader = computed(() => $slots['content-header'] !== undefined)
|
||||||
|
const hasContentFooter = computed(() => $slots['content-footer'] !== undefined)
|
||||||
|
|
||||||
|
const goBack = () => {
|
||||||
|
try {
|
||||||
|
router.back()
|
||||||
|
} catch (error) {
|
||||||
|
navigateTo('/admin')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
minHeight: {
|
||||||
|
type: String,
|
||||||
|
default: '400px'
|
||||||
|
},
|
||||||
|
isSubPage: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
:deep(.content-slot) {
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="min-h-screen bg-gray-50 dark:bg-gray-900">
|
<div class="min-h-screen max-h-screen overflow-hidden bg-gray-50 dark:bg-gray-900">
|
||||||
<!-- 设置通用title -->
|
<!-- 设置通用title -->
|
||||||
<Head>
|
<Head>
|
||||||
<title>管理后台 - 老九网盘资源数据库</title>
|
<title>管理后台 - 老九网盘资源数据库</title>
|
||||||
@@ -129,9 +129,9 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- 侧边栏和主内容区域 -->
|
<!-- 侧边栏和主内容区域 -->
|
||||||
<div class="flex">
|
<div class="flex main-content">
|
||||||
<!-- 侧边栏 -->
|
<!-- 侧边栏 -->
|
||||||
<aside class="w-64 bg-white dark:bg-gray-800 shadow-sm border-r border-gray-200 dark:border-gray-700 min-h-screen">
|
<aside class="w-64 bg-white dark:bg-gray-800 shadow-sm border-r border-gray-200 dark:border-gray-700 h-full overflow-y-auto">
|
||||||
<nav class="mt-8">
|
<nav class="mt-8">
|
||||||
<div class="px-4 space-y-6">
|
<div class="px-4 space-y-6">
|
||||||
<!-- 仪表盘 -->
|
<!-- 仪表盘 -->
|
||||||
@@ -273,7 +273,7 @@
|
|||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<!-- 主内容区域 -->
|
<!-- 主内容区域 -->
|
||||||
<main class="flex-1 p-8">
|
<main class="flex-1 p-4 h-full overflow-y-auto">
|
||||||
<ClientOnly>
|
<ClientOnly>
|
||||||
<n-message-provider>
|
<n-message-provider>
|
||||||
<n-notification-provider>
|
<n-notification-provider>
|
||||||
@@ -304,9 +304,7 @@ const systemConfigStore = useSystemConfigStore()
|
|||||||
// 任务状态管理
|
// 任务状态管理
|
||||||
const taskStore = useTaskStore()
|
const taskStore = useTaskStore()
|
||||||
|
|
||||||
// 初始化系统配置(管理员页面使用管理员API)
|
systemConfigStore.initConfig(false, true).catch(console.error)
|
||||||
// 在setup阶段初始化,确保数据可用
|
|
||||||
await systemConfigStore.initConfig(false, true)
|
|
||||||
|
|
||||||
// 版本信息
|
// 版本信息
|
||||||
const versionInfo = ref({
|
const versionInfo = ref({
|
||||||
@@ -331,7 +329,15 @@ onMounted(() => {
|
|||||||
|
|
||||||
// 启动任务状态自动更新
|
// 启动任务状态自动更新
|
||||||
taskStore.startAutoUpdate()
|
taskStore.startAutoUpdate()
|
||||||
console.log('Admin layout: 任务状态自动更新已启动')
|
|
||||||
|
// 确保在客户端配置被正确载入(防止SSR水合问题)
|
||||||
|
setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
await systemConfigStore.initConfig(true, true) // 强制刷新,防止SSR水合问题
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Admin layout: onMounted 配置刷新失败', error)
|
||||||
|
}
|
||||||
|
}, 100) // 延迟100ms,确保组件渲染完成
|
||||||
})
|
})
|
||||||
|
|
||||||
// 组件销毁时清理任务状态管理
|
// 组件销毁时清理任务状态管理
|
||||||
@@ -344,9 +350,6 @@ onBeforeUnmount(() => {
|
|||||||
// 系统配置
|
// 系统配置
|
||||||
const systemConfig = computed(() => {
|
const systemConfig = computed(() => {
|
||||||
const config = systemConfigStore.config || {}
|
const config = systemConfigStore.config || {}
|
||||||
console.log('顶部导航系统配置:', config)
|
|
||||||
console.log('自动处理状态:', config.auto_process_ready_resources)
|
|
||||||
console.log('自动转存状态:', config.auto_transfer_enabled)
|
|
||||||
return config
|
return config
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -605,4 +608,7 @@ const navigateToTasks = () => {
|
|||||||
font-family: 'Font Awesome 6 Free';
|
font-family: 'Font Awesome 6 Free';
|
||||||
font-weight: 900;
|
font-weight: 900;
|
||||||
}
|
}
|
||||||
|
.main-content {
|
||||||
|
height: calc(100vh - 85px);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="max-w-7xl mx-auto space-y-6">
|
<AdminPageLayout :is-sub-page="true">
|
||||||
<!-- 页面标题 -->
|
<!-- 页面头部 - 标题 -->
|
||||||
<div class="flex items-center justify-between">
|
<template #page-header>
|
||||||
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">账号扩容管理</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">账号扩容管理</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">管理账号扩容任务和状态</p>
|
<p class="text-gray-600 dark:text-gray-400">管理账号扩容任务和状态</p>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 提示信息 -->
|
<!-- 通知提示区域 - 扩容说明Alert -->
|
||||||
|
<template #notice-section>
|
||||||
<n-alert type="info" :show-icon="false">
|
<n-alert type="info" :show-icon="false">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<i class="fas fa-info-circle text-blue-500"></i>
|
<i class="fas fa-info-circle text-blue-500"></i>
|
||||||
@@ -19,7 +22,104 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</n-alert>
|
</n-alert>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 内容区header - 账号列表标题 -->
|
||||||
|
<template #content-header>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-lg font-semibold">支持扩容的账号列表</span>
|
||||||
|
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
共 {{ expansionAccounts.length }} 个账号
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 内容区content - 账号列表详情 -->
|
||||||
|
<template #content>
|
||||||
|
<div class="flex-1 h-full overflow-hidden">
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<div v-if="loading" class="flex items-center justify-center py-12">
|
||||||
|
<n-spin size="large">
|
||||||
|
<template #description>
|
||||||
|
<span class="text-gray-500">加载中...</span>
|
||||||
|
</template>
|
||||||
|
</n-spin>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<div v-else-if="expansionAccounts.length === 0" class="flex flex-col items-center justify-center py-12">
|
||||||
|
<n-empty description="暂无可扩容的账号,请先添加有效的 quark 账号">
|
||||||
|
<template #icon>
|
||||||
|
<i class="fas fa-user-circle text-4xl text-gray-400"></i>
|
||||||
|
</template>
|
||||||
|
</n-empty>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 账号列表内容的虚拟滚动列表 -->
|
||||||
|
<div v-else class="h-full">
|
||||||
|
<n-virtual-list class="h-full" :items="expansionAccounts" :item-size="80">
|
||||||
|
<template #default="{ item }">
|
||||||
|
<div class="border-b border-gray-200 dark:border-gray-700 p-4 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<!-- 左侧信息 -->
|
||||||
|
<div class="flex-1 min-w-0">
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
<!-- 平台图标 -->
|
||||||
|
<span v-html="getPlatformIcon(item.service_type === 'quark' ? '夸克网盘' : '其他')" class="text-lg"></span>
|
||||||
|
|
||||||
|
<!-- 账号信息 -->
|
||||||
|
<div class="flex-1 min-w-0">
|
||||||
|
<h3 class="text-sm font-medium text-gray-900 dark:text-gray-100 line-clamp-1">
|
||||||
|
{{ item.name }}
|
||||||
|
</h3>
|
||||||
|
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
{{ item.service_type === 'quark' ? '夸克网盘' : '其他平台' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 扩容状态 -->
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<n-tag v-if="item.expanded" type="success" size="small">
|
||||||
|
已扩容
|
||||||
|
</n-tag>
|
||||||
|
<n-tag v-else type="warning" size="small">
|
||||||
|
可扩容
|
||||||
|
</n-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 创建时间 -->
|
||||||
|
<div class="mt-2">
|
||||||
|
<span class="text-xs text-gray-600 dark:text-gray-400">
|
||||||
|
创建时间: {{ formatDate(item.created_at) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧操作按钮 -->
|
||||||
|
<div class="flex items-center space-x-2 ml-4">
|
||||||
|
<n-button
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
:disabled="item.expanded"
|
||||||
|
:loading="expandingAccountId === item.id"
|
||||||
|
@click="handleExpansion(item)"
|
||||||
|
>
|
||||||
|
<template #icon>
|
||||||
|
<i class="fas fa-expand"></i>
|
||||||
|
</template>
|
||||||
|
{{ item.expanded ? '已扩容' : '扩容' }}
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</n-virtual-list>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</AdminPageLayout>
|
||||||
|
<!-- 抽屉和模态框 - 移动到AdminPageLayout外部 -->
|
||||||
<n-drawer v-model:show="drawActive" :width="502" closable placement="right">
|
<n-drawer v-model:show="drawActive" :width="502" closable placement="right">
|
||||||
<n-drawer-content title="扩容说明">
|
<n-drawer-content title="扩容说明">
|
||||||
<div class="space-y-6 p-4">
|
<div class="space-y-6 p-4">
|
||||||
@@ -115,110 +215,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</n-card>
|
</n-card>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
|
|
||||||
<!-- 账号列表 -->
|
|
||||||
<n-card>
|
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<span class="text-lg font-semibold">支持扩容的账号列表</span>
|
|
||||||
<div class="text-sm text-gray-500">
|
|
||||||
共 {{ expansionAccounts.length }} 个账号
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- 加载状态 -->
|
|
||||||
<div v-if="loading" class="flex items-center justify-center py-12">
|
|
||||||
<n-spin size="large">
|
|
||||||
<template #description>
|
|
||||||
<span class="text-gray-500">加载中...</span>
|
|
||||||
</template>
|
|
||||||
</n-spin>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 空状态 -->
|
|
||||||
<div v-else-if="expansionAccounts.length === 0" class="flex flex-col items-center justify-center py-12">
|
|
||||||
<n-empty description="暂无可扩容的账号,请先添加有效的 quark 账号">
|
|
||||||
<template #icon>
|
|
||||||
<i class="fas fa-user-circle text-4xl text-gray-400"></i>
|
|
||||||
</template>
|
|
||||||
</n-empty>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 账号列表 -->
|
|
||||||
<div v-else>
|
|
||||||
<n-virtual-list :items="expansionAccounts" :item-size="80" style="max-height: 500px">
|
|
||||||
<template #default="{ item }">
|
|
||||||
<div class="border-b border-gray-200 dark:border-gray-700 p-4 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors">
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<!-- 左侧信息 -->
|
|
||||||
<div class="flex-1 min-w-0">
|
|
||||||
<div class="flex items-center space-x-4">
|
|
||||||
<!-- 平台图标 -->
|
|
||||||
<span v-html="getPlatformIcon(item.service_type === 'quark' ? '夸克网盘' : '其他')" class="text-lg"></span>
|
|
||||||
|
|
||||||
<!-- 账号信息 -->
|
|
||||||
<div class="flex-1 min-w-0">
|
|
||||||
<h3 class="text-sm font-medium text-gray-900 dark:text-gray-100 line-clamp-1">
|
|
||||||
{{ item.name }}
|
|
||||||
</h3>
|
|
||||||
<p class="text-xs text-gray-500">
|
|
||||||
{{ item.service_type === 'quark' ? '夸克网盘' : '其他平台' }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 扩容状态 -->
|
|
||||||
<div class="flex items-center space-x-2">
|
|
||||||
<n-tag v-if="item.expanded" type="success" size="small">
|
|
||||||
已扩容
|
|
||||||
</n-tag>
|
|
||||||
<n-tag v-else type="warning" size="small">
|
|
||||||
可扩容
|
|
||||||
</n-tag>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 创建时间 -->
|
|
||||||
<div class="mt-2">
|
|
||||||
<span class="text-xs text-gray-600 dark:text-gray-400">
|
|
||||||
创建时间: {{ formatDate(item.created_at) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 右侧操作按钮 -->
|
|
||||||
<div class="flex items-center space-x-2 ml-4">
|
|
||||||
<n-button
|
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
:disabled="item.expanded"
|
|
||||||
:loading="expandingAccountId === item.id"
|
|
||||||
@click="handleExpansion(item)"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<i class="fas fa-expand"></i>
|
|
||||||
</template>
|
|
||||||
{{ item.expanded ? '已扩容' : '扩容' }}
|
|
||||||
</n-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</n-virtual-list>
|
|
||||||
</div>
|
|
||||||
</n-card>
|
|
||||||
|
|
||||||
<!-- 扩容任务列表 -->
|
|
||||||
<n-card v-if="expansionTasks.length > 0" title="扩容任务列表">
|
|
||||||
<n-data-table
|
|
||||||
:columns="taskColumns"
|
|
||||||
:data="expansionTasks"
|
|
||||||
:pagination="false"
|
|
||||||
max-height="400"
|
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
</n-card>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -233,7 +229,6 @@ import { useNotification, useDialog } from 'naive-ui'
|
|||||||
|
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
const expansionAccounts = ref([])
|
const expansionAccounts = ref([])
|
||||||
const expansionTasks = ref([])
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const expandingAccountId = ref(null)
|
const expandingAccountId = ref(null)
|
||||||
const drawActive = ref(false) // 侧边栏激活
|
const drawActive = ref(false) // 侧边栏激活
|
||||||
@@ -248,62 +243,6 @@ const pendingAccount = ref<any>(null) // 待处理的账号
|
|||||||
const taskApi = useTaskApi()
|
const taskApi = useTaskApi()
|
||||||
const notification = useNotification()
|
const notification = useNotification()
|
||||||
|
|
||||||
// 表格列配置
|
|
||||||
const taskColumns = [
|
|
||||||
{
|
|
||||||
title: '任务ID',
|
|
||||||
key: 'id',
|
|
||||||
width: 80
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '标题',
|
|
||||||
key: 'title',
|
|
||||||
ellipsis: {
|
|
||||||
tooltip: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '状态',
|
|
||||||
key: 'status',
|
|
||||||
width: 100,
|
|
||||||
render: (row: any) => {
|
|
||||||
const statusMap = {
|
|
||||||
pending: { color: 'warning', text: '等待中', icon: 'fas fa-clock' },
|
|
||||||
running: { color: 'info', text: '运行中', icon: 'fas fa-spinner fa-spin' },
|
|
||||||
completed: { color: 'success', text: '已完成', icon: 'fas fa-check' },
|
|
||||||
failed: { color: 'error', text: '失败', icon: 'fas fa-times' }
|
|
||||||
}
|
|
||||||
const status = statusMap[row.status as keyof typeof statusMap] || statusMap.failed
|
|
||||||
return h('n-tag', { type: status.color }, {
|
|
||||||
icon: () => h('i', { class: status.icon }),
|
|
||||||
default: () => status.text
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '创建时间',
|
|
||||||
key: 'created_at',
|
|
||||||
width: 150,
|
|
||||||
render: (row: any) => formatDate(row.created_at)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
key: 'actions',
|
|
||||||
width: 150,
|
|
||||||
render: (row: any) => h('div', { class: 'flex space-x-2' }, [
|
|
||||||
h('n-button', {
|
|
||||||
size: 'small',
|
|
||||||
type: 'primary',
|
|
||||||
onClick: () => viewTaskDetails(row.id)
|
|
||||||
}, '详情'),
|
|
||||||
row.status === 'running' ? h('n-button', {
|
|
||||||
size: 'small',
|
|
||||||
type: 'warning',
|
|
||||||
onClick: () => stopTask(row.id)
|
|
||||||
}, '停止') : null
|
|
||||||
].filter(Boolean))
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// 获取支持扩容的账号列表
|
// 获取支持扩容的账号列表
|
||||||
const fetchExpansionAccounts = async () => {
|
const fetchExpansionAccounts = async () => {
|
||||||
@@ -323,15 +262,6 @@ const fetchExpansionAccounts = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取扩容任务列表
|
|
||||||
const fetchExpansionTasks = async () => {
|
|
||||||
try {
|
|
||||||
const response = await taskApi.getTasks({ taskType: 'expansion' })
|
|
||||||
expansionTasks.value = response.tasks || []
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取扩容任务列表失败:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理扩容操作
|
// 处理扩容操作
|
||||||
const handleExpansion = async (account) => {
|
const handleExpansion = async (account) => {
|
||||||
@@ -366,11 +296,12 @@ const confirmDataSourceSelection = async () => {
|
|||||||
duration: 3000
|
duration: 3000
|
||||||
})
|
})
|
||||||
|
|
||||||
|
navigateTo('/admin/tasks')
|
||||||
// 刷新数据
|
// 刷新数据
|
||||||
await Promise.all([
|
// await Promise.all([
|
||||||
fetchExpansionAccounts(),
|
// fetchExpansionAccounts(),
|
||||||
fetchExpansionTasks()
|
// fetchExpansionTasks()
|
||||||
])
|
// ])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('创建扩容任务失败:', error)
|
console.error('创建扩容任务失败:', error)
|
||||||
notification.error({
|
notification.error({
|
||||||
@@ -384,52 +315,7 @@ const confirmDataSourceSelection = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看任务详情
|
|
||||||
const viewTaskDetails = async (taskId) => {
|
|
||||||
try {
|
|
||||||
const status = await taskApi.getTaskStatus(taskId)
|
|
||||||
console.log('任务详情:', status)
|
|
||||||
|
|
||||||
// 这里可以展示任务详情的模态框
|
|
||||||
notification.info({
|
|
||||||
title: '任务详情',
|
|
||||||
content: `任务状态: ${status.status}, 总项目数: ${status.total_items}`,
|
|
||||||
duration: 5000
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取任务详情失败:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 停止任务
|
|
||||||
const stopTask = async (taskId) => {
|
|
||||||
const dialog = useDialog()
|
|
||||||
|
|
||||||
dialog.warning({
|
|
||||||
title: '确认停止',
|
|
||||||
content: '确定要停止这个扩容任务吗?',
|
|
||||||
positiveText: '确定',
|
|
||||||
negativeText: '取消',
|
|
||||||
onPositiveClick: async () => {
|
|
||||||
try {
|
|
||||||
await taskApi.stopTask(taskId)
|
|
||||||
notification.success({
|
|
||||||
title: '成功',
|
|
||||||
content: '任务已停止',
|
|
||||||
duration: 3000
|
|
||||||
})
|
|
||||||
await fetchExpansionTasks()
|
|
||||||
} catch (error) {
|
|
||||||
console.error('停止任务失败:', error)
|
|
||||||
notification.error({
|
|
||||||
title: '失败',
|
|
||||||
content: '停止任务失败',
|
|
||||||
duration: 3000
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取平台图标
|
// 获取平台图标
|
||||||
const getPlatformIcon = (platformName) => {
|
const getPlatformIcon = (platformName) => {
|
||||||
@@ -455,10 +341,7 @@ const formatDate = (dateString) => {
|
|||||||
|
|
||||||
// 页面加载
|
// 页面加载
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await Promise.all([
|
await fetchExpansionAccounts()
|
||||||
fetchExpansionAccounts(),
|
|
||||||
fetchExpansionTasks()
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<AdminPageLayout>
|
||||||
<!-- 页面标题 -->
|
<!-- 页面头部 - 标题和按钮 -->
|
||||||
<div class="flex items-center justify-between">
|
<template #page-header>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">账号管理</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">账号管理</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">管理平台账号信息</p>
|
<p class="text-gray-600 dark:text-gray-400">管理平台账号信息</p>
|
||||||
@@ -26,10 +26,11 @@
|
|||||||
刷新
|
刷新
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<!-- 搜索和筛选 -->
|
<!-- 过滤栏 - 搜索和筛选 -->
|
||||||
<n-card>
|
<template #filter-bar>
|
||||||
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4">
|
||||||
<div class="flex flex-col md:flex-row gap-4">
|
<div class="flex flex-col md:flex-row gap-4">
|
||||||
<n-input v-model:value="searchQuery" placeholder="搜索账号..." clearable class="flex-1">
|
<n-input v-model:value="searchQuery" placeholder="搜索账号..." clearable class="flex-1">
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
@@ -47,20 +48,21 @@
|
|||||||
搜索
|
搜索
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 账号列表 -->
|
<!-- 内容区header - 账号列表头部 -->
|
||||||
<n-card>
|
<template #content-header>
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-lg font-semibold">账号列表</span>
|
<span class="text-lg font-semibold text-gray-900 dark:text-white">账号列表</span>
|
||||||
<div class="text-sm text-gray-500">
|
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||||
共 {{ filteredCksList.length }} 个账号
|
共 {{ filteredCksList.length }} 个账号
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 加载状态 -->
|
<!-- 内容区content - 账号列表表格 -->
|
||||||
|
<template #content>
|
||||||
<div v-if="loading" class="flex items-center justify-center py-12">
|
<div v-if="loading" class="flex items-center justify-center py-12">
|
||||||
<n-spin size="large">
|
<n-spin size="large">
|
||||||
<template #description>
|
<template #description>
|
||||||
@@ -69,7 +71,6 @@
|
|||||||
</n-spin>
|
</n-spin>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
|
||||||
<div v-else-if="filteredCksList.length === 0" class="flex flex-col items-center justify-center py-12">
|
<div v-else-if="filteredCksList.length === 0" class="flex flex-col items-center justify-center py-12">
|
||||||
<n-empty description="暂无账号">
|
<n-empty description="暂无账号">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
@@ -86,9 +87,9 @@
|
|||||||
</n-empty>
|
</n-empty>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 账号列表 -->
|
<!-- 账号列表和分页 -->
|
||||||
<div v-else>
|
<div v-else class="flex flex-col flex-1 h-full overflow-hidden">
|
||||||
<n-virtual-list :items="filteredCksList" :item-size="100" style="max-height: 500px">
|
<n-virtual-list :items="filteredCksList" :item-size="100" class="h-full">
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<div
|
<div
|
||||||
class="border-b border-gray-200 dark:border-gray-700 p-4 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors">
|
class="border-b border-gray-200 dark:border-gray-700 p-4 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors">
|
||||||
@@ -123,13 +124,13 @@
|
|||||||
<n-tag :type="item.is_valid ? 'success' : 'error'" size="small">
|
<n-tag :type="item.is_valid ? 'success' : 'error'" size="small">
|
||||||
{{ item.is_valid ? '有效' : '无效' }}
|
{{ item.is_valid ? '有效' : '无效' }}
|
||||||
</n-tag>
|
</n-tag>
|
||||||
<span class="text-xs text-gray-500">
|
<span class="text-xs text-gray-500 dark:text-gray-400">
|
||||||
总空间: {{ formatFileSize(item.space) }}
|
总空间: {{ formatFileSize(item.space) }}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-xs text-gray-500">
|
<span class="text-xs text-gray-500 dark:text-gray-400">
|
||||||
已使用: {{ formatFileSize(Math.max(0, item.used_space || (item.space - item.left_space))) }}
|
已使用: {{ formatFileSize(Math.max(0, item.used_space || (item.space - item.left_space))) }}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-xs text-gray-500">
|
<span class="text-xs text-gray-500 dark:text-gray-400">
|
||||||
剩余: {{ formatFileSize(Math.max(0, item.left_space)) }}
|
剩余: {{ formatFileSize(Math.max(0, item.left_space)) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -171,15 +172,19 @@
|
|||||||
</template>
|
</template>
|
||||||
</n-virtual-list>
|
</n-virtual-list>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</template>
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 内容区footer - 分页组件 -->
|
||||||
|
<template #content-footer>
|
||||||
|
<div class="p-4">
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<n-pagination v-model:page="currentPage" v-model:page-size="itemsPerPage" :item-count="filteredCksList.length"
|
<n-pagination v-model:page="currentPage" v-model:page-size="itemsPerPage" :item-count="filteredCksList.length"
|
||||||
:page-sizes="[10, 20, 50, 100]" show-size-picker @update:page="goToPage"
|
:page-sizes="[10, 20, 50, 100]" show-size-picker @update:page="goToPage"
|
||||||
@update:page-size="(size) => { itemsPerPage = size; currentPage = 1; }" />
|
@update:page-size="(size) => { itemsPerPage = size; currentPage = 1; }" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</AdminPageLayout>
|
||||||
|
|
||||||
<!-- 创建/编辑账号模态框 -->
|
<!-- 创建/编辑账号模态框 -->
|
||||||
<n-modal :show="showCreateModal || showEditModal" preset="card" title="账号管理" style="width: 500px"
|
<n-modal :show="showCreateModal || showEditModal" preset="card" title="账号管理" style="width: 500px"
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<AdminPageLayout>
|
||||||
<!-- 页面标题 -->
|
<!-- 页面头部 - 标题 -->
|
||||||
<div class="flex items-center justify-between">
|
<template #page-header>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">机器人管理</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">机器人管理</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">管理各平台的机器人配置和自动回复功能</p>
|
<p class="text-gray-600 dark:text-gray-400">管理各平台的机器人配置和自动回复功能</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<!-- 配置表单 -->
|
<!-- 内容区 - 配置表单 -->
|
||||||
<n-card>
|
<template #content>
|
||||||
|
<div class="config-content h-full">
|
||||||
<!-- 顶部Tabs -->
|
<!-- 顶部Tabs -->
|
||||||
<n-tabs
|
<n-tabs
|
||||||
v-model:value="activeTab"
|
v-model:value="activeTab"
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
class="mb-6"
|
class="mb-6"
|
||||||
>
|
>
|
||||||
<n-tab-pane name="qq" tab="QQ机器人">
|
<n-tab-pane name="qq" tab="QQ机器人">
|
||||||
|
<div class="tab-content-container">
|
||||||
<div class="space-y-8">
|
<div class="space-y-8">
|
||||||
<!-- 步骤1:Astrobot 安装指南 -->
|
<!-- 步骤1:Astrobot 安装指南 -->
|
||||||
<div class="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-6">
|
<div class="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-6">
|
||||||
@@ -87,10 +89,8 @@
|
|||||||
• 自动格式化搜索结果<br>
|
• 自动格式化搜索结果<br>
|
||||||
• 支持超时时间配置<br><br>
|
• 支持超时时间配置<br><br>
|
||||||
<strong>安装步骤:</strong><br>
|
<strong>安装步骤:</strong><br>
|
||||||
1. 下载插件文件<br>
|
1. Astrbot 插件市场, 搜索 urldb 安装<br>
|
||||||
2. 将插件放入 Astrobot 的 plugins 目录<br>
|
2. 根据下面的配置信息配置插件
|
||||||
3. 重启 Astrobot<br>
|
|
||||||
4. 在配置文件中添加插件配置
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -158,37 +158,46 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<n-tab-pane name="wechat" tab="微信公众号">
|
<n-tab-pane name="wechat" tab="微信公众号">
|
||||||
|
<div class="tab-content-container">
|
||||||
<div class="text-center py-12">
|
<div class="text-center py-12">
|
||||||
<i class="fas fa-lock text-4xl text-gray-400 mb-4"></i>
|
<i class="fas fa-lock text-4xl text-gray-400 mb-4"></i>
|
||||||
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">功能暂未开放</h3>
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">功能暂未开放</h3>
|
||||||
<p class="text-gray-500 dark:text-gray-400">微信公众号机器人功能正在开发中,敬请期待</p>
|
<p class="text-gray-500 dark:text-gray-400">微信公众号机器人功能正在开发中,敬请期待</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<n-tab-pane name="telegram" tab="Telegram机器人">
|
<n-tab-pane name="telegram" tab="Telegram机器人">
|
||||||
|
<div class="tab-content-container">
|
||||||
<div class="text-center py-12">
|
<div class="text-center py-12">
|
||||||
<i class="fas fa-lock text-4xl text-gray-400 mb-4"></i>
|
<i class="fas fa-lock text-4xl text-gray-400 mb-4"></i>
|
||||||
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">功能暂未开放</h3>
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">功能暂未开放</h3>
|
||||||
<p class="text-gray-500 dark:text-gray-400">Telegram机器人功能正在开发中,敬请期待</p>
|
<p class="text-gray-500 dark:text-gray-400">Telegram机器人功能正在开发中,敬请期待</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<n-tab-pane name="wechat_open" tab="微信开放平台">
|
<n-tab-pane name="wechat_open" tab="微信开放平台">
|
||||||
|
<div class="tab-content-container">
|
||||||
<div class="text-center py-12">
|
<div class="text-center py-12">
|
||||||
<i class="fas fa-lock text-4xl text-gray-400 mb-4"></i>
|
<i class="fas fa-lock text-4xl text-gray-400 mb-4"></i>
|
||||||
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">功能暂未开放</h3>
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">功能暂未开放</h3>
|
||||||
<p class="text-gray-500 dark:text-gray-400">微信开放平台机器人功能正在开发中,敬请期待</p>
|
<p class="text-gray-500 dark:text-gray-400">微信开放平台机器人功能正在开发中,敬请期待</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
</n-tabs>
|
</n-tabs>
|
||||||
</n-card>
|
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</AdminPageLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import AdminPageLayout from '~/components/AdminPageLayout.vue'
|
||||||
import { useConfigChangeDetection } from '~/composables/useConfigChangeDetection'
|
import { useConfigChangeDetection } from '~/composables/useConfigChangeDetection'
|
||||||
|
|
||||||
// 设置页面布局
|
// 设置页面布局
|
||||||
@@ -278,5 +287,21 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 自定义样式 */
|
/* 机器人管理页面样式 */
|
||||||
|
|
||||||
|
.config-content {
|
||||||
|
padding: 8px;
|
||||||
|
background-color: var(--color-white, #ffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .config-content {
|
||||||
|
background-color: var(--color-dark-bg, #1f2937);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tab内容容器 - 个别内容滚动 */
|
||||||
|
.tab-content-container {
|
||||||
|
height: calc(100vh - 240px);
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,32 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<AdminPageLayout>
|
||||||
<!-- 页面标题 -->
|
<!-- 页面头部 - 标题和按钮 -->
|
||||||
<div class="flex items-center justify-between">
|
<template #page-header>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">分类管理</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">分类管理</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">管理系统中的资源分类</p>
|
<p class="text-gray-600 dark:text-gray-400">管理系统中的资源分类</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex space-x-3">
|
<div class="flex space-x-3">
|
||||||
<n-button type="primary" @click="showAddModal = true">
|
|
||||||
<template #icon>
|
|
||||||
<i class="fas fa-plus"></i>
|
|
||||||
</template>
|
|
||||||
添加分类
|
|
||||||
</n-button>
|
|
||||||
<n-button @click="refreshData">
|
|
||||||
<template #icon>
|
|
||||||
<i class="fas fa-refresh"></i>
|
|
||||||
</template>
|
|
||||||
刷新
|
|
||||||
</n-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 提示信息 -->
|
<!-- 提示信息区域 -->
|
||||||
|
<template #notice-section>
|
||||||
<n-alert title="分类用于对资源进行分类管理,可以关联多个标签" type="info" />
|
<n-alert title="分类用于对资源进行分类管理,可以关联多个标签" type="info" />
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 搜索和操作 -->
|
<!-- 过滤栏 - 搜索和操作 -->
|
||||||
<n-card>
|
<template #filter-bar>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<n-button @click="showAddModal = true" type="success">
|
<n-button @click="showAddModal = true" type="success">
|
||||||
@@ -58,21 +48,24 @@
|
|||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</template>
|
||||||
|
|
||||||
<!-- 分类列表 -->
|
<!-- 内容区header - 分类列表标题 -->
|
||||||
<n-card>
|
<!-- <template #content-header>
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-lg font-semibold">分类列表</span>
|
<span class="text-lg font-semibold">分类列表</span>
|
||||||
<span class="text-sm text-gray-500">共 {{ total }} 个分类</span>
|
<span class="text-sm text-gray-500">共 {{ total }} 个分类</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template> -->
|
||||||
|
|
||||||
<div v-if="loading" class="flex items-center justify-center py-8">
|
<!-- 内容区 - 分类数据 -->
|
||||||
|
<template #content>
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<div v-if="loading" class="flex h-full items-center justify-center py-8">
|
||||||
<n-spin size="large" />
|
<n-spin size="large" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
<div v-else-if="categories.length === 0" class="text-center py-8">
|
<div v-else-if="categories.length === 0" class="text-center py-8">
|
||||||
<svg class="w-16 h-16 text-gray-300 dark:text-gray-600 mb-4 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 48 48">
|
<svg class="w-16 h-16 text-gray-300 dark:text-gray-600 mb-4 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 48 48">
|
||||||
<circle cx="24" cy="24" r="20" stroke-width="3" stroke-dasharray="6 6" />
|
<circle cx="24" cy="24" r="20" stroke-width="3" stroke-dasharray="6 6" />
|
||||||
@@ -88,19 +81,37 @@
|
|||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<!-- 数据表格 -->
|
||||||
|
<div v-else class="flex flex-col h-full min-h-[600px]">
|
||||||
|
<!-- 数据表格容器,自适应填充剩余高度 -->
|
||||||
|
<div class="flex-1 overflow-hidden">
|
||||||
<n-data-table
|
<n-data-table
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:data="categories"
|
:data="categories"
|
||||||
:pagination="pagination"
|
:pagination="false"
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
:single-line="false"
|
:single-line="false"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@update:page="handlePageChange"
|
:scroll-x="800"
|
||||||
|
class="h-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
|
||||||
|
|
||||||
|
<!-- 分页组件外部显示 -->
|
||||||
|
<div class="mt-4 flex justify-center border-t pt-4">
|
||||||
|
<n-pagination
|
||||||
|
v-model:page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:item-count="total"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
show-size-picker
|
||||||
|
@update:page="handlePageChange"
|
||||||
|
@update:page-size="(size) => { pageSize = size; currentPage = 1; fetchData() }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</AdminPageLayout>
|
||||||
<!-- 添加/编辑分类模态框 -->
|
<!-- 添加/编辑分类模态框 -->
|
||||||
<n-modal v-model:show="showAddModal" preset="card" :title="editingCategory ? '编辑分类' : '添加分类'" style="width: 500px">
|
<n-modal v-model:show="showAddModal" preset="card" :title="editingCategory ? '编辑分类' : '添加分类'" style="width: 500px">
|
||||||
<n-form
|
<n-form
|
||||||
@@ -147,7 +158,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -288,23 +298,7 @@ const columns = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
// 分页配置
|
// 分页配置已经移到模板中外部显示
|
||||||
const pagination = computed(() => ({
|
|
||||||
page: currentPage.value,
|
|
||||||
pageSize: pageSize.value,
|
|
||||||
itemCount: total.value,
|
|
||||||
showSizePicker: true,
|
|
||||||
pageSizes: [10, 20, 50, 100],
|
|
||||||
onChange: (page: number) => {
|
|
||||||
currentPage.value = page
|
|
||||||
fetchData()
|
|
||||||
},
|
|
||||||
onUpdatePageSize: (size: number) => {
|
|
||||||
pageSize.value = size
|
|
||||||
currentPage.value = 1
|
|
||||||
fetchData()
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
// 获取数据
|
// 获取数据
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
|
|||||||
@@ -1,11 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="p-6">
|
<AdminPageLayout>
|
||||||
<div class="mb-6">
|
<!-- 页面头部 - 标题 -->
|
||||||
|
<template #page-header>
|
||||||
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">数据推送</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">数据推送</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400 mt-2">数据推送管理</p>
|
<p class="text-gray-600 dark:text-gray-400">数据推送管理</p>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
<!-- 内容区 -->
|
||||||
|
<template #content>
|
||||||
<div class="text-center py-12">
|
<div class="text-center py-12">
|
||||||
<div class="text-gray-400 dark:text-gray-500 mb-4">
|
<div class="text-gray-400 dark:text-gray-500 mb-4">
|
||||||
<i class="fas fa-upload text-4xl"></i>
|
<i class="fas fa-upload text-4xl"></i>
|
||||||
@@ -13,11 +17,13 @@
|
|||||||
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">功能开发中</h3>
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white mb-2">功能开发中</h3>
|
||||||
<p class="text-gray-500 dark:text-gray-400">数据推送功能正在开发中,敬请期待...</p>
|
<p class="text-gray-500 dark:text-gray-400">数据推送功能正在开发中,敬请期待...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</AdminPageLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import AdminPageLayout from '~/components/AdminPageLayout.vue'
|
||||||
|
|
||||||
// 数据推送管理页面
|
// 数据推送管理页面
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'admin',
|
layout: 'admin',
|
||||||
|
|||||||
@@ -1,35 +1,52 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="max-w-7xl mx-auto space-y-6">
|
<AdminPageLayout>
|
||||||
<!-- 页面标题 -->
|
<!-- 页面头部 - 标题 -->
|
||||||
|
<template #page-header>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">数据转存管理</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">数据转存管理</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">管理资源转存任务和状态</p>
|
<p class="text-gray-600 dark:text-gray-400">管理资源转存任务和状态</p>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 主要内容 -->
|
<!-- 内容区 - 配置表单 -->
|
||||||
<n-card>
|
<template #content>
|
||||||
<n-tabs v-model:value="activeTab" type="line" animated>
|
<div class="config-content h-full">
|
||||||
|
<!-- 顶部Tabs -->
|
||||||
|
<n-tabs
|
||||||
|
v-model:value="activeTab"
|
||||||
|
type="line"
|
||||||
|
animated
|
||||||
|
class="mb-6"
|
||||||
|
>
|
||||||
<!-- 手动批量转存 -->
|
<!-- 手动批量转存 -->
|
||||||
<n-tab-pane name="manual" tab="手动批量转存">
|
<n-tab-pane name="manual" tab="手动批量转存">
|
||||||
|
<div class="tab-content-container">
|
||||||
<AdminManualBatchTransfer />
|
<AdminManualBatchTransfer />
|
||||||
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<!-- 已转存列表 -->
|
<!-- 已转存列表 -->
|
||||||
<n-tab-pane name="transferred" tab="已转存列表">
|
<n-tab-pane name="transferred" tab="已转存列表">
|
||||||
|
<div class="tab-content-container">
|
||||||
<AdminTransferredList ref="transferredListRef" />
|
<AdminTransferredList ref="transferredListRef" />
|
||||||
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<!-- 未转存列表 -->
|
<!-- 未转存列表 -->
|
||||||
<n-tab-pane name="untransferred" tab="未转存列表">
|
<n-tab-pane name="untransferred" tab="未转存列表">
|
||||||
|
<div class="tab-content-container">
|
||||||
<AdminUntransferredList ref="untransferredListRef" />
|
<AdminUntransferredList ref="untransferredListRef" />
|
||||||
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
</n-tabs>
|
</n-tabs>
|
||||||
</n-card>
|
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</AdminPageLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
import AdminPageLayout from '~/components/AdminPageLayout.vue'
|
||||||
|
|
||||||
// 页面配置
|
// 页面配置
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
@@ -44,3 +61,23 @@ const activeTab = ref('manual')
|
|||||||
const transferredListRef = ref(null)
|
const transferredListRef = ref(null)
|
||||||
const untransferredListRef = ref(null)
|
const untransferredListRef = ref(null)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 数据转存管理页面样式 */
|
||||||
|
|
||||||
|
.config-content {
|
||||||
|
padding: 8px;
|
||||||
|
background-color: var(--color-white, #ffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .config-content {
|
||||||
|
background-color: var(--color-dark-bg, #1f2937);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tab内容容器 - 个别内容滚动 */
|
||||||
|
.tab-content-container {
|
||||||
|
height: calc(100vh - 240px);
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<AdminPageLayout>
|
||||||
<!-- 页面标题 -->
|
<!-- 页面头部 - 标题和保存按钮 -->
|
||||||
<div class="flex items-center justify-between">
|
<template #page-header>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">开发配置</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">开发配置</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">管理API和开发相关配置</p>
|
<p class="text-gray-600 dark:text-gray-400">管理API和开发相关配置</p>
|
||||||
@@ -12,11 +12,11 @@
|
|||||||
</template>
|
</template>
|
||||||
保存配置
|
保存配置
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<!-- 配置表单 -->
|
<!-- 内容区 - 配置表单 -->
|
||||||
<n-card>
|
<template #content>
|
||||||
<div class="space-y-6">
|
<div class="config-content h-full">
|
||||||
<!-- API Token -->
|
<!-- API Token -->
|
||||||
<div>
|
<div>
|
||||||
<n-form-item label="公开API访问令牌" path="api_token">
|
<n-form-item label="公开API访问令牌" path="api_token">
|
||||||
@@ -71,12 +71,13 @@
|
|||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</template>
|
||||||
</div>
|
</AdminPageLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useConfigChangeDetection } from '~/composables/useConfigChangeDetection'
|
import { useConfigChangeDetection } from '~/composables/useConfigChangeDetection'
|
||||||
|
import AdminPageLayout from '~/components/AdminPageLayout.vue'
|
||||||
|
|
||||||
// 设置页面布局
|
// 设置页面布局
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
@@ -332,4 +333,13 @@ onMounted(() => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 自定义样式 */
|
/* 自定义样式 */
|
||||||
|
|
||||||
|
.config-content {
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: var(--color-white, #ffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .config-content {
|
||||||
|
background-color: var(--color-dark-bg, #1f2937);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<AdminPageLayout :is-sub-page="true">
|
||||||
<!-- 页面标题 -->
|
<!-- 页面头部 - 标题和按钮 -->
|
||||||
<div class="flex items-center justify-between">
|
<template #page-header>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">失败资源列表</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">失败资源列表</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">显示处理失败的资源,包含错误信息</p>
|
<p class="text-gray-600 dark:text-gray-400">显示处理失败的资源,包含错误信息</p>
|
||||||
@@ -38,19 +38,18 @@
|
|||||||
刷新
|
刷新
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<!-- 搜索和筛选 -->
|
|
||||||
<n-card>
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
||||||
|
|
||||||
|
<!-- 过滤栏 - 搜索和筛选 -->
|
||||||
|
<template #filter-bar>
|
||||||
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4">
|
||||||
|
<div class="flex flex-col md:flex-row gap-4">
|
||||||
<n-select
|
<n-select
|
||||||
v-model:value="errorFilter"
|
v-model:value="errorFilter"
|
||||||
placeholder="选择状态"
|
placeholder="选择状态"
|
||||||
:options="statusOptions"
|
:options="statusOptions"
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<n-button type="primary" @click="handleSearch" class="w-full md:w-auto md:min-w-[100px]">
|
<n-button type="primary" @click="handleSearch" class="w-full md:w-auto md:min-w-[100px]">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="fas fa-search"></i>
|
<i class="fas fa-search"></i>
|
||||||
@@ -58,11 +57,11 @@
|
|||||||
搜索
|
搜索
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 失败资源列表 -->
|
<!-- 内容区header - 失败资源列表头部 -->
|
||||||
<n-card>
|
<template #content-header>
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<span class="text-lg font-semibold">失败资源列表</span>
|
<span class="text-lg font-semibold">失败资源列表</span>
|
||||||
@@ -76,11 +75,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="text-sm text-gray-500">
|
<div class="text-sm text-gray-500">
|
||||||
<span class="text-sm text-gray-500">共 {{ totalCount }} 个资源,已选择 {{ selectedResources.length }} 个</span>
|
<span>共 {{ totalCount }} 个资源,已选择 {{ selectedResources.length }} 个</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- 内容区content - 失败资源列表 -->
|
||||||
|
<template #content>
|
||||||
<!-- 加载状态 -->
|
<!-- 加载状态 -->
|
||||||
<div v-if="loading" class="flex items-center justify-center py-12">
|
<div v-if="loading" class="flex items-center justify-center py-12">
|
||||||
<n-spin size="large">
|
<n-spin size="large">
|
||||||
@@ -91,13 +92,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 虚拟列表 -->
|
<!-- 虚拟列表 -->
|
||||||
|
<div v-else-if="failedResources.length > 0">
|
||||||
<n-virtual-list
|
<n-virtual-list
|
||||||
v-if="!loading"
|
|
||||||
:items="failedResources"
|
:items="failedResources"
|
||||||
:item-size="80"
|
:item-size="120"
|
||||||
:item-resizable="true"
|
:item-resizable="true"
|
||||||
style="max-height: 400px"
|
style="max-height: 600px"
|
||||||
container-style="height: 600px;"
|
|
||||||
>
|
>
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<div class="border-b border-gray-200 dark:border-gray-700 p-4 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors">
|
<div class="border-b border-gray-200 dark:border-gray-700 p-4 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors">
|
||||||
@@ -194,9 +194,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</n-virtual-list>
|
</n-virtual-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
<!-- 空状态 -->
|
||||||
<div v-if="!loading && failedResources.length === 0" class="flex flex-col items-center justify-center py-12">
|
<div v-else class="flex flex-col items-center justify-center py-12">
|
||||||
<n-empty description="暂无失败资源">
|
<n-empty description="暂无失败资源">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="fas fa-check-circle text-4xl text-green-500"></i>
|
<i class="fas fa-check-circle text-4xl text-green-500"></i>
|
||||||
@@ -206,9 +207,12 @@
|
|||||||
</template>
|
</template>
|
||||||
</n-empty>
|
</n-empty>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 内容区footer - 分页组件 -->
|
||||||
<div class="mt-6 flex justify-center">
|
<template #content-footer>
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="flex justify-center">
|
||||||
<n-pagination
|
<n-pagination
|
||||||
v-model:page="currentPage"
|
v-model:page="currentPage"
|
||||||
v-model:page-size="pageSize"
|
v-model:page-size="pageSize"
|
||||||
@@ -219,8 +223,9 @@
|
|||||||
@update:page-size="(size) => { pageSize = size; currentPage = 1; fetchData() }"
|
@update:page-size="(size) => { pageSize = size; currentPage = 1; fetchData() }"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</AdminPageLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<AdminPageLayout>
|
||||||
<!-- 页面标题 -->
|
<!-- 页面头部 - 标题和保存按钮 -->
|
||||||
<div class="flex items-center justify-between">
|
<template #page-header>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">功能配置</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">功能配置</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">管理系统功能开关和参数设置</p>
|
<p class="text-gray-600 dark:text-gray-400">管理系统功能开关和参数设置</p>
|
||||||
@@ -12,10 +12,11 @@
|
|||||||
</template>
|
</template>
|
||||||
保存配置
|
保存配置
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<!-- 配置表单 -->
|
<!-- 内容区 - 配置表单 -->
|
||||||
<n-card>
|
<template #content>
|
||||||
|
<div class="config-content h-full">
|
||||||
<!-- 顶部Tabs -->
|
<!-- 顶部Tabs -->
|
||||||
<n-tabs
|
<n-tabs
|
||||||
v-model:value="activeTab"
|
v-model:value="activeTab"
|
||||||
@@ -24,7 +25,7 @@
|
|||||||
class="mb-6"
|
class="mb-6"
|
||||||
>
|
>
|
||||||
<n-tab-pane name="resource" tab="资源处理">
|
<n-tab-pane name="resource" tab="资源处理">
|
||||||
|
<div class="tab-content-container">
|
||||||
<n-form
|
<n-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="configForm"
|
:model="configForm"
|
||||||
@@ -167,10 +168,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-form>
|
</n-form>
|
||||||
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<n-tab-pane name="transfer" tab="转存配置">
|
<n-tab-pane name="transfer" tab="转存配置">
|
||||||
|
<div class="tab-content-container">
|
||||||
<n-form
|
<n-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="configForm"
|
:model="configForm"
|
||||||
@@ -244,10 +246,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-form>
|
</n-form>
|
||||||
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<n-tab-pane name="drama" tab="热播剧">
|
<n-tab-pane name="drama" tab="热播剧">
|
||||||
|
<div class="tab-content-container">
|
||||||
<n-form
|
<n-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="configForm"
|
:model="configForm"
|
||||||
@@ -267,16 +270,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-form>
|
</n-form>
|
||||||
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
</n-tabs>
|
</n-tabs>
|
||||||
</n-card>
|
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</AdminPageLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { useNotification } from 'naive-ui'
|
import { useNotification } from 'naive-ui'
|
||||||
import { useConfigChangeDetection } from '~/composables/useConfigChangeDetection'
|
import { useConfigChangeDetection } from '~/composables/useConfigChangeDetection'
|
||||||
|
import AdminPageLayout from '~/components/AdminPageLayout.vue'
|
||||||
|
|
||||||
// 设置页面布局
|
// 设置页面布局
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
@@ -537,4 +543,20 @@ onMounted(() => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 自定义样式 */
|
/* 自定义样式 */
|
||||||
|
|
||||||
|
.config-content {
|
||||||
|
padding: 8px;
|
||||||
|
background-color: var(--color-white, #ffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .config-content {
|
||||||
|
background-color: var(--color-dark-bg, #1f2937);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tab内容容器 - 个别内容滚动 */
|
||||||
|
.tab-content-container {
|
||||||
|
height: calc(100vh - 240px);
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<AdminPageLayout>
|
||||||
<!-- 页面标题 -->
|
<!-- 页面头部 - 标题和按钮 -->
|
||||||
<div class="flex items-center justify-between">
|
<template #page-header>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">文件管理</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">文件管理</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">管理系统中的上传文件</p>
|
<p class="text-gray-600 dark:text-gray-400">管理系统中的上传文件</p>
|
||||||
@@ -20,13 +20,15 @@
|
|||||||
刷新
|
刷新
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<!-- 提示信息 -->
|
<!-- 提示信息区域 -->
|
||||||
|
<template #notice-section>
|
||||||
<n-alert title="支持图片格式文件,最大文件大小5MB" type="info" />
|
<n-alert title="支持图片格式文件,最大文件大小5MB" type="info" />
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 搜索和筛选 -->
|
<!-- 过滤栏 - 搜索功能 -->
|
||||||
<n-card>
|
<template #filter-bar>
|
||||||
<div class="flex gap-4">
|
<div class="flex gap-4">
|
||||||
<n-input
|
<n-input
|
||||||
v-model:value="searchKeyword"
|
v-model:value="searchKeyword"
|
||||||
@@ -47,21 +49,24 @@
|
|||||||
搜索
|
搜索
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</template>
|
||||||
|
|
||||||
<!-- 文件列表 -->
|
<!-- 内容区header - 文件列表标题 -->
|
||||||
<n-card>
|
<!-- <template #content-header>
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-lg font-semibold">文件列表</span>
|
<span class="text-lg font-semibold">文件列表</span>
|
||||||
<span class="text-sm text-gray-500">共 {{ total }} 个文件</span>
|
<span class="text-sm text-gray-500">共 {{ total }} 个文件</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template> -->
|
||||||
|
|
||||||
<div v-if="loading" class="flex items-center justify-center py-8">
|
<!-- 内容区 - 文件列表 -->
|
||||||
|
<template #content>
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<div v-if="loading" class="flex h-full items-center justify-center py-8">
|
||||||
<n-spin size="large" />
|
<n-spin size="large" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
<div v-else-if="fileList.length === 0" class="text-center py-8">
|
<div v-else-if="fileList.length === 0" class="text-center py-8">
|
||||||
<i class="fas fa-file-upload text-4xl text-gray-400 mb-4"></i>
|
<i class="fas fa-file-upload text-4xl text-gray-400 mb-4"></i>
|
||||||
<p class="text-gray-500">暂无文件数据</p>
|
<p class="text-gray-500">暂无文件数据</p>
|
||||||
@@ -73,9 +78,11 @@
|
|||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<!-- 文件网格和分页容器 -->
|
||||||
<!-- 图片预览区域 -->
|
<div v-else class="flex flex-col h-full">
|
||||||
<div class="image-preview-container">
|
<!-- 文件网格区域 - 自适应高度 -->
|
||||||
|
<div class="flex-1 overflow-auto">
|
||||||
|
<div class="file-list-container">
|
||||||
<n-image-group>
|
<n-image-group>
|
||||||
<div class="image-grid">
|
<div class="image-grid">
|
||||||
<div
|
<div
|
||||||
@@ -148,24 +155,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-image-group>
|
</n-image-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 内容区footer - 分页组件 -->
|
||||||
<div class="pagination-wrapper">
|
<template #content-footer>
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="flex justify-center">
|
||||||
<n-pagination
|
<n-pagination
|
||||||
v-model:page="pagination.page"
|
v-model:page="pagination.page"
|
||||||
v-model:page-size="pagination.pageSize"
|
v-model:page-size="pagination.pageSize"
|
||||||
:page-count="Math.ceil(pagination.total / pagination.pageSize)"
|
:item-count="pagination.total"
|
||||||
:page-sizes="pagination.pageSizes"
|
:page-sizes="[100, 200, 500, 1000]"
|
||||||
show-size-picker
|
show-size-picker
|
||||||
@update:page="handlePageChange"
|
@update:page="handlePageChange"
|
||||||
@update:page-size="handlePageSizeChange"
|
@update:page-size="handlePageSizeChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</n-card>
|
</AdminPageLayout>
|
||||||
|
<!-- 上传模态框 -->
|
||||||
<!-- 上传模态框 -->
|
|
||||||
<n-modal v-model:show="showUploadModal" preset="card" title="上传文件" style="width: 800px" @update:show="handleModalClose">
|
<n-modal v-model:show="showUploadModal" preset="card" title="上传文件" style="width: 800px" @update:show="handleModalClose">
|
||||||
<FileUpload ref="fileUploadRef" :key="uploadModalKey" />
|
<FileUpload ref="fileUploadRef" :key="uploadModalKey" />
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -191,7 +203,6 @@
|
|||||||
</n-space>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -479,12 +490,22 @@ onMounted(() => {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
/* 文件管理页面样式 */
|
/* 文件管理页面样式 */
|
||||||
|
|
||||||
|
.file-list-container {
|
||||||
|
/* 容器样式,将替换原来的n-card背景 */
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: var(--color-white, #ffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 暗色主题支持 */
|
||||||
|
.dark .file-list-container {
|
||||||
|
background-color: var(--color-dark-bg, #1f2937);
|
||||||
|
}
|
||||||
|
|
||||||
.image-grid {
|
.image-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
max-height: 400px;
|
height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,27 +589,26 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.pagination-wrapper {
|
.pagination-wrapper {
|
||||||
display: flex;
|
/* 由于分页已移到外部,这里的样式不再需要 */
|
||||||
justify-content: center;
|
/* 分页现在直接使用 AdminPageLayout 的 content-footer */
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 滚动条样式 */
|
/* 滚动条样式 - 更新为新的容器类名 */
|
||||||
.image-preview-container::-webkit-scrollbar {
|
.image-grid::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-preview-container::-webkit-scrollbar-track {
|
.image-grid::-webkit-scrollbar-track {
|
||||||
background: #f1f1f1;
|
background: #f1f1f1;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-preview-container::-webkit-scrollbar-thumb {
|
.image-grid::-webkit-scrollbar-thumb {
|
||||||
background: #c1c1c1;
|
background: #c1c1c1;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-preview-container::-webkit-scrollbar-thumb:hover {
|
.image-grid::-webkit-scrollbar-thumb:hover {
|
||||||
background: #a8a8a8;
|
background: #a8a8a8;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<AdminPageLayout>
|
||||||
<!-- 页面标题 -->
|
<!-- 页面头部 - 标题和按钮 -->
|
||||||
<div class="flex items-center justify-between">
|
<template #page-header>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">待处理资源</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">待处理资源</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">管理待处理的资源</p>
|
<p class="text-gray-600 dark:text-gray-400">管理待处理的资源</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex space-x-3">
|
<div class="flex space-x-3">
|
||||||
<n-button @click="navigateTo('/admin/failed-resources')" type="error">
|
<n-button @click="navigateTo('/admin/failed-resources')" type="tertiary">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i class="fas fa-exclamation-triangle"></i>
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
</template>
|
</template>
|
||||||
@@ -19,76 +19,22 @@
|
|||||||
</template>
|
</template>
|
||||||
刷新
|
刷新
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 自动处理配置状态 -->
|
|
||||||
<n-card>
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<div class="flex items-center space-x-3">
|
|
||||||
<div class="flex items-center space-x-2">
|
|
||||||
<i class="fas fa-cog text-gray-600 dark:text-gray-400"></i>
|
|
||||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">自动处理配置:</span>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center space-x-2">
|
|
||||||
<div
|
|
||||||
:class="[
|
|
||||||
'w-3 h-3 rounded-full',
|
|
||||||
systemConfig?.auto_process_ready_resources
|
|
||||||
? 'bg-green-500 animate-pulse'
|
|
||||||
: 'bg-red-500'
|
|
||||||
]"
|
|
||||||
></div>
|
|
||||||
<span
|
|
||||||
:class="[
|
|
||||||
'text-sm font-medium',
|
|
||||||
systemConfig?.auto_process_ready_resources
|
|
||||||
? 'text-green-600 dark:text-green-400'
|
|
||||||
: 'text-red-600 dark:text-red-400'
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
{{ systemConfig?.auto_process_ready_resources ? '已开启' : '已关闭' }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center space-x-3">
|
|
||||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
|
||||||
<i class="fas fa-info-circle mr-1"></i>
|
|
||||||
{{ systemConfig?.auto_process_ready_resources
|
|
||||||
? '系统会自动处理待处理资源并入库'
|
|
||||||
: '需要手动处理待处理资源'
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
<!-- <n-button
|
|
||||||
@click="refreshConfig"
|
|
||||||
:disabled="updatingConfig"
|
|
||||||
size="small"
|
|
||||||
type="tertiary"
|
|
||||||
title="刷新配置"
|
|
||||||
>
|
|
||||||
<template #icon>
|
|
||||||
<i class="fas fa-sync-alt"></i>
|
|
||||||
</template>
|
|
||||||
</n-button> -->
|
|
||||||
<n-button
|
<n-button
|
||||||
@click="toggleAutoProcess"
|
@click="toggleAutoProcess"
|
||||||
:disabled="updatingConfig"
|
:disabled="updatingConfig"
|
||||||
:type="systemConfig?.auto_process_ready_resources ? 'error' : 'success'"
|
:type="systemConfig?.auto_process_ready_resources ? 'error' : 'success'"
|
||||||
size="small"
|
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<i v-if="updatingConfig" class="fas fa-spinner fa-spin"></i>
|
<i v-if="updatingConfig" class="fas fa-spinner fa-spin"></i>
|
||||||
<i v-else :class="systemConfig?.auto_process_ready_resources ? 'fas fa-pause' : 'fas fa-play'"></i>
|
<i v-else :class="systemConfig?.auto_process_ready_resources ? 'fas fa-pause' : 'fas fa-play'"></i>
|
||||||
</template>
|
</template>
|
||||||
{{ systemConfig?.auto_process_ready_resources ? '关闭' : '开启' }}
|
{{ systemConfig?.auto_process_ready_resources ? '关闭自动处理' : '开启自动处理' }}
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</n-card>
|
|
||||||
|
|
||||||
<!-- 资源列表 -->
|
<!-- 内容区header - 资源列表头部 -->
|
||||||
<n-card>
|
<template #content-header>
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-lg font-semibold">待处理资源列表</span>
|
<span class="text-lg font-semibold">待处理资源列表</span>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
@@ -107,10 +53,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- 内容区content - 资源列表 -->
|
||||||
|
<template #content>
|
||||||
|
<!-- 加载状态 -->
|
||||||
<div v-if="loading" class="flex items-center justify-center py-8">
|
<div v-if="loading" class="flex items-center justify-center py-8">
|
||||||
<n-spin size="large" />
|
<n-spin size="large" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
<div v-else-if="readyResources.length === 0" class="text-center py-8">
|
<div v-else-if="readyResources.length === 0" class="text-center py-8">
|
||||||
<i class="fas fa-inbox text-4xl text-gray-400 mb-4"></i>
|
<i class="fas fa-inbox text-4xl text-gray-400 mb-4"></i>
|
||||||
<p class="text-gray-500">暂无待处理资源</p>
|
<p class="text-gray-500">暂无待处理资源</p>
|
||||||
@@ -125,6 +75,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<n-data-table
|
<n-data-table
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
@@ -136,8 +87,8 @@
|
|||||||
@update:page="handlePageChange"
|
@update:page="handlePageChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</template>
|
||||||
</div>
|
</AdminPageLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<AdminPageLayout>
|
||||||
<!-- 页面标题 -->
|
<!-- 页面头部 - 标题和按钮 -->
|
||||||
<div class="flex items-center justify-between">
|
<template #page-header>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">资源管理</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">资源管理</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">管理系统中的所有资源</p>
|
<p class="text-gray-600 dark:text-gray-400">管理系统中的所有资源</p>
|
||||||
@@ -26,10 +26,11 @@
|
|||||||
刷新
|
刷新
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<!-- 搜索和筛选 -->
|
<!-- 过滤栏 - 搜索和筛选 -->
|
||||||
<n-card>
|
<template #filter-bar>
|
||||||
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4">
|
||||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||||
<n-input
|
<n-input
|
||||||
v-model:value="searchQuery"
|
v-model:value="searchQuery"
|
||||||
@@ -63,11 +64,11 @@
|
|||||||
搜索
|
搜索
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 资源列表 -->
|
<!-- 内容区header - 资源列表头部 -->
|
||||||
<n-card>
|
<template #content-header>
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<span class="text-lg font-semibold">资源列表</span>
|
<span class="text-lg font-semibold">资源列表</span>
|
||||||
@@ -77,29 +78,32 @@
|
|||||||
@update:checked="toggleSelectAll"
|
@update:checked="toggleSelectAll"
|
||||||
:indeterminate="isIndeterminate"
|
:indeterminate="isIndeterminate"
|
||||||
/>
|
/>
|
||||||
<span class="text-sm text-gray-500">全选</span>
|
<span class="text-sm text-gray-500 dark:text-gray-400">全选</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-sm text-gray-500">共 {{ total }} 个资源,已选择 {{ selectedResources.length }} 个</span>
|
<span class="text-sm text-gray-500 dark:text-gray-400">共 {{ total }} 个资源,已选择 {{ selectedResources.length }} 个</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div v-if="loading" class="flex items-center justify-center py-8">
|
<!-- 内容区content - 资源列表 -->
|
||||||
|
<template #content>
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<div v-if="loading" class="flex items-center justify-center py-12">
|
||||||
<n-spin size="large" />
|
<n-spin size="large" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="resources.length === 0" class="text-center py-8">
|
<!-- 空状态 -->
|
||||||
|
<div v-else-if="resources.length === 0" class="flex flex-col items-center justify-center py-12">
|
||||||
<i class="fas fa-inbox text-4xl text-gray-400 mb-4"></i>
|
<i class="fas fa-inbox text-4xl text-gray-400 mb-4"></i>
|
||||||
<p class="text-gray-500">暂无资源数据</p>
|
<p class="text-gray-500 dark:text-gray-400">暂无资源数据</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<!-- 虚拟列表容器 -->
|
||||||
<!-- 虚拟列表 -->
|
<div v-else class="flex-1 h-full overflow-hidden">
|
||||||
<n-virtual-list
|
<n-virtual-list
|
||||||
:items="resources"
|
:items="resources"
|
||||||
:item-size="100"
|
:item-size="100"
|
||||||
style="max-height: 400px"
|
class="h-full"
|
||||||
container-style="height: 600px;"
|
|
||||||
>
|
>
|
||||||
<template #default="{ item: resource }">
|
<template #default="{ item: resource }">
|
||||||
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-4 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors mb-4">
|
<div class="border border-gray-200 dark:border-gray-700 rounded-lg p-4 hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors mb-4">
|
||||||
@@ -111,7 +115,7 @@
|
|||||||
:checked="selectedResources.includes(resource.id)"
|
:checked="selectedResources.includes(resource.id)"
|
||||||
@update:checked="(checked) => toggleResourceSelection(resource.id, checked)"
|
@update:checked="(checked) => toggleResourceSelection(resource.id, checked)"
|
||||||
/>
|
/>
|
||||||
<span class="text-sm text-gray-500">{{ resource.id }}</span>
|
<span class="text-sm text-gray-500 dark:text-gray-400">{{ resource.id }}</span>
|
||||||
|
|
||||||
<span v-if="resource.pan_id" class="text-xs px-2 py-1 bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 rounded flex-shrink-0">
|
<span v-if="resource.pan_id" class="text-xs px-2 py-1 bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 rounded flex-shrink-0">
|
||||||
{{ getPlatformName(resource.pan_id) }}
|
{{ getPlatformName(resource.pan_id) }}
|
||||||
@@ -129,7 +133,7 @@
|
|||||||
{{ resource.description }}
|
{{ resource.description }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="flex items-center space-x-4 text-sm text-gray-500">
|
<div class="flex items-center space-x-4 text-sm text-gray-500 dark:text-gray-400">
|
||||||
<span>
|
<span>
|
||||||
<i class="fas fa-link mr-1"></i>
|
<i class="fas fa-link mr-1"></i>
|
||||||
{{ resource.url }}
|
{{ resource.url }}
|
||||||
@@ -179,9 +183,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</n-virtual-list>
|
</n-virtual-list>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 分页 -->
|
<!-- 内容区footer - 分页组件 -->
|
||||||
<div class="mt-6">
|
<template #content-footer>
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="flex justify-center">
|
||||||
<n-pagination
|
<n-pagination
|
||||||
v-model:page="currentPage"
|
v-model:page="currentPage"
|
||||||
v-model:page-size="pageSize"
|
v-model:page-size="pageSize"
|
||||||
@@ -193,8 +201,10 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</template>
|
||||||
|
</AdminPageLayout>
|
||||||
|
|
||||||
|
<!-- 模态框 - 在AdminPageLayout外部 -->
|
||||||
<!-- 批量操作模态框 -->
|
<!-- 批量操作模态框 -->
|
||||||
<n-modal v-model:show="showBatchModal" preset="card" title="批量操作" style="width: 600px">
|
<n-modal v-model:show="showBatchModal" preset="card" title="批量操作" style="width: 600px">
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
@@ -290,7 +300,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
</div>
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="p-6">
|
<AdminPageLayout>
|
||||||
<div class="mb-6">
|
<!-- 页面头部 - 标题 -->
|
||||||
|
<template #page-header>
|
||||||
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">SEO管理</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">SEO管理</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400 mt-2">搜索引擎优化管理</p>
|
<p class="text-gray-600 dark:text-gray-400">搜索引擎优化管理</p>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 内容区 -->
|
||||||
|
<template #content>
|
||||||
|
<div class="config-content h-full">
|
||||||
<!-- Tab导航 -->
|
<!-- Tab导航 -->
|
||||||
<n-tabs v-model:value="activeTab" type="line" animated>
|
<n-tabs v-model:value="activeTab" type="line" animated>
|
||||||
<n-tab-pane name="site-submit" tab="站点提交">
|
<n-tab-pane name="site-submit" tab="站点提交">
|
||||||
|
<div class="tab-content-container">
|
||||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">站点提交(待开发)</h3>
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">站点提交(待开发)</h3>
|
||||||
@@ -181,9 +188,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
<n-tab-pane name="link-building" tab="外链建设">
|
<n-tab-pane name="link-building" tab="外链建设">
|
||||||
|
<div class="tab-content-container">
|
||||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">外链建设(待开发)</h3>
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">外链建设(待开发)</h3>
|
||||||
@@ -263,12 +272,17 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
</n-tabs>
|
</n-tabs>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
</AdminPageLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import AdminPageLayout from '~/components/AdminPageLayout.vue'
|
||||||
|
|
||||||
// SEO管理页面
|
// SEO管理页面
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'admin'
|
layout: 'admin'
|
||||||
@@ -494,3 +508,22 @@ onMounted(() => {
|
|||||||
loadLinkList()
|
loadLinkList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* SEO管理页面样式 */
|
||||||
|
|
||||||
|
.config-content {
|
||||||
|
padding: 8px;
|
||||||
|
background-color: var(--color-white, #ffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .config-content {
|
||||||
|
background-color: var(--color-dark-bg, #1f2937);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content-container {
|
||||||
|
height: calc(100vh - 240px);
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<AdminPageLayout>
|
||||||
<!-- 页面标题 -->
|
<!-- 页面头部 - 标题和保存按钮 -->
|
||||||
<div class="flex items-center justify-between">
|
<template #page-header>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">站点配置</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">站点配置</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">管理网站基本信息和设置</p>
|
<p class="text-gray-600 dark:text-gray-400">管理网站基本信息和设置</p>
|
||||||
@@ -12,19 +12,19 @@
|
|||||||
</template>
|
</template>
|
||||||
保存配置
|
保存配置
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<!-- 配置表单 -->
|
<!-- 内容区 - 配置表单 -->
|
||||||
<n-card>
|
<template #content>
|
||||||
|
<div class="config-content h-full">
|
||||||
<!-- 顶部Tabs -->
|
<!-- 顶部Tabs -->
|
||||||
<n-tabs
|
<n-tabs
|
||||||
v-model:value="activeTab"
|
v-model:value="activeTab"
|
||||||
type="line"
|
type="line"
|
||||||
animated
|
animated
|
||||||
class="mb-6"
|
|
||||||
>
|
>
|
||||||
<n-tab-pane name="basic" tab="基本信息">
|
<n-tab-pane name="basic" tab="基本信息">
|
||||||
|
<div class="tab-content-container">
|
||||||
<n-form
|
<n-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="configForm"
|
:model="configForm"
|
||||||
@@ -117,12 +117,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-form>
|
</n-form>
|
||||||
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<n-tab-pane name="security" tab="安全设置">
|
<n-tab-pane name="security" tab="安全设置">
|
||||||
|
|
||||||
|
<div class="tab-content-container">
|
||||||
<n-form
|
<n-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="configForm"
|
:model="configForm"
|
||||||
@@ -174,10 +174,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-form>
|
</n-form>
|
||||||
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
</n-tabs>
|
</n-tabs>
|
||||||
</n-card>
|
</div>
|
||||||
|
</template>
|
||||||
|
</AdminPageLayout>
|
||||||
<!-- Logo选择模态框 -->
|
<!-- Logo选择模态框 -->
|
||||||
<n-modal v-model:show="showLogoSelector" preset="card" title="选择Logo图片" style="width: 90vw; max-width: 1200px; max-height: 80vh;">
|
<n-modal v-model:show="showLogoSelector" preset="card" title="选择Logo图片" style="width: 90vw; max-width: 1200px; max-height: 80vh;">
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
@@ -271,7 +273,6 @@
|
|||||||
</n-space>
|
</n-space>
|
||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -584,7 +585,31 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 自定义样式 */
|
/* 站点配置页面样式 */
|
||||||
|
|
||||||
|
.config-content {
|
||||||
|
padding: 8px;
|
||||||
|
background-color: var(--color-white, #ffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .config-content {
|
||||||
|
background-color: var(--color-dark-bg, #1f2937);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 配置标签容器 - 支持滚动 */
|
||||||
|
.config-tabs-container {
|
||||||
|
height: calc(100vh - 200px);
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tab内容容器 - 个别内容滚动 */
|
||||||
|
.tab-content-container {
|
||||||
|
height: calc(100vh - 240px);
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.file-grid {
|
.file-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
@@ -611,8 +636,6 @@ onMounted(() => {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.pagination-wrapper {
|
.pagination-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -1,32 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<AdminPageLayout>
|
||||||
<!-- 页面标题 -->
|
<template #page-header>
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">标签管理</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">标签管理</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">管理系统中的资源标签</p>
|
<p class="text-gray-600 dark:text-gray-400">管理系统中的资源标签</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex space-x-3">
|
<div class="flex space-x-3">
|
||||||
<n-button type="primary" @click="showAddModal = true">
|
|
||||||
<template #icon>
|
|
||||||
<i class="fas fa-plus"></i>
|
|
||||||
</template>
|
|
||||||
添加标签
|
|
||||||
</n-button>
|
|
||||||
<n-button @click="refreshData">
|
|
||||||
<template #icon>
|
|
||||||
<i class="fas fa-refresh"></i>
|
|
||||||
</template>
|
|
||||||
刷新
|
|
||||||
</n-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 提示信息 -->
|
<!-- 提示信息区域 -->
|
||||||
|
<template #notice-section>
|
||||||
<n-alert title="提交的数据中,如果包含标签,数据添加成功,会自动添加标签" type="info" />
|
<n-alert title="提交的数据中,如果包含标签,数据添加成功,会自动添加标签" type="info" />
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 搜索和操作 -->
|
<!-- 过滤栏 - 搜索和操作 -->
|
||||||
<n-card>
|
<template #filter-bar>
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<n-button @click="showAddModal = true" type="success">
|
<n-button @click="showAddModal = true" type="success">
|
||||||
@@ -58,21 +47,24 @@
|
|||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</template>
|
||||||
|
|
||||||
<!-- 标签列表 -->
|
<!-- 内容区header - 标签列表标题 -->
|
||||||
<n-card>
|
<!-- <template #content-header>
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-lg font-semibold">标签列表</span>
|
<span class="text-lg font-semibold">标签列表</span>
|
||||||
<span class="text-sm text-gray-500">共 {{ total }} 个标签</span>
|
<span class="text-sm text-gray-500">共 {{ total }} 个标签</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template> -->
|
||||||
|
|
||||||
<div v-if="loading" class="flex items-center justify-center py-8">
|
<!-- 内容区 - 标签数据 -->
|
||||||
|
<template #content>
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<div v-if="loading" class="flex h-full items-center justify-center py-8">
|
||||||
<n-spin size="large" />
|
<n-spin size="large" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
<div v-else-if="tags.length === 0" class="text-center py-8">
|
<div v-else-if="tags.length === 0" class="text-center py-8">
|
||||||
<svg class="w-16 h-16 text-gray-300 dark:text-gray-600 mb-4 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 48 48">
|
<svg class="w-16 h-16 text-gray-300 dark:text-gray-600 mb-4 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 48 48">
|
||||||
<circle cx="24" cy="24" r="20" stroke-width="3" stroke-dasharray="6 6" />
|
<circle cx="24" cy="24" r="20" stroke-width="3" stroke-dasharray="6 6" />
|
||||||
@@ -87,20 +79,39 @@
|
|||||||
添加标签
|
添加标签
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 数据表格 - 自适应高度 -->
|
||||||
<div v-else>
|
<div v-else class="flex flex-col h-full overflow-auto">
|
||||||
<n-data-table
|
<n-data-table
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:data="tags"
|
:data="tags"
|
||||||
:pagination="pagination"
|
:pagination="false"
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
:single-line="false"
|
:single-line="false"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@update:page="handlePageChange"
|
:scroll-x="800"
|
||||||
|
class="h-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</template>
|
||||||
|
|
||||||
|
<!-- 内容区footer - 分页组件 -->
|
||||||
|
<template #content-footer>
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<n-pagination
|
||||||
|
v-model:page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:item-count="total"
|
||||||
|
:page-sizes="[100, 200, 500, 1000]"
|
||||||
|
show-size-picker
|
||||||
|
@update:page="fetchData"
|
||||||
|
@update:page-size="(size) => { pageSize = size; currentPage = 1; fetchData() }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</AdminPageLayout>
|
||||||
<!-- 添加/编辑标签模态框 -->
|
<!-- 添加/编辑标签模态框 -->
|
||||||
<n-modal v-model:show="showAddModal" preset="card" :title="editingTag ? '编辑标签' : '添加标签'" style="width: 500px">
|
<n-modal v-model:show="showAddModal" preset="card" :title="editingTag ? '编辑标签' : '添加标签'" style="width: 500px">
|
||||||
<n-form
|
<n-form
|
||||||
@@ -146,7 +157,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -291,23 +301,7 @@ const columns = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
// 分页配置
|
// 分页配置已经被移到模板中处理
|
||||||
const pagination = computed(() => ({
|
|
||||||
page: currentPage.value,
|
|
||||||
pageSize: pageSize.value,
|
|
||||||
itemCount: total.value,
|
|
||||||
showSizePicker: true,
|
|
||||||
pageSizes: [10, 20, 50, 100],
|
|
||||||
onChange: (page: number) => {
|
|
||||||
currentPage.value = page
|
|
||||||
fetchData()
|
|
||||||
},
|
|
||||||
onUpdatePageSize: (size: number) => {
|
|
||||||
pageSize.value = size
|
|
||||||
currentPage.value = 1
|
|
||||||
fetchData()
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
// 获取数据
|
// 获取数据
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<AdminPageLayout>
|
||||||
<!-- 页面标题 -->
|
<!-- 页面头部 - 标题和操作按钮 -->
|
||||||
<div class="flex items-center justify-between">
|
<template #page-header>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">用户管理</h1>
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">用户管理</h1>
|
||||||
<p class="text-gray-600 dark:text-gray-400">管理系统中的用户账户</p>
|
<p class="text-gray-600 dark:text-gray-400">管理系统中的用户账户</p>
|
||||||
@@ -20,20 +20,24 @@
|
|||||||
刷新
|
刷新
|
||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
|
|
||||||
<!-- 提示信息 -->
|
<!-- 通知区域 -->
|
||||||
|
<template #notice-section>
|
||||||
<n-alert title="用户管理功能,可以创建、编辑、删除用户,以及修改用户密码" type="info" />
|
<n-alert title="用户管理功能,可以创建、编辑、删除用户,以及修改用户密码" type="info" />
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 用户列表 -->
|
<!-- 内容区header -->
|
||||||
<n-card>
|
<template #content-header>
|
||||||
<template #header>
|
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<span class="text-lg font-semibold">用户列表</span>
|
<span class="text-lg font-semibold">用户列表</span>
|
||||||
<span class="text-sm text-gray-500">共 {{ total }} 个用户</span>
|
<span class="text-sm text-gray-500">共 {{ total }} 个用户</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- 内容区 - 用户列表 -->
|
||||||
|
<template #content>
|
||||||
|
|
||||||
<div v-if="loading" class="flex items-center justify-center py-8">
|
<div v-if="loading" class="flex items-center justify-center py-8">
|
||||||
<n-spin size="large" />
|
<n-spin size="large" />
|
||||||
</div>
|
</div>
|
||||||
@@ -53,18 +57,36 @@
|
|||||||
</n-button>
|
</n-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else>
|
<div v-else class="h-full">
|
||||||
<n-data-table
|
<n-data-table
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:data="users"
|
:data="users"
|
||||||
:pagination="pagination"
|
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
:single-line="false"
|
:single-line="false"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@update:page="handlePageChange"
|
@update:page="handlePageChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</n-card>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 内容区footer - 分页组件 -->
|
||||||
|
<template #content-footer>
|
||||||
|
<div class="p-4">
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<n-pagination
|
||||||
|
v-model:page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:item-count="total"
|
||||||
|
:page-sizes="[100, 200, 500, 1000]"
|
||||||
|
show-size-picker
|
||||||
|
@update:page="fetchData"
|
||||||
|
@update:page-size="(size) => { pageSize = size; currentPage = 1; fetchData() }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</AdminPageLayout>
|
||||||
|
|
||||||
<!-- 创建/编辑用户模态框 -->
|
<!-- 创建/编辑用户模态框 -->
|
||||||
<n-modal v-model:show="showModal" preset="card" :title="showEditModal ? '编辑用户' : '创建用户'" style="width: 500px">
|
<n-modal v-model:show="showModal" preset="card" :title="showEditModal ? '编辑用户' : '创建用户'" style="width: 500px">
|
||||||
@@ -176,10 +198,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import AdminPageLayout from '~/components/AdminPageLayout.vue'
|
||||||
|
|
||||||
// 设置页面布局
|
// 设置页面布局
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'admin'
|
layout: 'admin'
|
||||||
@@ -594,4 +617,13 @@ const showModal = computed({
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 自定义样式 */
|
/* 自定义样式 */
|
||||||
|
|
||||||
|
.config-content {
|
||||||
|
padding: 1rem;
|
||||||
|
background-color: var(--color-white, #ffffff);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .config-content {
|
||||||
|
background-color: var(--color-dark-bg, #1f2937);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -65,7 +65,7 @@ export const useTaskStore = defineStore('task', () => {
|
|||||||
// 获取任务统计信息
|
// 获取任务统计信息
|
||||||
const fetchTaskStats = async () => {
|
const fetchTaskStats = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await taskApi.getTasks({status: 'running'}) as any
|
const response = await taskApi.getTasks() as any
|
||||||
// console.log('原始任务API响应:', response)
|
// console.log('原始任务API响应:', response)
|
||||||
|
|
||||||
// 处理API响应格式
|
// 处理API响应格式
|
||||||
|
|||||||
Reference in New Issue
Block a user