update: add site_url config

This commit is contained in:
Kerwin
2025-11-21 09:30:33 +08:00
parent 8708e869a4
commit d113dcd926
10 changed files with 130 additions and 24 deletions

9
.gitignore vendored
View File

@@ -123,4 +123,11 @@ dist/
.dockerignore
# Air live reload
tmp/
tmp/
#
data/
.claude/
main
output.zip
CLAUDE.md

View File

@@ -112,6 +112,8 @@ func SystemConfigToResponse(configs []entity.SystemConfig) *dto.SystemConfigResp
response.TelegramQrImage = config.Value
case entity.ConfigKeyQrCodeStyle:
response.QrCodeStyle = config.Value
case entity.ConfigKeyWebsiteURL:
response.SiteURL = config.Value
}
}
@@ -271,6 +273,10 @@ func RequestToSystemConfig(req *dto.SystemConfigRequest) []entity.SystemConfig {
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyQrCodeStyle, Value: *req.QrCodeStyle, Type: entity.ConfigTypeString})
updatedKeys = append(updatedKeys, entity.ConfigKeyQrCodeStyle)
}
if req.SiteURL != nil {
configs = append(configs, entity.SystemConfig{Key: entity.ConfigKeyWebsiteURL, Value: *req.SiteURL, Type: entity.ConfigTypeString})
updatedKeys = append(updatedKeys, entity.ConfigKeyWebsiteURL)
}
// 记录更新的配置项
if len(updatedKeys) > 0 {
@@ -298,6 +304,7 @@ func SystemConfigToPublicResponse(configs []entity.SystemConfig) map[string]inte
entity.ConfigResponseFieldMaintenanceMode: false,
entity.ConfigResponseFieldEnableRegister: true, // 默认开启注册功能
entity.ConfigResponseFieldThirdPartyStatsCode: "",
entity.ConfigResponseFieldWebsiteURL: "",
}
// 将键值对转换为map过滤掉敏感配置
@@ -353,6 +360,8 @@ func SystemConfigToPublicResponse(configs []entity.SystemConfig) map[string]inte
response["telegram_qr_image"] = config.Value
case entity.ConfigKeyQrCodeStyle:
response["qr_code_style"] = config.Value
case entity.ConfigKeyWebsiteURL:
response[entity.ConfigResponseFieldWebsiteURL] = config.Value
case entity.ConfigKeyAutoProcessReadyResources:
if val, err := strconv.ParseBool(config.Value); err == nil {
response["auto_process_ready_resources"] = val
@@ -420,5 +429,6 @@ func getDefaultConfigResponse() *dto.SystemConfigResponse {
WechatSearchImage: entity.ConfigDefaultWechatSearchImage,
TelegramQrImage: entity.ConfigDefaultTelegramQrImage,
QrCodeStyle: entity.ConfigDefaultQrCodeStyle,
SiteURL: entity.ConfigDefaultWebsiteURL,
}
}

View File

@@ -50,6 +50,9 @@ type SystemConfigRequest struct {
WechatSearchImage *string `json:"wechat_search_image,omitempty"`
TelegramQrImage *string `json:"telegram_qr_image,omitempty"`
QrCodeStyle *string `json:"qr_code_style,omitempty"`
// 网站URL配置
SiteURL *string `json:"site_url,omitempty"`
}
// SystemConfigResponse 系统配置响应
@@ -106,6 +109,9 @@ type SystemConfigResponse struct {
WechatSearchImage string `json:"wechat_search_image"`
TelegramQrImage string `json:"telegram_qr_image"`
QrCodeStyle string `json:"qr_code_style"`
// 网站URL配置
SiteURL string `json:"site_url"`
}
// SystemConfigItem 单个配置项

View File

@@ -170,6 +170,9 @@ const (
ConfigResponseFieldWechatSearchImage = "wechat_search_image"
ConfigResponseFieldTelegramQrImage = "telegram_qr_image"
ConfigResponseFieldQrCodeStyle = "qr_code_style"
// 网站URL配置字段
ConfigResponseFieldWebsiteURL = "site_url"
)
// ConfigDefaultValue 配置默认值常量
@@ -245,4 +248,7 @@ const (
ConfigDefaultWechatSearchImage = ""
ConfigDefaultTelegramQrImage = ""
ConfigDefaultQrCodeStyle = "Plain"
// 网站URL配置默认值
ConfigDefaultWebsiteURL = ""
)

View File

