Files
urldb/web/pages/admin/seo.vue
2025-11-25 02:05:59 +08:00

846 lines
28 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<AdminPageLayout>
<!-- 页面头部 - 标题 -->
<template #page-header>
<div>
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">SEO管理</h1>
<p class="text-gray-600 dark:text-gray-400">搜索引擎优化管理</p>
</div>
</template>
<!-- 内容区 -->
<template #content>
<div class="config-content h-full">
<!-- Tab导航 -->
<n-tabs v-model:value="activeTab" type="line" animated>
<!-- Google Index Tab -->
<n-tab-pane name="google-index" tab="Google Index">
<GoogleIndexTab
:system-config="systemConfig"
:google-index-config="googleIndexConfig"
:google-index-stats="googleIndexStats"
:tasks="googleIndexTasks"
:credentials-status="credentialsStatus"
:credentials-status-message="credentialsStatusMessage"
:config-loading="configLoading"
:manual-check-loading="manualCheckLoading"
:submit-sitemap-loading="submitSitemapLoading"
:tasks-loading="tasksLoading"
:pagination="googleIndexPagination"
@update:google-index-config="updateGoogleIndexConfig"
@show-verification="showVerificationModal = true"
@show-credentials-guide="showCredentialsGuide = true"
@select-credentials-file="selectCredentialsFile"
@manual-check-urls="manualCheckURLs"
@refresh-status="refreshGoogleIndexStatus"
@view-task-items="viewTaskItems"
@start-task="startTask"
/>
</n-tab-pane>
<!-- Sitemap管理 Tab -->
<n-tab-pane name="sitemap" tab="Sitemap管理">
<SitemapTab
:system-config="systemConfig"
:sitemap-config="sitemapConfig"
:sitemap-stats="sitemapStats"
:config-loading="configLoading"
:is-generating="isGenerating"
:generate-status="generateStatus"
@update:sitemap-config="updateSitemapConfig"
@refresh-status="refreshSitemapStatus"
/>
</n-tab-pane>
<!-- 站点提交 Tab -->
<n-tab-pane name="site-submit" tab="站点提交">
<SiteSubmitTab
:last-submit-time="lastSubmitTime"
@update:last-submit-time="updateLastSubmitTime"
/>
</n-tab-pane>
<!-- 外链建设 Tab -->
<n-tab-pane name="link-building" tab="外链建设">
<LinkBuildingTab
:link-stats="linkStats"
:link-list="linkList"
:loading="linkLoading"
:pagination="linkPagination"
@add-new-link="addNewLink"
@edit-link="editLink"
@delete-link="deleteLink"
@load-link-list="loadLinkList"
/>
</n-tab-pane>
</n-tabs>
</div>
</template>
</AdminPageLayout>
<!-- URL检查模态框 -->
<n-modal v-model:show="urlCheckModal.show" preset="card" title="手动检查URL" style="max-width: 600px;">
<div class="space-y-4">
<p class="text-gray-600 dark:text-gray-400">输入要检查索引状态的URL每行一个</p>
<n-input
v-model:value="urlCheckModal.urls"
type="textarea"
:autosize="{ minRows: 4, maxRows: 8 }"
placeholder="https://yoursite.com/page1&#10;https://yoursite.com/page2"
/>
<div class="flex justify-end space-x-2">
<n-button @click="urlCheckModal.show = false">取消</n-button>
<n-button type="primary" @click="confirmManualCheckURLs" :loading="manualCheckLoading">确认</n-button>
</div>
</div>
</n-modal>
<!-- 所有权验证模态框 -->
<n-modal v-model:show="showVerificationModal" preset="card" title="站点所有权验证" style="max-width: 600px;">
<div class="space-y-6">
<div class="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-4">
<div class="flex">
<div class="flex-shrink-0">
<i class="fas fa-info-circle text-blue-500 dark:text-blue-400 text-xl"></i>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-blue-800 dark:text-blue-200">DNS方式验证</h3>
<div class="mt-2 text-sm text-blue-700 dark:text-blue-300">
<p>推荐使用DNS方式验证站点所有权这是最安全和可靠的方法</p>
<ol class="list-decimal list-inside mt-2 space-y-1">
<li>登录您的域名注册商或DNS管理平台</li>
<li>添加一条TXT记录</li>
<li>在Google Search Console中输入您的验证字符串</li>
<li>验证DNS TXT记录是否生效</li>
</ol>
</div>
</div>
</div>
</div>
<div class="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4">
<div class="flex">
<div class="flex-shrink-0">
<i class="fas fa-exclamation-triangle text-yellow-500 dark:text-yellow-400 text-xl"></i>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium text-yellow-800 dark:text-yellow-200">注意事项</h3>
<div class="mt-2 text-sm text-yellow-700 dark:text-yellow-300">
<ul class="list-disc list-inside space-y-1">
<li>DNS验证比HTML标签更安全不易被其他网站复制</li>
<li>验证成功后Google会自动检测您的站点所有权</li>
<li>如果您的域名服务商不支持TXT记录请联系客服寻求帮助</li>
</ul>
</div>
</div>
</div>
</div>
<div class="flex justify-end pt-2">
<n-button type="primary" @click="showVerificationModal = false">
确定
</n-button>
</div>
</div>
</n-modal>
<!-- 申请凭据说明抽屉 -->
<n-drawer v-model:show="showCredentialsGuide" :width="600" placement="right">
<n-drawer-content title="如何申请Google Search Console API凭据" closable>
<div class="space-y-6">
<!-- 步骤1 -->
<div class="border-l-4 border-blue-500 pl-4">
<h4 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">
<i class="fas fa-number-1 text-blue-500 mr-2"></i>创建Google Cloud项目
</h4>
<p class="text-gray-600 dark:text-gray-400 mb-3">
首先需要在Google Cloud Console中创建一个新项目或选择现有项目
</p>
<ol class="list-decimal list-inside space-y-2 text-sm text-gray-600 dark:text-gray-400">
<li>访问 <a href="https://console.cloud.google.com/" target="_blank" class="text-blue-600 hover:text-blue-800 underline">Google Cloud Console</a></li>
<li>点击顶部的项目选择器</li>
<li>点击"新建项目"或选择现有项目</li>
<li>输入项目名称点击"创建"</li>
</ol>
</div>
<!-- 步骤2 -->
<div class="border-l-4 border-green-500 pl-4">
<h4 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">
<i class="fas fa-number-2 text-green-500 mr-2"></i>启用Search Console API
</h4>
<p class="text-gray-600 dark:text-gray-400 mb-3">
在项目中启用Google Search Console API
</p>
<ol class="list-decimal list-inside space-y-2 text-sm text-gray-600 dark:text-gray-400">
<li>在导航菜单中选择"API和服务" > "库"</li>
<li>搜索"Google Search Console API"</li>
<li>点击搜索结果中的"Google Search Console API"</li>
<li>点击"启用"按钮</li>
</ol>
</div>
<!-- 步骤3 -->
<div class="border-l-4 border-yellow-500 pl-4">
<h4 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">
<i class="fas fa-number-3 text-yellow-500 mr-2"></i>创建服务账号
</h4>
<p class="text-gray-600 dark:text-gray-400 mb-3">
创建服务账号并生成JSON密钥文件
</p>
<ol class="list-decimal list-inside space-y-2 text-sm text-gray-600 dark:text-gray-400">
<li>在导航菜单中选择"API和服务" > "凭据"</li>
<li>点击"创建凭据" > "服务账号"</li>
<li>输入服务账号名称google-index-api</li>
<li>点击"创建并继续"</li>
<li>在角色选择中选择"项目" > "编辑者"</li>
<li>点击"继续"然后点击"完成"</li>
</ol>
</div>
<!-- 步骤4 -->
<div class="border-l-4 border-purple-500 pl-4">
<h4 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">
<i class="fas fa-number-4 text-purple-500 mr-2"></i>生成JSON密钥
</h4>
<p class="text-gray-600 dark:text-gray-400 mb-3">
为服务账号生成JSON格式的密钥文件
</p>
<ol class="list-decimal list-inside space-y-2 text-sm text-gray-600 dark:text-gray-400">
<li>在服务账号列表中找到刚创建的服务账号</li>
<li>点击服务账号名称进入详情页面</li>
<li>切换到"密钥"标签页</li>
<li>点击"添加密钥" > "创建新密钥"</li>
<li>选择"JSON"作为密钥类型</li>
<li>点击"创建"JSON文件将自动下载</li>
</ol>
</div>
<!-- 步骤5 -->
<div class="border-l-4 border-red-500 pl-4">
<h4 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">
<i class="fas fa-number-5 text-red-500 mr-2"></i>验证网站所有权
</h4>
<p class="text-gray-600 dark:text-gray-400 mb-3">
在Google Search Console中验证网站并添加服务账号权限
</p>
<ol class="list-decimal list-inside space-y-2 text-sm text-gray-600 dark:text-gray-400">
<li>访问 <a href="https://search.google.com/search-console/" target="_blank" class="text-blue-600 hover:text-blue-800 underline">Google Search Console</a></li>
<li>添加属性并验证网站所有权</li>
<li>在设置中找到"用户和权限"</li>
<li>点击"添加用户"输入服务账号的邮箱地址</li>
<li>授予"所有者""完整"权限</li>
</ol>
</div>
<!-- 注意事项 -->
<div class="bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4">
<h5 class="font-semibold text-yellow-800 dark:text-yellow-200 mb-2">
<i class="fas fa-exclamation-triangle mr-2"></i>重要注意事项
</h5>
<ul class="space-y-1 text-sm text-yellow-700 dark:text-yellow-300">
<li> 请妥善保管下载的JSON密钥文件不要泄露给他人</li>
<li> 服务账号邮箱地址通常格式为xxx@xxx.iam.gserviceaccount.com</li>
<li> API配额有限制请合理使用避免超出限制</li>
<li> 确保网站已在Search Console中验证所有权</li>
</ul>
</div>
<!-- 完成按钮 -->
<div class="flex justify-end">
<n-button type="primary" @click="showCredentialsGuide = false">
我已了解
</n-button>
</div>
</div>
</n-drawer-content>
</n-drawer>
<!-- 隐藏的文件输入 -->
<input
type="file"
ref="credentialsFileInput"
accept=".json"
@change="handleCredentialsFileSelect"
style="display: none;"
/>
</template>
<script setup lang="ts">
import AdminPageLayout from '~/components/AdminPageLayout.vue'
import GoogleIndexTab from '~/components/Admin/GoogleIndexTab.vue'
import SitemapTab from '~/components/Admin/SitemapTab.vue'
import SiteSubmitTab from '~/components/Admin/SiteSubmitTab.vue'
import LinkBuildingTab from '~/components/Admin/LinkBuildingTab.vue'
// SEO管理页面
definePageMeta({
layout: 'admin'
})
import { useMessage } from 'naive-ui'
import { useApi } from '~/composables/useApi'
import { ref, onMounted, watch } from 'vue'
// 获取消息组件
const message = useMessage()
// 当前激活的Tab - 默认显示 Google Index
const activeTab = ref('google-index')
// 获取系统配置
const systemConfig = ref<any>(null)
// Google索引配置
const googleIndexConfig = ref({
enabled: false,
siteURL: '',
credentialsFile: '',
checkInterval: 60,
batchSize: 100,
concurrency: 5
})
// 凭据验证相关
const credentialsStatus = ref<string | null>(null)
const credentialsStatusMessage = ref('')
const credentialsFileInput = ref<HTMLInputElement | null>(null)
// 申请凭据抽屉显示状态
const showCredentialsGuide = ref(false)
// 所有权验证相关
const showVerificationModal = ref(false)
// Google索引统计
const googleIndexStats = ref({
totalURLs: 0,
indexedURLs: 0,
errorURLs: 0,
totalTasks: 0,
runningTasks: 0,
completedTasks: 0,
failedTasks: 0
})
// Google索引任务列表
const googleIndexTasks = ref([])
const tasksLoading = ref(false)
// 分页配置
const googleIndexPagination = ref({
page: 1,
pageSize: 10,
showSizePicker: true,
pageSizes: [10, 20, 50],
itemCount: 0,
onChange: (page: number) => {
googleIndexPagination.value.page = page
loadGoogleIndexTasks()
},
onUpdatePageSize: (pageSize: number) => {
googleIndexPagination.value.pageSize = pageSize
googleIndexPagination.value.page = 1
loadGoogleIndexTasks()
}
})
// 模态框状态
const urlCheckModal = ref({
show: false,
urls: ''
})
// 加载状态
const configLoading = ref(false)
const manualCheckLoading = ref(false)
const submitSitemapLoading = ref(false)
// Sitemap管理相关
const sitemapConfig = ref({
autoGenerate: false,
lastGenerate: '',
lastUpdate: ''
})
const sitemapStats = ref({
total_resources: 0,
total_pages: 0,
last_generate: ''
})
const isGenerating = ref(false)
const generateStatus = ref('')
// 最后提交时间
const lastSubmitTime = ref({
baidu: '',
google: '',
bing: '',
sogou: '',
shenma: '',
so360: ''
})
// 外链统计
const linkStats = ref({
total: 156,
valid: 142,
pending: 8,
invalid: 6
})
// 外链列表
const linkList = ref([
{
id: 1,
url: 'https://example1.com',
title: '示例外链1',
status: 'valid',
domain: 'example1.com',
created_at: '2024-01-15'
},
{
id: 2,
url: 'https://example2.com',
title: '示例外链2',
status: 'pending',
domain: 'example2.com',
created_at: '2024-01-16'
},
{
id: 3,
url: 'https://example3.com',
title: '示例外链3',
status: 'invalid',
domain: 'example3.com',
created_at: '2024-01-17'
}
])
const linkLoading = ref(false)
// 分页配置
const linkPagination = ref({
page: 1,
pageSize: 10,
showSizePicker: true,
pageSizes: [10, 20, 50],
onChange: (page: number) => {
linkPagination.value.page = page
loadLinkList()
},
onUpdatePageSize: (pageSize: number) => {
linkPagination.value.pageSize = pageSize
linkPagination.value.page = 1
loadLinkList()
}
})
// 加载系统配置
const loadSystemConfig = async () => {
try {
const { useSystemConfigStore } = await import('~/stores/systemConfig')
const systemConfigStore = useSystemConfigStore()
await systemConfigStore.initConfig(true, true)
systemConfig.value = systemConfigStore.config
} catch (error) {
console.error('获取系统配置失败:', error)
}
}
// 加载Google索引配置
const loadGoogleIndexConfig = async () => {
try {
console.log('开始加载 Google 索引配置...')
const api = useApi()
const configs = await api.googleIndexApi.getGoogleIndexConfig()
console.log('获取到的配置:', configs)
if (configs) {
// 查找general配置
const generalConfig = configs.find((c: any) => c.group === 'general')
const authConfig = configs.find((c: any) => c.group === 'auth')
console.log('找到的配置 - general:', generalConfig, 'auth:', authConfig)
let newConfig = { ...googleIndexConfig.value }
if (generalConfig) {
const configData = JSON.parse(generalConfig.value)
newConfig.enabled = configData.enabled || false
newConfig.siteURL = configData.siteURL || ''
newConfig.checkInterval = configData.checkInterval || 60
newConfig.batchSize = configData.batchSize || 100
newConfig.concurrency = configData.concurrency || 5
}
if (authConfig) {
console.log('解析 auth 配置:', authConfig.value)
const authData = JSON.parse(authConfig.value)
console.log('解析后的 authData:', authData)
newConfig.credentialsFile = authData.credentialsFile || authData.credentials_file || ''
console.log('设置凭据文件路径:', newConfig.credentialsFile)
}
// 强制触发响应式更新
googleIndexConfig.value = newConfig
console.log('最终配置:', googleIndexConfig.value)
}
} catch (error) {
console.error('获取Google索引配置失败:', error)
}
}
// 选择凭据文件
const selectCredentialsFile = () => {
if (credentialsFileInput.value) {
credentialsFileInput.value.click()
}
}
// 处理凭据文件选择
const handleCredentialsFileSelect = async (event: Event) => {
const target = event.target as HTMLInputElement
const file = target.files?.[0]
if (!file) {
return
}
// 验证文件类型
if (file.type !== 'application/json' && !file.name.endsWith('.json')) {
message.error('请上传JSON格式的凭据文件')
return
}
// 验证文件大小 (2MB限制)
if (file.size > 2 * 1024 * 1024) {
message.error('文件大小不能超过2MB')
return
}
// 上传文件
try {
const api = useApi()
const response = await api.googleIndexApi.uploadCredentials(file)
// 检查API是否成功success字段为true且包含有效的文件路径
if (response?.success === true && response?.file_path) {
console.log('上传成功,文件路径:', response.file_path)
// 统一路径格式为 Unix 格式
const normalizedPath = response.file_path.replace(/\\/g, '/')
console.log('标准化路径:', normalizedPath)
// 强制触发响应式更新
googleIndexConfig.value = {
...googleIndexConfig.value,
credentialsFile: normalizedPath
}
console.log('更新后的 googleIndexConfig:', googleIndexConfig.value)
message.success(response.message || '凭据文件上传成功,请验证凭据')
// 清空文件输入以允许重新选择相同文件
if (credentialsFileInput.value) {
credentialsFileInput.value.value = ''
}
// 上传成功后立即更新后端配置并重新加载配置
try {
const configData = {
group: 'auth',
key: 'credentials_file',
value: JSON.stringify({
credentials_file: googleIndexConfig.value.credentialsFile.replace(/\\/g, '/')
})
}
console.log('更新后端配置,发送数据:', JSON.stringify(configData, null, 2))
const updateResponse = await api.googleIndexApi.updateGoogleIndexGroupConfig(configData)
console.log('后端配置更新响应:', updateResponse)
// 等待一下确保后端处理完成
await new Promise(resolve => setTimeout(resolve, 500))
// 重新加载配置以确保UI状态与后端同步
console.log('重新加载配置...')
await loadGoogleIndexConfig()
console.log('配置重新加载完成')
} catch (configError) {
console.error('更新配置失败:', configError)
message.error('配置更新失败,但文件已上传')
// 即使配置更新失败,也尝试刷新状态
setTimeout(async () => {
console.log('延迟重新加载配置...')
await loadGoogleIndexConfig()
}, 1000)
}
} else {
// 如果API调用成功但返回的数据有问题或者API调用失败
message.error(response?.message || '上传响应格式错误')
}
} catch (error: any) {
console.error('凭据文件上传失败:', error)
message.error('凭据文件上传失败: ' + (error?.message || '未知错误'))
}
}
// 更新Google索引配置
const updateGoogleIndexConfig = async () => {
configLoading.value = true
try {
const api = useApi()
// 更新general配置
await api.googleIndexApi.updateGoogleIndexGroupConfig({
group: 'general',
key: 'general',
value: JSON.stringify({
enabled: googleIndexConfig.value.enabled,
siteURL: systemConfig.value?.site_url || googleIndexConfig.value.siteURL,
checkInterval: googleIndexConfig.value.checkInterval,
batchSize: googleIndexConfig.value.batchSize,
concurrency: googleIndexConfig.value.concurrency || 5
})
})
message.success('Google索引配置已更新')
} catch (error) {
console.error('更新Google索引配置失败:', error)
message.error('更新配置失败')
} finally {
configLoading.value = false
}
}
// 刷新Google索引状态
const refreshGoogleIndexStatus = async () => {
try {
const api = useApi()
// 加载统计信息
const statsResponse = await api.googleIndexApi.getGoogleIndexStatus()
if (statsResponse) {
googleIndexStats.value = statsResponse
}
// 加载任务列表
await loadGoogleIndexTasks()
} catch (error) {
console.error('刷新Google索引状态失败:', error)
message.error('刷新状态失败')
}
}
// 加载Google索引任务列表
const loadGoogleIndexTasks = async () => {
tasksLoading.value = true
try {
const api = useApi()
const response = await api.googleIndexApi.getGoogleIndexTasks({
page: googleIndexPagination.value.page,
pageSize: googleIndexPagination.value.pageSize
})
if (response) {
googleIndexTasks.value = response.tasks || []
googleIndexPagination.value.itemCount = response.total || 0
}
} catch (error) {
console.error('加载Google索引任务列表失败:', error)
message.error('加载任务列表失败')
} finally {
tasksLoading.value = false
}
}
// 手动检查URL
const manualCheckURLs = () => {
urlCheckModal.value.show = true
urlCheckModal.value.urls = ''
}
// 确认手动检查URL
const confirmManualCheckURLs = async () => {
const urls = urlCheckModal.value.urls.split('\n').filter(url => url.trim() !== '')
if (urls.length === 0) {
message.warning('请至少输入一个URL')
return
}
manualCheckLoading.value = true
try {
const api = useApi()
const response = await api.googleIndexApi.createGoogleIndexTask({
title: `手动URL检查任务 - ${new Date().toLocaleString('zh-CN')}`,
type: 'status_check',
description: `手动检查 ${urls.length} 个URL的索引状态`,
URLs: urls
})
if (response) {
message.success('URL检查任务已创建')
urlCheckModal.value.show = false
await refreshGoogleIndexStatus()
}
} catch (error: any) {
console.error('手动检查URL失败:', error)
const errorMsg = error?.response?.data?.message || error?.message || '手动检查URL失败'
message.error('手动检查URL失败: ' + errorMsg)
} finally {
manualCheckLoading.value = false
}
}
// 查看任务详情
const viewTaskItems = async (taskId: number) => {
try {
const api = useApi()
const response = await api.googleIndexApi.getGoogleIndexTaskItems(taskId)
if (response) {
const items = response.items || []
// 创建任务详情内容
let content = `任务 ${taskId} 详情:\n\n`
if (items.length === 0) {
content += '暂无任务项'
} else {
items.forEach((item: any, index: number) => {
content += `--- 任务项 ${index + 1} ---\n`
content += `URL: ${item.URL || 'N/A'}\n`
content += `状态: ${item.status || 'N/A'}\n`
content += `索引状态: ${item.indexStatus || 'N/A'}\n`
content += `移动友好: ${item.mobileFriendly ? '是' : '否'}\n`
content += `状态码: ${item.statusCode || 'N/A'}\n`
content += `最后抓取: ${item.lastCrawled ? new Date(item.lastCrawled).toLocaleString('zh-CN') : 'N/A'}\n`
// 错误信息处理
const errorMsg = item.errorMessage || item.outputData?.error || '无'
content += `错误信息: ${errorMsg}\n`
content += '\n'
})
}
// 使用更好的显示方式
if (window.confirm(content + '\n\n点击确定关闭此窗口')) {
// 用户确认后关闭
}
}
} catch (error: any) {
console.error('获取任务详情失败:', error)
const errorMsg = error?.response?.data?.message || error?.message || '获取任务详情失败'
message.error('获取任务详情失败: ' + errorMsg)
}
}
// 启动任务
const startTask = async (taskId: number) => {
try {
const api = useApi()
const response = await api.googleIndexApi.startGoogleIndexTask(taskId)
if (response) {
message.success('任务已启动')
await loadGoogleIndexTasks()
}
} catch (error: any) {
console.error('启动任务失败:', error)
const errorMsg = error?.response?.data?.message || error?.message || '启动任务失败'
message.error('启动任务失败: ' + errorMsg)
}
}
// 获取Sitemap配置
const loadSitemapConfig = async () => {
try {
const api = useApi()
const response = await api.sitemapApi.getSitemapConfig()
if (response) {
sitemapConfig.value = response
}
} catch (error) {
message.error('获取Sitemap配置失败')
}
}
// 更新Sitemap配置
const updateSitemapConfig = async (value: boolean) => {
configLoading.value = true
try {
const api = useApi()
await api.sitemapApi.updateSitemapConfig({
autoGenerate: value,
lastGenerate: sitemapConfig.value.lastGenerate,
lastUpdate: new Date().toISOString()
})
message.success(value ? '自动生成功能已开启' : '自动生成功能已关闭')
} catch (error) {
message.error('更新配置失败')
} finally {
configLoading.value = false
}
}
// 刷新Sitemap状态
const refreshSitemapStatus = async () => {
try {
const api = useApi()
const response = await api.sitemapApi.getSitemapStatus()
if (response) {
sitemapStats.value = response
generateStatus.value = '状态已刷新'
}
} catch (error: any) {
const errorMsg = error?.response?.data?.message || error?.message || '刷新状态失败'
message.error('刷新状态失败: ' + errorMsg)
}
}
// 更新最后提交时间
const updateLastSubmitTime = (engine: string, time: string) => {
lastSubmitTime.value[engine as keyof typeof lastSubmitTime.value] = time
}
// 加载外链列表
const loadLinkList = () => {
linkLoading.value = true
setTimeout(() => {
linkLoading.value = false
}, 1000)
}
// 添加新外链
const addNewLink = () => {
message.info('添加外链功能开发中')
}
// 编辑外链
const editLink = (row: any) => {
message.info(`编辑外链: ${row.title}`)
}
// 删除外链
const deleteLink = (row: any) => {
message.warning(`删除外链: ${row.title}`)
}
// 初始化
onMounted(async () => {
await loadSystemConfig()
await loadGoogleIndexConfig()
await refreshGoogleIndexStatus()
await loadSitemapConfig()
await refreshSitemapStatus()
loadLinkList()
})
</script>
<style scoped>
/* SEO管理页面样式 */
.config-content {
padding: 8px;
background-color: var(--color-white, #ffffff);
}
.dark .config-content {
background-color: var(--color-dark-bg, #1f2937);
}
</style>