mirror of
https://github.com/ctwj/urldb.git
synced 2025-11-25 03:15:04 +08:00
update: 后台UI 优化
This commit is contained in:
@@ -117,19 +117,11 @@ func RequestToSystemConfig(req *dto.SystemConfigRequest) []entity.SystemConfig {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoFetchHotDramaEnabled, Value: strconv.FormatBool(req.AutoFetchHotDramaEnabled), Type: entity.ConfigTypeBool})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyMaintenanceMode, Value: strconv.FormatBool(req.MaintenanceMode), Type: entity.ConfigTypeBool})
|
||||
|
||||
// 整数字段 - 只添加非零值
|
||||
if req.AutoProcessInterval != 0 {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoProcessInterval, Value: strconv.Itoa(req.AutoProcessInterval), Type: entity.ConfigTypeInt})
|
||||
}
|
||||
if req.AutoTransferLimitDays != 0 {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoTransferLimitDays, Value: strconv.Itoa(req.AutoTransferLimitDays), Type: entity.ConfigTypeInt})
|
||||
}
|
||||
if req.AutoTransferMinSpace != 0 {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoTransferMinSpace, Value: strconv.Itoa(req.AutoTransferMinSpace), Type: entity.ConfigTypeInt})
|
||||
}
|
||||
if req.PageSize != 0 {
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyPageSize, Value: strconv.Itoa(req.PageSize), Type: entity.ConfigTypeInt})
|
||||
}
|
||||
// 整数字段 - 添加所有提交的字段,包括0值
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoProcessInterval, Value: strconv.Itoa(req.AutoProcessInterval), Type: entity.ConfigTypeInt})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoTransferLimitDays, Value: strconv.Itoa(req.AutoTransferLimitDays), Type: entity.ConfigTypeInt})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyAutoTransferMinSpace, Value: strconv.Itoa(req.AutoTransferMinSpace), Type: entity.ConfigTypeInt})
|
||||
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyPageSize, Value: strconv.Itoa(req.PageSize), Type: entity.ConfigTypeInt})
|
||||
|
||||
return configs
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ type SystemConfig struct {
|
||||
|
||||
// 键值对配置
|
||||
Key string `json:"key" gorm:"size:100;not null;unique;comment:配置键"`
|
||||
Value string `json:"value" gorm:"size:1000"`
|
||||
Value string `json:"value" gorm:"type:text"`
|
||||
Type string `json:"type" gorm:"size:20;default:'string'"` // string, int, bool, json
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ type ResourceRepository interface {
|
||||
InvalidateCache() error
|
||||
FindExists(url string, excludeID ...uint) (bool, error)
|
||||
BatchFindByURLs(urls []string) ([]entity.Resource, error)
|
||||
GetResourcesForTransfer(panID uint, sinceTime time.Time) ([]*entity.Resource, error)
|
||||
GetResourcesForTransfer(panID uint, sinceTime time.Time, limit int) ([]*entity.Resource, error)
|
||||
CreateResourceTag(resourceID, tagID uint) error
|
||||
}
|
||||
|
||||
@@ -398,12 +398,18 @@ func (r *ResourceRepositoryImpl) BatchFindByURLs(urls []string) ([]entity.Resour
|
||||
}
|
||||
|
||||
// GetResourcesForTransfer 获取需要转存的资源
|
||||
func (r *ResourceRepositoryImpl) GetResourcesForTransfer(panID uint, sinceTime time.Time) ([]*entity.Resource, error) {
|
||||
func (r *ResourceRepositoryImpl) GetResourcesForTransfer(panID uint, sinceTime time.Time, limit int) ([]*entity.Resource, error) {
|
||||
var resources []*entity.Resource
|
||||
query := r.db.Where("pan_id = ? AND (save_url = '' OR save_url IS NULL) AND (error_msg = '' OR error_msg IS NULL)", panID)
|
||||
if !sinceTime.IsZero() {
|
||||
query = query.Where("created_at >= ?", sinceTime)
|
||||
}
|
||||
|
||||
// 添加数量限制
|
||||
if limit > 0 {
|
||||
query = query.Limit(limit)
|
||||
}
|
||||
|
||||
err := query.Order("created_at DESC").Find(&resources).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -51,23 +51,23 @@ func (h *SystemConfigHandler) UpdateConfig(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if req.AutoProcessInterval != 0 && (req.AutoProcessInterval < 1 || req.AutoProcessInterval > 1440) {
|
||||
if req.AutoProcessInterval > 0 && (req.AutoProcessInterval < 1 || req.AutoProcessInterval > 1440) {
|
||||
ErrorResponse(c, "自动处理间隔必须在1-1440分钟之间", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.PageSize != 0 && (req.PageSize < 10 || req.PageSize > 500) {
|
||||
if req.PageSize > 0 && (req.PageSize < 10 || req.PageSize > 500) {
|
||||
ErrorResponse(c, "每页显示数量必须在10-500之间", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证自动转存配置
|
||||
if req.AutoTransferLimitDays != 0 && (req.AutoTransferLimitDays < 0 || req.AutoTransferLimitDays > 365) {
|
||||
if req.AutoTransferLimitDays > 0 && (req.AutoTransferLimitDays < 0 || req.AutoTransferLimitDays > 365) {
|
||||
ErrorResponse(c, "自动转存限制天数必须在0-365之间", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.AutoTransferMinSpace != 0 && (req.AutoTransferMinSpace < 100 || req.AutoTransferMinSpace > 1024) {
|
||||
if req.AutoTransferMinSpace > 0 && (req.AutoTransferMinSpace < 100 || req.AutoTransferMinSpace > 1024) {
|
||||
ErrorResponse(c, "最小存储空间必须在100-1024GB之间", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -146,8 +146,24 @@ func (a *AutoTransferScheduler) processAutoTransfer() {
|
||||
|
||||
utils.Info(fmt.Sprintf("找到 %d 个可用quark网盘账号,开始自动转存处理...", len(validAccounts)))
|
||||
|
||||
// 获取需要转存的资源
|
||||
resources, err := a.getResourcesForTransfer(quarkPanID)
|
||||
// 计算处理数量限制
|
||||
// 假设每5秒转存一个资源,每分钟20个,5分钟100个
|
||||
// 根据时间间隔和账号数量计算大致的处理数量
|
||||
interval := 5 * time.Minute // 默认5分钟
|
||||
if autoProcessInterval, err := a.systemConfigRepo.GetConfigInt(entity.ConfigKeyAutoProcessInterval); err == nil && autoProcessInterval > 0 {
|
||||
interval = time.Duration(autoProcessInterval) * time.Minute
|
||||
}
|
||||
|
||||
// 计算每分钟能处理的资源数量:账号数 * 12(每分钟12个,即每5秒一个)
|
||||
resourcesPerMinute := len(validAccounts) * 12
|
||||
// 根据时间间隔计算总处理数量
|
||||
maxProcessCount := int(float64(resourcesPerMinute) * interval.Minutes())
|
||||
|
||||
utils.Info(fmt.Sprintf("时间间隔: %v, 账号数: %d, 每分钟处理能力: %d, 最大处理数量: %d",
|
||||
interval, len(validAccounts), resourcesPerMinute, maxProcessCount))
|
||||
|
||||
// 获取需要转存的资源(限制数量)
|
||||
resources, err := a.getResourcesForTransfer(quarkPanID, maxProcessCount)
|
||||
if err != nil {
|
||||
utils.Error(fmt.Sprintf("获取需要转存的资源失败: %v", err))
|
||||
return
|
||||
@@ -167,33 +183,58 @@ func (a *AutoTransferScheduler) processAutoTransfer() {
|
||||
forbiddenWords = "" // 如果获取失败,使用空字符串
|
||||
}
|
||||
|
||||
// 过滤包含违禁词的资源
|
||||
// 过滤包含违禁词的资源,并标记违禁词错误
|
||||
var filteredResources []*entity.Resource
|
||||
var forbiddenResources []*entity.Resource
|
||||
|
||||
if forbiddenWords != "" {
|
||||
words := strings.Split(forbiddenWords, ",")
|
||||
// 清理违禁词数组,去除空格
|
||||
var cleanWords []string
|
||||
for _, word := range words {
|
||||
word = strings.TrimSpace(word)
|
||||
if word != "" {
|
||||
cleanWords = append(cleanWords, word)
|
||||
}
|
||||
}
|
||||
|
||||
for _, resource := range resources {
|
||||
shouldSkip := false
|
||||
var matchedWords []string
|
||||
title := strings.ToLower(resource.Title)
|
||||
description := strings.ToLower(resource.Description)
|
||||
|
||||
for _, word := range words {
|
||||
word = strings.TrimSpace(word)
|
||||
if word != "" && (strings.Contains(title, strings.ToLower(word)) || strings.Contains(description, strings.ToLower(word))) {
|
||||
utils.Info(fmt.Sprintf("跳过包含违禁词 '%s' 的资源: %s", word, resource.Title))
|
||||
for _, word := range cleanWords {
|
||||
wordLower := strings.ToLower(word)
|
||||
if strings.Contains(title, wordLower) || strings.Contains(description, wordLower) {
|
||||
matchedWords = append(matchedWords, word)
|
||||
shouldSkip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !shouldSkip {
|
||||
if shouldSkip {
|
||||
// 标记为违禁词错误
|
||||
resource.ErrorMsg = fmt.Sprintf("存在违禁词: %s", strings.Join(matchedWords, ", "))
|
||||
forbiddenResources = append(forbiddenResources, resource)
|
||||
utils.Info(fmt.Sprintf("标记违禁词资源: %s, 违禁词: %s", resource.Title, strings.Join(matchedWords, ", ")))
|
||||
} else {
|
||||
filteredResources = append(filteredResources, resource)
|
||||
}
|
||||
}
|
||||
utils.Info(fmt.Sprintf("违禁词过滤后,剩余 %d 个资源需要转存", len(filteredResources)))
|
||||
utils.Info(fmt.Sprintf("违禁词过滤后,剩余 %d 个资源需要转存,违禁词资源 %d 个", len(filteredResources), len(forbiddenResources)))
|
||||
} else {
|
||||
filteredResources = resources
|
||||
}
|
||||
|
||||
// 注意:资源数量已在数据库查询时限制,无需再次限制
|
||||
|
||||
// 保存违禁词资源的错误信息
|
||||
for _, resource := range forbiddenResources {
|
||||
if err := a.resourceRepo.Update(resource); err != nil {
|
||||
utils.Error(fmt.Sprintf("保存违禁词错误信息失败 (ID: %d): %v", resource.ID, err))
|
||||
}
|
||||
}
|
||||
|
||||
// 并发自动转存
|
||||
resourceCh := make(chan *entity.Resource, len(filteredResources))
|
||||
for _, res := range filteredResources {
|
||||
@@ -220,7 +261,8 @@ func (a *AutoTransferScheduler) processAutoTransfer() {
|
||||
}(account)
|
||||
}
|
||||
wg.Wait()
|
||||
utils.Info(fmt.Sprintf("自动转存处理完成,账号数: %d,资源数: %d", len(validAccounts), len(filteredResources)))
|
||||
utils.Info(fmt.Sprintf("自动转存处理完成,账号数: %d,处理资源数: %d,违禁词资源数: %d",
|
||||
len(validAccounts), len(filteredResources), len(forbiddenResources)))
|
||||
}
|
||||
|
||||
// getQuarkPanID 获取夸克网盘ID
|
||||
@@ -241,7 +283,7 @@ func (a *AutoTransferScheduler) getQuarkPanID() (uint, error) {
|
||||
}
|
||||
|
||||
// getResourcesForTransfer 获取需要转存的资源
|
||||
func (a *AutoTransferScheduler) getResourcesForTransfer(quarkPanID uint) ([]*entity.Resource, error) {
|
||||
func (a *AutoTransferScheduler) getResourcesForTransfer(quarkPanID uint, limit int) ([]*entity.Resource, error) {
|
||||
// 获取最近24小时内的资源
|
||||
sinceTime := time.Now().Add(-24 * time.Hour)
|
||||
|
||||
@@ -251,7 +293,7 @@ func (a *AutoTransferScheduler) getResourcesForTransfer(quarkPanID uint) ([]*ent
|
||||
return nil, fmt.Errorf("资源仓库类型错误")
|
||||
}
|
||||
|
||||
return repoImpl.GetResourcesForTransfer(quarkPanID, sinceTime)
|
||||
return repoImpl.GetResourcesForTransfer(quarkPanID, sinceTime, limit)
|
||||
}
|
||||
|
||||
// transferResource 转存单个资源
|
||||
|
||||
@@ -186,6 +186,30 @@ func (r *ReadyResourceScheduler) convertReadyResourceToResource(readyResource en
|
||||
PanID: r.getPanIDByServiceType(serviceType),
|
||||
}
|
||||
|
||||
// 检查违禁词
|
||||
forbiddenWords, err := r.systemConfigRepo.GetConfigValue(entity.ConfigKeyForbiddenWords)
|
||||
if err == nil && forbiddenWords != "" {
|
||||
words := strings.Split(forbiddenWords, ",")
|
||||
var matchedWords []string
|
||||
title := strings.ToLower(resource.Title)
|
||||
description := strings.ToLower(resource.Description)
|
||||
|
||||
for _, word := range words {
|
||||
word = strings.TrimSpace(word)
|
||||
if word != "" {
|
||||
wordLower := strings.ToLower(word)
|
||||
if strings.Contains(title, wordLower) || strings.Contains(description, wordLower) {
|
||||
matchedWords = append(matchedWords, word)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(matchedWords) > 0 {
|
||||
utils.Warn(fmt.Sprintf("资源包含违禁词: %s, 违禁词: %s", resource.Title, strings.Join(matchedWords, ", ")))
|
||||
return fmt.Errorf("存在违禁词: %s", strings.Join(matchedWords, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
// 不是夸克,直接保存
|
||||
if serviceType != panutils.Quark {
|
||||
// 检测是否有效
|
||||
|
||||
1
web/components.d.ts
vendored
1
web/components.d.ts
vendored
@@ -19,6 +19,7 @@ declare module 'vue' {
|
||||
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||
NTabPane: typeof import('naive-ui')['NTabPane']
|
||||
NTabs: typeof import('naive-ui')['NTabs']
|
||||
NTag: typeof import('naive-ui')['NTag']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
|
||||
@@ -18,10 +18,6 @@
|
||||
{{ currentPageTitle }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- 页面描述 -->
|
||||
<!-- <div v-if="currentPageDescription && currentPageTitle !== '管理后台'" class="text-xs text-white/60 mt-1">
|
||||
{{ currentPageDescription }}
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div class="absolute left-4 top-4 flex items-center gap-2">
|
||||
@@ -37,7 +33,7 @@
|
||||
<!-- 用户信息 -->
|
||||
<div v-if="userStore.isAuthenticated" class="hidden sm:flex items-center gap-2">
|
||||
<span class="text-sm text-white/80">欢迎,{{ userStore.user?.username || '管理员' }}</span>
|
||||
<span class="px-2 py-1 bg-blue-600/80 rounded text-xs text-white">{{ userStore.user?.role || 'admin' }}</span>
|
||||
<n-tag type="success" size="small" round>{{ userStore.user?.role || '-' }}</n-tag>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
|
||||
@@ -74,9 +74,20 @@
|
||||
|
||||
<!-- 禁止词 -->
|
||||
<div class="md:col-span-2">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
违禁词
|
||||
</label>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
违禁词
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<n-button
|
||||
type="default"
|
||||
size="small"
|
||||
@click="openForbiddenWordsSource"
|
||||
>
|
||||
开源违禁词
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
<n-input
|
||||
v-model:value="config.forbiddenWords"
|
||||
type="textarea"
|
||||
@@ -236,15 +247,31 @@
|
||||
<div class="flex gap-2">
|
||||
<n-input
|
||||
v-model:value="config.apiToken"
|
||||
type="text"
|
||||
type="password"
|
||||
placeholder="输入API Token,用于公开API访问认证"
|
||||
:show-password-on="'click'"
|
||||
/>
|
||||
<n-button
|
||||
type="info"
|
||||
v-if="!config.apiToken"
|
||||
type="primary"
|
||||
@click="generateApiToken"
|
||||
>
|
||||
生成
|
||||
</n-button>
|
||||
<template v-else>
|
||||
<n-button
|
||||
type="primary"
|
||||
@click="copyApiToken"
|
||||
>
|
||||
复制
|
||||
</n-button>
|
||||
<n-button
|
||||
type="default"
|
||||
@click="generateApiToken"
|
||||
>
|
||||
重新生成
|
||||
</n-button>
|
||||
</template>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
用于公开API的访问认证,建议使用随机字符串
|
||||
@@ -309,6 +336,7 @@ const notification = useNotification()
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false)
|
||||
const loadingForbiddenWords = ref(false)
|
||||
const config = ref({
|
||||
// SEO 配置
|
||||
siteTitle: '老九网盘资源数据库',
|
||||
@@ -332,24 +360,29 @@ const config = ref({
|
||||
})
|
||||
|
||||
// 系统配置状态(用于SEO)
|
||||
const systemConfig = ref(null)
|
||||
const systemConfig = ref({
|
||||
site_title: '老九网盘资源数据库',
|
||||
site_description: '系统配置管理页面',
|
||||
keywords: '系统配置,管理',
|
||||
author: '系统管理员'
|
||||
})
|
||||
const originalConfig = ref(null)
|
||||
|
||||
// 页面元数据 - 移到变量声明之后
|
||||
useHead({
|
||||
title: () => systemConfig.value?.site_title ? `${systemConfig.value.site_title} - 系统配置` : '系统配置 - 老九网盘资源数据库',
|
||||
title: () => `${systemConfig.value.site_title} - 系统配置`,
|
||||
meta: [
|
||||
{
|
||||
name: 'description',
|
||||
content: () => systemConfig.value?.site_description || '系统配置管理页面'
|
||||
content: () => systemConfig.value.site_description
|
||||
},
|
||||
{
|
||||
name: 'keywords',
|
||||
content: () => systemConfig.value?.keywords || '系统配置,管理'
|
||||
content: () => systemConfig.value.keywords
|
||||
},
|
||||
{
|
||||
name: 'author',
|
||||
content: () => systemConfig.value?.author || '系统管理员'
|
||||
content: () => systemConfig.value.author
|
||||
}
|
||||
]
|
||||
})
|
||||
@@ -370,13 +403,13 @@ const loadConfig = async () => {
|
||||
author: response.author || '系统管理员',
|
||||
copyright: response.copyright || '© 2024 老九网盘资源数据库',
|
||||
autoProcessReadyResources: response.auto_process_ready_resources || false,
|
||||
autoProcessInterval: response.auto_process_interval || 30,
|
||||
autoProcessInterval: String(response.auto_process_interval || 30),
|
||||
autoTransferEnabled: response.auto_transfer_enabled || false, // 新增
|
||||
autoTransferLimitDays: response.auto_transfer_limit_days || 30, // 新增:自动转存限制天数
|
||||
autoTransferMinSpace: response.auto_transfer_min_space || 500, // 新增:最小存储空间(GB)
|
||||
autoTransferLimitDays: String(response.auto_transfer_limit_days || 30), // 新增:自动转存限制天数
|
||||
autoTransferMinSpace: String(response.auto_transfer_min_space || 500), // 新增:最小存储空间(GB)
|
||||
autoFetchHotDramaEnabled: response.auto_fetch_hot_drama_enabled || false, // 新增
|
||||
forbiddenWords: response.forbidden_words || '',
|
||||
pageSize: response.page_size || 100,
|
||||
forbiddenWords: formatForbiddenWordsForDisplay(response.forbidden_words || ''),
|
||||
pageSize: String(response.page_size || 100),
|
||||
maintenanceMode: response.maintenance_mode || false,
|
||||
apiToken: response.api_token || '' // 加载API Token
|
||||
}
|
||||
@@ -421,25 +454,25 @@ const saveConfig = async () => {
|
||||
changes.auto_process_ready_resources = currentConfig.autoProcessReadyResources
|
||||
}
|
||||
if (currentConfig.autoProcessInterval !== original.autoProcessInterval) {
|
||||
changes.auto_process_interval = currentConfig.autoProcessInterval
|
||||
changes.auto_process_interval = parseInt(currentConfig.autoProcessInterval) || 0
|
||||
}
|
||||
if (currentConfig.autoTransferEnabled !== original.autoTransferEnabled) {
|
||||
changes.auto_transfer_enabled = currentConfig.autoTransferEnabled
|
||||
}
|
||||
if (currentConfig.autoTransferLimitDays !== original.autoTransferLimitDays) {
|
||||
changes.auto_transfer_limit_days = currentConfig.autoTransferLimitDays
|
||||
changes.auto_transfer_limit_days = parseInt(currentConfig.autoTransferLimitDays) || 0
|
||||
}
|
||||
if (currentConfig.autoTransferMinSpace !== original.autoTransferMinSpace) {
|
||||
changes.auto_transfer_min_space = currentConfig.autoTransferMinSpace
|
||||
changes.auto_transfer_min_space = parseInt(currentConfig.autoTransferMinSpace) || 0
|
||||
}
|
||||
if (currentConfig.autoFetchHotDramaEnabled !== original.autoFetchHotDramaEnabled) {
|
||||
changes.auto_fetch_hot_drama_enabled = currentConfig.autoFetchHotDramaEnabled
|
||||
}
|
||||
if (currentConfig.forbiddenWords !== original.forbiddenWords) {
|
||||
changes.forbidden_words = currentConfig.forbiddenWords
|
||||
changes.forbidden_words = formatForbiddenWordsForSave(currentConfig.forbiddenWords)
|
||||
}
|
||||
if (currentConfig.pageSize !== original.pageSize) {
|
||||
changes.page_size = currentConfig.pageSize
|
||||
changes.page_size = parseInt(currentConfig.pageSize) || 0
|
||||
}
|
||||
if (currentConfig.maintenanceMode !== original.maintenanceMode) {
|
||||
changes.maintenance_mode = currentConfig.maintenanceMode
|
||||
@@ -504,6 +537,65 @@ const generateApiToken = () => {
|
||||
})
|
||||
};
|
||||
|
||||
// 复制API Token
|
||||
const copyApiToken = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(config.value.apiToken);
|
||||
notification.success({
|
||||
content: 'API Token已复制到剪贴板',
|
||||
duration: 3000
|
||||
});
|
||||
} catch (err) {
|
||||
// 降级方案:使用传统的复制方法
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = config.value.apiToken;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
notification.success({
|
||||
content: 'API Token已复制到剪贴板',
|
||||
duration: 3000
|
||||
});
|
||||
} catch (fallbackErr) {
|
||||
notification.error({
|
||||
content: '复制失败,请手动复制',
|
||||
duration: 3000
|
||||
});
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 打开违禁词源文件
|
||||
const openForbiddenWordsSource = () => {
|
||||
const url = 'https://raw.githubusercontent.com/ctwj/urldb/refs/heads/main/db/forbidden.txt'
|
||||
window.open(url, '_blank', 'noopener,noreferrer')
|
||||
}
|
||||
|
||||
// 格式化违禁词用于显示(逗号分隔转为多行)
|
||||
const formatForbiddenWordsForDisplay = (forbiddenWords) => {
|
||||
if (!forbiddenWords) return ''
|
||||
|
||||
// 按逗号分割,过滤空字符串,然后按行显示
|
||||
return forbiddenWords.split(',')
|
||||
.map(word => word.trim())
|
||||
.filter(word => word.length > 0)
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
// 格式化违禁词用于保存(多行转为逗号分隔)
|
||||
const formatForbiddenWordsForSave = (forbiddenWords) => {
|
||||
if (!forbiddenWords) return ''
|
||||
|
||||
// 按行分割,过滤空行,然后用逗号连接
|
||||
return forbiddenWords.split('\n')
|
||||
.map(line => line.trim())
|
||||
.filter(line => line.length > 0)
|
||||
.join(',')
|
||||
}
|
||||
|
||||
// 页面加载时获取配置
|
||||
onMounted(() => {
|
||||
loadConfig()
|
||||
|
||||
Reference in New Issue
Block a user