@@ -143,6 +143,7 @@ func (r *SystemConfigRepositoryImpl) GetOrCreateDefault() ([]entity.SystemConfig
{Key: entity.ConfigKeyWechatSearchImage, Value: entity.ConfigDefaultWechatSearchImage, Type: entity.ConfigTypeString},
{Key: entity.ConfigKeyTelegramQrImage, Value: entity.ConfigDefaultTelegramQrImage, Type: entity.ConfigTypeString},
{Key: entity.ConfigKeyQrCodeStyle, Value: entity.ConfigDefaultQrCodeStyle, Type: entity.ConfigTypeString},
{Key: entity.ConfigKeyWebsiteURL, Value: entity.ConfigDefaultWebsiteURL, Type: entity.ConfigTypeString},
}
createStart := utils.GetCurrentTime()
@@ -189,6 +190,7 @@ func (r *SystemConfigRepositoryImpl) GetOrCreateDefault() ([]entity.SystemConfig
entity.ConfigKeyEnableFloatButtons: {Key: entity.ConfigKeyEnableFloatButtons, Value: entity.ConfigDefaultEnableFloatButtons, Type: entity.ConfigTypeBool},
entity.ConfigKeyWechatSearchImage: {Key: entity.ConfigKeyWechatSearchImage, Value: entity.ConfigDefaultWechatSearchImage, Type: entity.ConfigTypeString},
entity.ConfigKeyTelegramQrImage: {Key: entity.ConfigKeyTelegramQrImage, Value: entity.ConfigDefaultTelegramQrImage, Type: entity.ConfigTypeString},
entity.ConfigKeyWebsiteURL: {Key: entity.ConfigKeyWebsiteURL, Value: entity.ConfigDefaultWebsiteURL, Type: entity.ConfigTypeString},
}
// 检查现有配置中是否有缺失的配置项

View File

