mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-25 19:37:33 +08:00
update: 更新页面,修复添加资源的问题
This commit is contained in:
@@ -334,7 +334,7 @@ func (r *ResourceRepositoryImpl) InvalidateCache() error {
|
||||
// FindExists 检查是否存在相同URL的资源
|
||||
func (r *ResourceRepositoryImpl) FindExists(url string, excludeID ...uint) (bool, error) {
|
||||
var count int64
|
||||
query := r.db.Model(&entity.Resource{}).Where("url = ? or save_url ", url, url)
|
||||
query := r.db.Model(&entity.Resource{}).Where("url = ? OR save_url = ?", url, url)
|
||||
|
||||
// 如果有排除ID,则排除该记录(用于更新时排除自己)
|
||||
if len(excludeID) > 0 {
|
||||
|
||||
@@ -428,6 +428,34 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes
|
||||
|
||||
return nil
|
||||
} else {
|
||||
// 获取夸克网盘账号的 cookie
|
||||
accounts, err := s.cksRepo.FindByPanID(*s.getPanIDByServiceType(serviceType))
|
||||
if err != nil {
|
||||
Error("获取夸克网盘账号失败: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(accounts) == 0 {
|
||||
Error("没有可用的夸克网盘账号")
|
||||
return fmt.Errorf("没有可用的夸克网盘账号")
|
||||
}
|
||||
|
||||
// 选择第一个有效的账号
|
||||
var selectedAccount *entity.Cks
|
||||
for _, account := range accounts {
|
||||
if account.IsValid {
|
||||
selectedAccount = &account
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if selectedAccount == nil {
|
||||
Error("没有有效的夸克网盘账号")
|
||||
return fmt.Errorf("没有有效的夸克网盘账号")
|
||||
}
|
||||
|
||||
Debug("使用夸克网盘账号: %d, Cookie: %s", selectedAccount.ID, selectedAccount.Ck[:20]+"...")
|
||||
|
||||
// 准备配置
|
||||
config := &panutils.PanConfig{
|
||||
URL: readyResource.URL,
|
||||
@@ -436,6 +464,7 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes
|
||||
ExpiredType: 1, // 永久分享
|
||||
AdFid: "",
|
||||
Stoken: "",
|
||||
Cookie: selectedAccount.Ck, // 添加 cookie
|
||||
}
|
||||
|
||||
// 通过工厂获取对应的网盘服务单例
|
||||
@@ -457,14 +486,22 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes
|
||||
return nil
|
||||
}
|
||||
|
||||
// 如果获取到了标题,更新资源标题
|
||||
if result.Title != "" {
|
||||
resource.Title = result.Title
|
||||
}
|
||||
}
|
||||
|
||||
// 处理标签
|
||||
tagIDs, err := s.handleTags(readyResource.Tags)
|
||||
if err != nil || tagIDs == nil {
|
||||
if err != nil {
|
||||
Error("处理标签失败: %v", err)
|
||||
return err
|
||||
}
|
||||
// 如果没有标签,tagIDs 可能为 nil,这是正常的
|
||||
if tagIDs == nil {
|
||||
tagIDs = []uint{} // 初始化为空数组
|
||||
}
|
||||
// 处理分类
|
||||
categoryID, err := s.resolveCategory(readyResource.Category, tagIDs)
|
||||
if err != nil {
|
||||
@@ -481,12 +518,16 @@ func (s *Scheduler) convertReadyResourceToResource(readyResource entity.ReadyRes
|
||||
return err
|
||||
}
|
||||
// 插入 resource_tags 关联
|
||||
if len(tagIDs) > 0 {
|
||||
for _, tagID := range tagIDs {
|
||||
err := s.resourceRepo.CreateResourceTag(resource.ID, tagID)
|
||||
if err != nil {
|
||||
Error("插入资源标签关联失败: %v", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Debug("没有标签,跳过插入资源标签关联")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -889,44 +930,80 @@ func splitTags(tagStr string) []string {
|
||||
// 处理标签,返回所有标签ID
|
||||
func (s *Scheduler) handleTags(tagStr string) ([]uint, error) {
|
||||
if tagStr == "" {
|
||||
return nil, nil
|
||||
Debug("标签字符串为空,返回空数组")
|
||||
return []uint{}, nil // 返回空数组而不是 nil
|
||||
}
|
||||
|
||||
Debug("开始处理标签字符串: %s", tagStr)
|
||||
tagNames := splitTags(tagStr)
|
||||
Debug("分割后的标签名称: %v", tagNames)
|
||||
|
||||
var tagIDs []uint
|
||||
for _, name := range tagNames {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
Debug("跳过空标签名称")
|
||||
continue
|
||||
}
|
||||
|
||||
Debug("查找标签: %s", name)
|
||||
tag, err := s.tagRepo.FindByName(name)
|
||||
if err != nil || tag == nil {
|
||||
if err != nil {
|
||||
Debug("标签 %s 不存在,创建新标签", name)
|
||||
// 不存在则新建
|
||||
tag = &entity.Tag{Name: name}
|
||||
err = s.tagRepo.Create(tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
Error("创建标签 %s 失败: %v", name, err)
|
||||
return nil, fmt.Errorf("创建标签 %s 失败: %v", name, err)
|
||||
}
|
||||
Debug("成功创建标签: %s (ID: %d)", name, tag.ID)
|
||||
} else {
|
||||
Debug("找到已存在的标签: %s (ID: %d)", name, tag.ID)
|
||||
}
|
||||
tagIDs = append(tagIDs, tag.ID)
|
||||
}
|
||||
|
||||
Debug("处理完成,标签ID列表: %v", tagIDs)
|
||||
return tagIDs, nil
|
||||
}
|
||||
|
||||
// 分类处理逻辑
|
||||
func (s *Scheduler) resolveCategory(categoryName string, tagIDs []uint) (*uint, error) {
|
||||
Debug("开始处理分类,分类名称: %s, 标签ID列表: %v", categoryName, tagIDs)
|
||||
|
||||
if categoryName != "" {
|
||||
Debug("查找分类: %s", categoryName)
|
||||
cat, err := s.categoryRepo.FindByName(categoryName)
|
||||
if err == nil && cat != nil {
|
||||
if err != nil {
|
||||
Debug("分类 %s 不存在: %v", categoryName, err)
|
||||
} else if cat != nil {
|
||||
Debug("找到分类: %s (ID: %d)", categoryName, cat.ID)
|
||||
return &cat.ID, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 没有分类,尝试用标签反查
|
||||
if len(tagIDs) == 0 {
|
||||
Debug("没有标签,无法通过标签反查分类")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
Debug("尝试通过标签反查分类")
|
||||
for _, tagID := range tagIDs {
|
||||
Debug("查找标签ID: %d", tagID)
|
||||
tag, err := s.tagRepo.GetByID(tagID)
|
||||
if err == nil && tag != nil && tag.CategoryID != nil {
|
||||
if err != nil {
|
||||
Debug("查找标签ID %d 失败: %v", tagID, err)
|
||||
continue
|
||||
}
|
||||
if tag != nil && tag.CategoryID != nil {
|
||||
Debug("通过标签 %s (ID: %d) 找到分类ID: %d", tag.Name, tagID, *tag.CategoryID)
|
||||
return tag.CategoryID, nil
|
||||
}
|
||||
}
|
||||
|
||||
Debug("未找到分类,返回nil")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
required
|
||||
></textarea>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
支持百度网盘、阿里云盘、夸克网盘等链接
|
||||
支持百度网盘、阿里云盘、夸克网盘等链接,每行一个链接
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -205,7 +205,7 @@ const clearForm = () => {
|
||||
newTag.value = ''
|
||||
}
|
||||
|
||||
// 单个添加提交
|
||||
// 单个添加提交 - 更新为使用批量添加方法
|
||||
const handleSubmit = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
@@ -214,23 +214,23 @@ const handleSubmit = async () => {
|
||||
// 多行链接处理
|
||||
const urls = form.value.url.split(/\r?\n/).map(l => l.trim()).filter(Boolean)
|
||||
|
||||
// 为每个URL创建一个资源
|
||||
for (const url of urls) {
|
||||
// 使用批量添加方法,将多个URL作为一个资源的多个链接
|
||||
const resourceData = {
|
||||
title: form.value.title, // 标题必填
|
||||
description: form.value.description || undefined, // 添加描述
|
||||
url: url,
|
||||
resources: [{
|
||||
title: form.value.title || undefined, // 后端期望 *string 类型
|
||||
description: form.value.description || '',
|
||||
url: urls, // 现在 url 是一个数组
|
||||
category: form.value.category || '',
|
||||
tags: form.value.tags.join(','), // 转换为逗号分隔的字符串
|
||||
img: form.value.img || '',
|
||||
source: form.value.source || '手动添加',
|
||||
extra: form.value.extra || '',
|
||||
}]
|
||||
}
|
||||
|
||||
await readyResourceApi.createReadyResource(resourceData)
|
||||
}
|
||||
const response = await readyResourceApi.batchCreateReadyResources(resourceData)
|
||||
|
||||
emit('success', `成功添加 ${urls.length} 个资源到待处理列表`)
|
||||
emit('success', `成功添加资源,包含 ${urls.length} 个链接`)
|
||||
clearForm()
|
||||
} catch (e: any) {
|
||||
emit('error', e.message || '添加失败')
|
||||
|
||||
@@ -18,6 +18,11 @@ export const parseApiResponse = <T>(response: any): T => {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否是包含items字段的响应格式(如分类接口)
|
||||
if (response && typeof response === 'object' && 'items' in response) {
|
||||
return response
|
||||
}
|
||||
|
||||
// 检查是否是包含success字段的响应格式(如登录接口)
|
||||
if (response && typeof response === 'object' && 'success' in response && 'data' in response) {
|
||||
if (response.success) {
|
||||
@@ -66,7 +71,7 @@ export const useAuthApi = () => {
|
||||
}
|
||||
|
||||
export const useCategoryApi = () => {
|
||||
const getCategories = () => useApiFetch('/categories').then(parseApiResponse)
|
||||
const getCategories = (params?: any) => useApiFetch('/categories', { params }).then(parseApiResponse)
|
||||
const createCategory = (data: any) => useApiFetch('/categories', { method: 'POST', body: data }).then(parseApiResponse)
|
||||
const updateCategory = (id: number, data: any) => useApiFetch(`/categories/${id}`, { method: 'PUT', body: data }).then(parseApiResponse)
|
||||
const deleteCategory = (id: number) => useApiFetch(`/categories/${id}`, { method: 'DELETE' }).then(parseApiResponse)
|
||||
@@ -93,7 +98,7 @@ export const useCksApi = () => {
|
||||
}
|
||||
|
||||
export const useTagApi = () => {
|
||||
const getTags = () => useApiFetch('/tags').then(parseApiResponse)
|
||||
const getTags = (params?: any) => useApiFetch('/tags', { params }).then(parseApiResponse)
|
||||
const getTagsByCategory = (categoryId: number, params?: any) => useApiFetch(`/categories/${categoryId}/tags`, { params }).then(parseApiResponse)
|
||||
const getTag = (id: number) => useApiFetch(`/tags/${id}`).then(parseApiResponse)
|
||||
const createTag = (data: any) => useApiFetch('/tags', { method: 'POST', body: data }).then(parseApiResponse)
|
||||
|
||||
@@ -260,12 +260,34 @@ const fetchCategories = async () => {
|
||||
page_size: pageSize.value,
|
||||
search: searchQuery.value
|
||||
}
|
||||
console.log('获取分类列表参数:', params)
|
||||
const response = await categoryApi.getCategories(params)
|
||||
categories.value = Array.isArray(response) ? response : []
|
||||
console.log('分类接口响应:', response)
|
||||
|
||||
// 适配后端API响应格式
|
||||
if (response && response.items) {
|
||||
console.log('使用 items 格式:', response.items)
|
||||
categories.value = response.items
|
||||
totalCount.value = response.total || 0
|
||||
totalPages.value = Math.ceil(totalCount.value / pageSize.value)
|
||||
} else if (Array.isArray(response)) {
|
||||
console.log('使用数组格式:', response)
|
||||
// 兼容旧格式
|
||||
categories.value = response
|
||||
totalCount.value = response.length
|
||||
totalPages.value = 1
|
||||
} else {
|
||||
console.log('使用默认格式:', response)
|
||||
categories.value = []
|
||||
totalCount.value = 0
|
||||
totalPages.value = 1
|
||||
}
|
||||
console.log('最终分类数据:', categories.value)
|
||||
} catch (error) {
|
||||
console.error('获取分类列表失败:', error)
|
||||
categories.value = []
|
||||
totalCount.value = 0
|
||||
totalPages.value = 1
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -267,6 +267,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// API 导入
|
||||
import { useTagApi, useCategoryApi } from '~/composables/useApi'
|
||||
|
||||
// 设置页面布局
|
||||
definePageMeta({
|
||||
layout: 'admin',
|
||||
@@ -277,6 +280,13 @@ const router = useRouter()
|
||||
const userStore = useUserStore()
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
const tagApi = useTagApi()
|
||||
const categoryApi = useCategoryApi()
|
||||
|
||||
// 调试信息
|
||||
console.log('tagApi:', tagApi)
|
||||
console.log('categoryApi:', categoryApi)
|
||||
|
||||
// 页面状态
|
||||
const pageLoading = ref(true)
|
||||
const loading = ref(false)
|
||||
@@ -332,10 +342,25 @@ const checkAuth = () => {
|
||||
// 获取分类列表
|
||||
const fetchCategories = async () => {
|
||||
try {
|
||||
console.log('获取分类列表...')
|
||||
const response = await categoryApi.getCategories()
|
||||
categories.value = Array.isArray(response) ? response : []
|
||||
console.log('分类接口响应:', response)
|
||||
|
||||
// 适配后端API响应格式
|
||||
if (response && response.items) {
|
||||
console.log('使用 items 格式:', response.items)
|
||||
categories.value = response.items
|
||||
} else if (Array.isArray(response)) {
|
||||
console.log('使用数组格式:', response)
|
||||
categories.value = response
|
||||
} else {
|
||||
console.log('使用默认格式:', response)
|
||||
categories.value = []
|
||||
}
|
||||
console.log('最终分类数据:', categories.value)
|
||||
} catch (error) {
|
||||
console.error('获取分类列表失败:', error)
|
||||
categories.value = []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,17 +373,39 @@ const fetchTags = async () => {
|
||||
page_size: pageSize.value,
|
||||
search: searchQuery.value
|
||||
}
|
||||
console.log('获取标签列表参数:', params)
|
||||
|
||||
let response: any
|
||||
if (selectedCategory.value) {
|
||||
response = await tagApi.getTagsByCategory(selectedCategory.value, params)
|
||||
response = await tagApi.getTagsByCategory(parseInt(selectedCategory.value), params)
|
||||
} else {
|
||||
response = await tagApi.getTags(params)
|
||||
}
|
||||
tags.value = response.items || []
|
||||
console.log('标签接口响应:', response)
|
||||
|
||||
// 适配后端API响应格式
|
||||
if (response && response.items) {
|
||||
console.log('使用 items 格式:', response.items)
|
||||
tags.value = response.items
|
||||
totalCount.value = response.total || 0
|
||||
totalPages.value = Math.ceil(totalCount.value / pageSize.value)
|
||||
} else if (Array.isArray(response)) {
|
||||
console.log('使用数组格式:', response)
|
||||
tags.value = response
|
||||
totalCount.value = response.length
|
||||
totalPages.value = 1
|
||||
} else {
|
||||
console.log('使用默认格式:', response)
|
||||
tags.value = []
|
||||
totalCount.value = 0
|
||||
totalPages.value = 1
|
||||
}
|
||||
console.log('最终标签数据:', tags.value)
|
||||
} catch (error) {
|
||||
console.error('获取标签列表失败:', error)
|
||||
tags.value = []
|
||||
totalCount.value = 0
|
||||
totalPages.value = 1
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -481,15 +528,25 @@ const handleLogout = () => {
|
||||
// 页面加载
|
||||
onMounted(async () => {
|
||||
try {
|
||||
console.log('页面开始加载...')
|
||||
checkAuth()
|
||||
console.log('认证检查完成')
|
||||
|
||||
console.log('开始获取分类列表...')
|
||||
await fetchCategories()
|
||||
console.log('分类列表获取完成')
|
||||
|
||||
console.log('开始获取标签列表...')
|
||||
await fetchTags()
|
||||
console.log('标签列表获取完成')
|
||||
|
||||
// 检查URL参数,如果action=add则自动打开新增弹窗
|
||||
const route = useRoute()
|
||||
if (route.query.action === 'add') {
|
||||
showAddModal.value = true
|
||||
}
|
||||
|
||||
console.log('页面加载完成')
|
||||
} catch (error) {
|
||||
console.error('标签管理页面初始化失败:', error)
|
||||
} finally {
|
||||
|
||||
@@ -5,12 +5,8 @@
|
||||
<div class="text-center">
|
||||
<h1 class="text-2xl font-bold text-gray-900 dark:text-gray-100">管理员登录</h1>
|
||||
<p class="mt-2 text-sm text-gray-600">请输入管理员账号密码</p>
|
||||
<div class="mt-3 p-3 bg-blue-50 rounded-lg">
|
||||
<p class="text-xs text-blue-700">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
默认管理员账户:admin / password
|
||||
</p>
|
||||
</div>
|
||||
<!-- <div class="mt-3 p-3 bg-blue-50 rounded-lg">
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<form @submit.prevent="handleLogin" class="space-y-4">
|
||||
|
||||
Reference in New Issue
Block a user