chore: bump version to v1.2.1

This commit is contained in:
Kerwin
2025-08-13 15:22:01 +08:00
parent 05930a3e70
commit 9b0d385c52
7 changed files with 139 additions and 88 deletions

View File

@@ -35,6 +35,10 @@
- [服务器要求](https://ecn5khs4t956.feishu.cn/wiki/W8YBww1Mmiu4Cdkp5W4c8pFNnMf?from=from_copylink)
- [QQ机器人](https://github.com/ctwj/astrbot_plugin_urldb)
### v1.2.1
1. 修复转存移除广告失败的问题和添加广告失败的问题
2. 管理后台UI优化
### v1.2.0
1. 新增手动批量转存
2. 新增QQ机器人

View File

@@ -1 +1 @@
1.2.0
1.2.1

View File

@@ -18,6 +18,7 @@ type SearchStatRepository interface {
GetSearchTrend(days int) ([]entity.DailySearchStat, error)
GetKeywordTrend(keyword string, days int) ([]entity.DailySearchStat, error)
GetSummary() (map[string]int64, error)
FindWithPaginationOrdered(page, limit int) ([]entity.SearchStat, int64, error)
}
// SearchStatRepositoryImpl 搜索统计Repository实现
@@ -157,3 +158,20 @@ func (r *SearchStatRepositoryImpl) GetSummary() (map[string]int64, error) {
"keywords": keywords,
}, nil
}
// FindWithPaginationOrdered 按时间倒序分页查找搜索记录
func (r *SearchStatRepositoryImpl) FindWithPaginationOrdered(page, limit int) ([]entity.SearchStat, int64, error) {
var stats []entity.SearchStat
var total int64
offset := (page - 1) * limit
// 获取总数
if err := r.db.Model(&entity.SearchStat{}).Count(&total).Error; err != nil {
return nil, 0, err
}
// 获取分页数据,按创建时间倒序排列(最新的在前面)
err := r.db.Order("created_at DESC").Offset(offset).Limit(limit).Find(&stats).Error
return stats, total, err
}

View File

@@ -37,7 +37,8 @@ func GetSearchStats(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
stats, total, err := repoManager.SearchStatRepository.FindWithPagination(page, pageSize)
// 使用自定义方法获取按时间倒序排列的搜索记录
stats, total, err := repoManager.SearchStatRepository.FindWithPaginationOrdered(page, pageSize)
if err != nil {
ErrorResponse(c, "获取搜索统计失败", http.StatusInternalServerError)
return

1
web/components.d.ts vendored
View File

@@ -24,7 +24,6 @@ declare module 'vue' {
NForm: typeof import('naive-ui')['NForm']
NFormItem: typeof import('naive-ui')['NFormItem']
NInput: typeof import('naive-ui')['NInput']
NInputNumber: typeof import('naive-ui')['NInputNumber']
NList: typeof import('naive-ui')['NList']
NListItem: typeof import('naive-ui')['NListItem']
NMessageProvider: typeof import('naive-ui')['NMessageProvider']

View File

@@ -65,51 +65,60 @@
</div>
</n-card>
<!-- 热门关键词 -->
<n-card>
<template #header>
<span class="text-xl font-semibold text-gray-900 dark:text-white">热门关键词</span>
</template>
<div class="space-y-4">
<div v-for="keyword in stats.hotKeywords" :key="keyword.keyword"
class="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center">
<span class="inline-flex items-center justify-center w-8 h-8 bg-blue-100 dark:bg-blue-900 text-blue-600 dark:text-blue-400 rounded-full text-sm font-medium mr-3">
{{ keyword.rank }}
</span>
<span class="text-gray-900 dark:text-white font-medium">{{ keyword.keyword }}</span>
</div>
<div class="flex items-center">
<span class="text-gray-600 dark:text-gray-400 mr-2">{{ keyword.count }}</span>
<div class="w-24 bg-gray-200 dark:bg-gray-700 rounded-full h-2">
<div class="bg-blue-600 h-2 rounded-full"
:style="{ width: getPercentage(keyword.count) + '%' }"></div>
<!-- 热门关键词和搜索记录并排显示 -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- 热门关键词 -->
<n-card>
<template #header>
<span class="text-xl font-semibold text-gray-900 dark:text-white">热门关键词</span>
</template>
<div class="space-y-4">
<div v-for="keyword in limitedHotKeywords" :key="keyword.keyword"
class="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex items-center">
<span class="inline-flex items-center justify-center w-8 h-8 bg-blue-100 dark:bg-blue-900 text-blue-600 dark:text-blue-400 rounded-full text-sm font-medium mr-3">
{{ keyword.rank }}
</span>
<span class="text-gray-900 dark:text-white font-medium">{{ keyword.keyword }}</span>
</div>
<div class="flex items-center">
<span class="text-gray-600 dark:text-gray-400 mr-2">{{ keyword.count }}</span>
<div class="w-24 bg-gray-200 dark:bg-gray-700 rounded-full h-2">
<div class="bg-blue-600 h-2 rounded-full"
:style="{ width: getPercentage(keyword.count) + '%' }"></div>
</div>
</div>
</div>
<div v-if="!stats.hotKeywords || stats.hotKeywords.length === 0" class="text-center py-8 text-gray-500 dark:text-gray-400">
暂无热门关键词数据
</div>
</div>
<div v-if="!stats.hotKeywords || stats.hotKeywords.length === 0" class="text-center py-8 text-gray-500 dark:text-gray-400">
暂无热门关键词数据
</div>
</div>
</n-card>
</n-card>
<!-- 搜索记录 -->
<n-card>
<template #header>
<span class="text-xl font-semibold text-gray-900 dark:text-white">搜索记录</span>
</template>
<n-data-table
:columns="columns"
:data="searchList"
:pagination="pagination"
:loading="loading"
:bordered="false"
striped
/>
<div v-if="searchList.length === 0 && !loading" class="text-center py-8 text-gray-500 dark:text-gray-400">
暂无搜索记录
</div>
</n-card>
<!-- 搜索记录 -->
<n-card>
<template #header>
<span class="text-xl font-semibold text-gray-900 dark:text-white">搜索记录</span>
</template>
<div class="space-y-3">
<div v-for="record in limitedSearchList" :key="record.id"
class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
<div class="flex-1">
<div class="font-medium text-gray-900 dark:text-white">{{ record.keyword }}</div>
<div class="text-sm text-gray-500 dark:text-gray-400">
{{ formatDate(record.created_at) }}
</div>
</div>
<div class="text-right">
<div class="text-sm font-medium text-gray-900 dark:text-white">{{ record.count }}</div>
</div>
</div>
<div v-if="searchList.length === 0 && !loading" class="text-center py-8 text-gray-500 dark:text-gray-400">
暂无搜索记录
</div>
</div>
</n-card>
</div>
</div>
</template>
@@ -160,44 +169,35 @@ const loading = ref(false)
const trendChart = ref<HTMLCanvasElement | null>(null)
let chart: any = null
// 分页配置
const pagination = ref({
page: 1,
pageSize: 20,
showSizePicker: true,
pageSizes: [10, 20, 50, 100],
onChange: (page: number) => {
pagination.value.page = page
loadSearchRecords()
},
onUpdatePageSize: (pageSize: number) => {
pagination.value.pageSize = pageSize
pagination.value.page = 1
loadSearchRecords()
}
// 按时间排序的搜索记录(最新的在前面)
const sortedSearchList = computed(() => {
return [...searchList.value].sort((a, b) => {
return new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
})
})
// 表格列配置
const columns = [
{
title: '关键词',
key: 'keyword',
width: 200
},
{
title: '搜索次数',
key: 'count',
width: 120
},
{
title: '日期',
key: 'date',
width: 150,
render: (row: any) => {
return row.date ? new Date(row.date).toLocaleDateString() : ''
}
}
]
// 限制显示前10条热门关键词
const limitedHotKeywords = computed(() => {
return stats.value.hotKeywords.slice(0, 10)
})
// 限制显示前10条搜索记录
const limitedSearchList = computed(() => {
return sortedSearchList.value.slice(0, 10)
})
// 格式化日期
const formatDate = (dateString: string) => {
if (!dateString) return ''
const date = new Date(dateString)
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
})
}
// 获取百分比
const getPercentage = (count: number) => {

View File

@@ -105,16 +105,16 @@
<!-- 资源列表 -->
<div class="overflow-x-auto bg-white dark:bg-gray-800 rounded-lg shadow">
<table class="w-full min-w-full table-fixed">
<table class="w-full min-w-full">
<thead>
<tr class="bg-slate-800 dark:bg-gray-700 text-white dark:text-gray-100">
<th class="px-2 sm:px-6 py-3 sm:py-4 text-left text-xs sm:text-sm w-1/2 sm:w-4/6">
<th class="px-2 sm:px-6 py-3 sm:py-4 text-left text-xs sm:text-sm">
<div class="flex items-center">
<i class="fas fa-cloud mr-1 text-gray-300"></i> 文件名
</div>
</th>
<th class="px-2 sm:px-6 py-3 sm:py-4 text-left text-xs sm:text-sm hidden sm:table-cell w-1/6">链接</th>
<th class="px-2 sm:px-6 py-3 sm:py-4 text-left text-xs sm:text-sm hidden sm:table-cell w-1/6">更新时间</th>
<th class="px-2 sm:px-6 py-3 sm:py-4 text-left text-xs sm:text-sm hidden sm:table-cell w-24">链接</th>
<th class="px-2 sm:px-6 py-3 sm:py-4 text-left text-xs sm:text-sm hidden sm:table-cell w-32">更新时间</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
@@ -136,10 +136,16 @@
:class="isUpdatedToday(resource.updated_at) ? 'hover:bg-pink-50 dark:hover:bg-pink-900 bg-pink-50/30 dark:bg-pink-900/30' : 'hover:bg-gray-50 dark:hover:bg-gray-800'"
:data-index="index"
>
<td class="px-2 sm:px-6 py-2 sm:py-4 text-xs sm:text-sm w-1/2 sm:w-2/5">
<td class="px-2 sm:px-6 py-2 sm:py-4 text-xs sm:text-sm">
<div class="flex items-start">
<span class="mr-2 flex-shrink-0" v-html="getPlatformIcon(resource.pan_id || 0)"></span>
<span class="break-words">{{ resource.title }}</span>
<div class="flex-1 min-w-0">
<div class="break-words font-medium">{{ resource.title }}</div>
<!-- 显示描述 -->
<div v-if="resource.description" class="text-xs text-gray-600 dark:text-gray-400 mt-1 break-words line-clamp-2">
{{ resource.description }}
</div>
</div>
</div>
<div class="sm:hidden mt-1 space-y-1">
<!-- 移动端显示更新时间 -->
@@ -155,7 +161,7 @@
</button>
</div>
</td>
<td class="px-2 sm:px-6 py-2 sm:py-4 text-xs sm:text-sm hidden sm:table-cell w-1/5">
<td class="px-2 sm:px-6 py-2 sm:py-4 text-xs sm:text-sm hidden sm:table-cell w-32">
<button
class="text-blue-600 hover:text-blue-800 flex items-center gap-1 show-link-btn"
@click="toggleLink(resource)"
@@ -163,7 +169,7 @@
<i class="fas fa-eye"></i> 显示链接
</button>
</td>
<td class="px-2 sm:px-6 py-2 sm:py-4 text-xs sm:text-sm text-gray-500 hidden sm:table-cell w-2/5" :title="resource.updated_at">
<td class="px-2 sm:px-6 py-2 sm:py-4 text-xs sm:text-sm text-gray-500 hidden sm:table-cell w-32" :title="resource.updated_at">
<span v-html="formatRelativeTime(resource.updated_at)"></span>
</td>
</tr>
@@ -510,4 +516,27 @@ const animateCounters = () => {
rgba(0,0,0,0.25) 100%
);
}
/* 文本截断样式 */
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-wrap: break-word;
word-break: break-word;
}
/* 表格单元格内容溢出控制 */
table td {
overflow: hidden;
word-wrap: break-word;
word-break: break-word;
}
/* 确保flex容器不会溢出 */
.min-w-0 {
min-width: 0;
}
</style>