@@ -431,7 +431,7 @@ const getCategoryName = (categoryId: number) => {
// 获取平台名称
const getPlatformName = (platformId: number) => {
console.log('platformId', platformId, platformsData.value)
// console.log('platformId', platformId, platformsData.value)
const platform = (platformsData.value as any)?.find((plat: any) => plat.id === platformId)
return platform?.remark || platform?.name || '未知平台'
}
@@ -449,22 +449,22 @@ const fetchData = async () => {
// 添加分类筛选
if (selectedCategory.value) {
params.category_id = selectedCategory.value
console.log('添加分类筛选:', selectedCategory.value)
// console.log('添加分类筛选:', selectedCategory.value)
}
// 添加平台筛选
if (selectedPlatform.value) {
params.pan_id = selectedPlatform.value
console.log('添加平台筛选:', selectedPlatform.value)
// console.log('添加平台筛选:', selectedPlatform.value)
}
console.log('请求参数:', params)
console.log('pageSize:', pageSize.value)
console.log('selectedCategory:', selectedCategory.value)
console.log('selectedPlatform:', selectedPlatform.value)
// console.log('请求参数:', params)
// console.log('pageSize:', pageSize.value)
// console.log('selectedCategory:', selectedCategory.value)
// console.log('selectedPlatform:', selectedPlatform.value)
const response = await resourceApi.getResources(params) as any
console.log('API响应:', response)
console.log('返回的资源数量:', response?.data?.length || 0)
// console.log('API响应:', response)
// console.log('返回的资源数量:', response?.data?.length || 0)
if (response && response.data) {
// 处理嵌套的data结构{data: {data: [...], total: ...}}

View File

@@ -285,7 +285,7 @@
<!-- Sitemap配置 -->
<div class="mb-6 p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex items-center justify-between mb-4">
<div>
<h4 class="font-medium text-gray-900 dark:text-white mb-2">自动生成功能</h4>
<p class="text-sm text-gray-600 dark:text-gray-400">
@@ -302,6 +302,29 @@
<template #unchecked>已关闭</template>
</n-switch>
</div>
<!-- 站点URL显示 -->
<div class="border-t border-gray-200 dark:border-gray-600 pt-4">
<div class="flex items-center justify-between">
<div>
<h5 class="font-medium text-gray-900 dark:text-white mb-1">当前站点URL</h5>
<p class="text-sm text-gray-600 dark:text-gray-400">
Sitemap生成时将使用此URL作为基础域名
</p>
</div>
<div class="text-right">
<div v-if="systemConfig?.site_url" class="text-sm font-medium text-blue-600 dark:text-blue-400 mb-2">
{{ systemConfig.site_url }}
</div>
<div v-else class="text-sm text-yellow-600 dark:text-yellow-400 mb-2">
未配置
</div>
<NuxtLink to="/admin/site-config" class="text-xs text-blue-500 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 underline">
前往配置
</NuxtLink>
</div>
</div>
</div>
</div>
<!-- Sitemap统计 -->
@@ -678,12 +701,20 @@ const generateSitemap = async () => {
generateStatus.value = '正在启动生成任务...'
try {
// 使用已经加载的系统配置
const siteUrl = systemConfig.value?.site_url || ''
if (!siteUrl) {
message.warning('请先在站点配置中设置站点URL然后再生成Sitemap')
isGenerating.value = false
return
}
const sitemapApi = useSitemapApi()
const response = await sitemapApi.generateSitemap()
const response = await sitemapApi.generateSitemap({ site_url: siteUrl })
if (response) {
generateStatus.value = response.message || '生成任务已启动'
message.success('Sitemap生成任务已启动')
message.success(`Sitemap生成任务已启动使用站点URL: ${siteUrl}`)
// 更新统计信息
sitemapStats.value.total_resources = response.total_resources || 0
sitemapStats.value.total_pages = response.total_pages || 0
@@ -715,8 +746,24 @@ const viewSitemap = () => {
window.open('/sitemap.xml', '_blank')
}
// 获取系统配置
const systemConfig = ref<any>(null)
// 加载系统配置
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)
}
}
// 初始化
onMounted(async () => {
await loadSystemConfig()
loadLinkList()
await loadSitemapConfig()
await refreshSitemapStatus()

View File

@@ -34,6 +34,18 @@
require-mark-placement="right-hanging"
>
<div class="space-y-6">
<!-- 站点URL -->
<div class="space-y-2">
<div class="flex items-center space-x-2">
<label class="text-base font-semibold text-gray-800 dark:text-gray-200">站点URL</label>
<span class="text-xs text-gray-500 dark:text-gray-400">网站的基础URL用于生成sitemap等请包含协议名https://example.com</span>
</div>
<n-input
v-model:value="configForm.site_url"
placeholder="请输入站点URLhttps://example.com"
/>
</div>
<!-- 网站标题 -->
<div class="space-y-2">
<div class="flex items-center space-x-2">
@@ -298,6 +310,7 @@ interface Announcement {
// 配置表单数据类型
interface SiteConfigForm {
site_url: string
site_title: string
site_description: string
keywords: string
@@ -362,6 +375,7 @@ const {
},
// 字段映射:前端字段名 -> 后端字段名
fieldMapping: {
site_url: 'site_url',
site_title: 'site_title',
site_description: 'site_description',
keywords: 'keywords',
@@ -383,6 +397,7 @@ const {
// 配置表单数据
const configForm = ref<SiteConfigForm>({
site_url: '',
site_title: '',
site_description: '',
keywords: '',
@@ -424,6 +439,7 @@ const fetchConfig = async () => {
if (response) {
const configData = {
site_url: response.site_url || '',
site_title: response.site_title || '',
site_description: response.site_description || '',
keywords: response.keywords || '',

View File

@@ -608,24 +608,39 @@ watch(systemConfigError, (error) => {
// 从 SSR 数据中获取值
const safeResources = computed(() => {
const data = resourcesData.value as any
// console.log('原始API数据结构:', JSON.stringify(data, null, 2))
let resources: any[] = []
// 处理嵌套的data结构{data: {data: [...], total: ...}}
if (data?.data?.data && Array.isArray(data.data.data)) {
const resources = data.data.data
resources = data.data.data
console.log('第一层嵌套资源:', resources)
return resources
}
// 处理直接的data结构{data: [...], total: ...}
if (data?.data && Array.isArray(data.data)) {
const resources = data.data
else if (data?.data && Array.isArray(data.data)) {
resources = data.data
// console.log('第二层嵌套资源:', resources)
return resources
}
// 处理直接的数组结构
if (Array.isArray(data)) {
else if (Array.isArray(data)) {
resources = data
// console.log('直接数组结构:', data)
return data
}
// 根据 key 字段去重
if (resources.length > 0) {
const keyMap = new Map()
const deduplicatedResources: any[] = []
for (const resource of resources) {
const key = resource.key
if (!keyMap.has(key)) {
keyMap.set(key, true)
deduplicatedResources.push(resource)
}
}
// console.log(`去重前: ${resources.length} 个资源, 去重后: ${deduplicatedResources.length} 个资源`)
return deduplicatedResources
}
// console.log('未匹配到任何数据结构')

View File

@@ -203,9 +203,6 @@
? (detectionMethods[resource.id] === 'unsupported' ? '不支持检测' : (detectionResults[resource.id] ? '有效' : '无效'))
: '不支持检测' }}
</span>
<span v-if="resource.save_url" class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-700 dark:bg-blue-500/20 dark:text-blue-100">
已转存
</span>
</div>
</div>
</div>