feat(settings): 添加文档链接配置功能
- 后台系统设置新增文档链接(doc_url)配置项 - 首页顶部导航栏显示文档链接图标(条件渲染) - Footer区域添加文档链接和GitHub链接 - 支持中英文国际化
This commit is contained in:
@@ -60,6 +60,7 @@ type UpdateSettingsRequest struct {
|
|||||||
SiteSubtitle string `json:"site_subtitle"`
|
SiteSubtitle string `json:"site_subtitle"`
|
||||||
ApiBaseUrl string `json:"api_base_url"`
|
ApiBaseUrl string `json:"api_base_url"`
|
||||||
ContactInfo string `json:"contact_info"`
|
ContactInfo string `json:"contact_info"`
|
||||||
|
DocUrl string `json:"doc_url"`
|
||||||
|
|
||||||
// 默认配置
|
// 默认配置
|
||||||
DefaultConcurrency int `json:"default_concurrency"`
|
DefaultConcurrency int `json:"default_concurrency"`
|
||||||
@@ -104,6 +105,7 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
|
|||||||
SiteSubtitle: req.SiteSubtitle,
|
SiteSubtitle: req.SiteSubtitle,
|
||||||
ApiBaseUrl: req.ApiBaseUrl,
|
ApiBaseUrl: req.ApiBaseUrl,
|
||||||
ContactInfo: req.ContactInfo,
|
ContactInfo: req.ContactInfo,
|
||||||
|
DocUrl: req.DocUrl,
|
||||||
DefaultConcurrency: req.DefaultConcurrency,
|
DefaultConcurrency: req.DefaultConcurrency,
|
||||||
DefaultBalance: req.DefaultBalance,
|
DefaultBalance: req.DefaultBalance,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ const (
|
|||||||
SettingKeySiteSubtitle = "site_subtitle" // 网站副标题
|
SettingKeySiteSubtitle = "site_subtitle" // 网站副标题
|
||||||
SettingKeyApiBaseUrl = "api_base_url" // API端点地址(用于客户端配置和导入)
|
SettingKeyApiBaseUrl = "api_base_url" // API端点地址(用于客户端配置和导入)
|
||||||
SettingKeyContactInfo = "contact_info" // 客服联系方式
|
SettingKeyContactInfo = "contact_info" // 客服联系方式
|
||||||
|
SettingKeyDocUrl = "doc_url" // 文档链接
|
||||||
|
|
||||||
// 默认配置
|
// 默认配置
|
||||||
SettingKeyDefaultConcurrency = "default_concurrency" // 新用户默认并发量
|
SettingKeyDefaultConcurrency = "default_concurrency" // 新用户默认并发量
|
||||||
@@ -80,6 +81,7 @@ type SystemSettings struct {
|
|||||||
SiteSubtitle string `json:"site_subtitle"`
|
SiteSubtitle string `json:"site_subtitle"`
|
||||||
ApiBaseUrl string `json:"api_base_url"`
|
ApiBaseUrl string `json:"api_base_url"`
|
||||||
ContactInfo string `json:"contact_info"`
|
ContactInfo string `json:"contact_info"`
|
||||||
|
DocUrl string `json:"doc_url"`
|
||||||
|
|
||||||
// 默认配置
|
// 默认配置
|
||||||
DefaultConcurrency int `json:"default_concurrency"`
|
DefaultConcurrency int `json:"default_concurrency"`
|
||||||
@@ -97,5 +99,6 @@ type PublicSettings struct {
|
|||||||
SiteSubtitle string `json:"site_subtitle"`
|
SiteSubtitle string `json:"site_subtitle"`
|
||||||
ApiBaseUrl string `json:"api_base_url"`
|
ApiBaseUrl string `json:"api_base_url"`
|
||||||
ContactInfo string `json:"contact_info"`
|
ContactInfo string `json:"contact_info"`
|
||||||
|
DocUrl string `json:"doc_url"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*model.PublicSe
|
|||||||
model.SettingKeySiteSubtitle,
|
model.SettingKeySiteSubtitle,
|
||||||
model.SettingKeyApiBaseUrl,
|
model.SettingKeyApiBaseUrl,
|
||||||
model.SettingKeyContactInfo,
|
model.SettingKeyContactInfo,
|
||||||
|
model.SettingKeyDocUrl,
|
||||||
}
|
}
|
||||||
|
|
||||||
settings, err := s.settingRepo.GetMultiple(ctx, keys)
|
settings, err := s.settingRepo.GetMultiple(ctx, keys)
|
||||||
@@ -71,6 +72,7 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*model.PublicSe
|
|||||||
SiteSubtitle: s.getStringOrDefault(settings, model.SettingKeySiteSubtitle, "Subscription to API Conversion Platform"),
|
SiteSubtitle: s.getStringOrDefault(settings, model.SettingKeySiteSubtitle, "Subscription to API Conversion Platform"),
|
||||||
ApiBaseUrl: settings[model.SettingKeyApiBaseUrl],
|
ApiBaseUrl: settings[model.SettingKeyApiBaseUrl],
|
||||||
ContactInfo: settings[model.SettingKeyContactInfo],
|
ContactInfo: settings[model.SettingKeyContactInfo],
|
||||||
|
DocUrl: settings[model.SettingKeyDocUrl],
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +108,7 @@ func (s *SettingService) UpdateSettings(ctx context.Context, settings *model.Sys
|
|||||||
updates[model.SettingKeySiteSubtitle] = settings.SiteSubtitle
|
updates[model.SettingKeySiteSubtitle] = settings.SiteSubtitle
|
||||||
updates[model.SettingKeyApiBaseUrl] = settings.ApiBaseUrl
|
updates[model.SettingKeyApiBaseUrl] = settings.ApiBaseUrl
|
||||||
updates[model.SettingKeyContactInfo] = settings.ContactInfo
|
updates[model.SettingKeyContactInfo] = settings.ContactInfo
|
||||||
|
updates[model.SettingKeyDocUrl] = settings.DocUrl
|
||||||
|
|
||||||
// 默认配置
|
// 默认配置
|
||||||
updates[model.SettingKeyDefaultConcurrency] = strconv.Itoa(settings.DefaultConcurrency)
|
updates[model.SettingKeyDefaultConcurrency] = strconv.Itoa(settings.DefaultConcurrency)
|
||||||
@@ -210,6 +213,7 @@ func (s *SettingService) parseSettings(settings map[string]string) *model.System
|
|||||||
SiteSubtitle: s.getStringOrDefault(settings, model.SettingKeySiteSubtitle, "Subscription to API Conversion Platform"),
|
SiteSubtitle: s.getStringOrDefault(settings, model.SettingKeySiteSubtitle, "Subscription to API Conversion Platform"),
|
||||||
ApiBaseUrl: settings[model.SettingKeyApiBaseUrl],
|
ApiBaseUrl: settings[model.SettingKeyApiBaseUrl],
|
||||||
ContactInfo: settings[model.SettingKeyContactInfo],
|
ContactInfo: settings[model.SettingKeyContactInfo],
|
||||||
|
DocUrl: settings[model.SettingKeyDocUrl],
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析整数类型
|
// 解析整数类型
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export interface SystemSettings {
|
|||||||
site_subtitle: string;
|
site_subtitle: string;
|
||||||
api_base_url: string;
|
api_base_url: string;
|
||||||
contact_info: string;
|
contact_info: string;
|
||||||
|
doc_url: string;
|
||||||
// SMTP settings
|
// SMTP settings
|
||||||
smtp_host: string;
|
smtp_host: string;
|
||||||
smtp_port: number;
|
smtp_port: number;
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ export default {
|
|||||||
// Home Page
|
// Home Page
|
||||||
home: {
|
home: {
|
||||||
viewOnGithub: 'View on GitHub',
|
viewOnGithub: 'View on GitHub',
|
||||||
|
viewDocs: 'View Documentation',
|
||||||
|
docs: 'Docs',
|
||||||
switchToLight: 'Switch to Light Mode',
|
switchToLight: 'Switch to Light Mode',
|
||||||
switchToDark: 'Switch to Dark Mode',
|
switchToDark: 'Switch to Dark Mode',
|
||||||
dashboard: 'Dashboard',
|
dashboard: 'Dashboard',
|
||||||
@@ -1077,6 +1079,8 @@ export default {
|
|||||||
contactInfo: 'Contact Info',
|
contactInfo: 'Contact Info',
|
||||||
contactInfoPlaceholder: 'e.g., QQ: 123456789',
|
contactInfoPlaceholder: 'e.g., QQ: 123456789',
|
||||||
contactInfoHint: 'Customer support contact info, displayed on redeem page, profile, etc.',
|
contactInfoHint: 'Customer support contact info, displayed on redeem page, profile, etc.',
|
||||||
|
docUrl: 'Documentation URL',
|
||||||
|
docUrlHint: 'Link to your documentation site. Leave empty to hide the documentation link.',
|
||||||
siteLogo: 'Site Logo',
|
siteLogo: 'Site Logo',
|
||||||
uploadImage: 'Upload Image',
|
uploadImage: 'Upload Image',
|
||||||
remove: 'Remove',
|
remove: 'Remove',
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ export default {
|
|||||||
// Home Page
|
// Home Page
|
||||||
home: {
|
home: {
|
||||||
viewOnGithub: '在 GitHub 上查看',
|
viewOnGithub: '在 GitHub 上查看',
|
||||||
|
viewDocs: '查看文档',
|
||||||
|
docs: '文档',
|
||||||
switchToLight: '切换到浅色模式',
|
switchToLight: '切换到浅色模式',
|
||||||
switchToDark: '切换到深色模式',
|
switchToDark: '切换到深色模式',
|
||||||
dashboard: '控制台',
|
dashboard: '控制台',
|
||||||
@@ -1301,6 +1303,8 @@ export default {
|
|||||||
contactInfo: '客服联系方式',
|
contactInfo: '客服联系方式',
|
||||||
contactInfoPlaceholder: '例如:QQ: 123456789',
|
contactInfoPlaceholder: '例如:QQ: 123456789',
|
||||||
contactInfoHint: '填写客服联系方式,将展示在兑换页面、个人资料等位置',
|
contactInfoHint: '填写客服联系方式,将展示在兑换页面、个人资料等位置',
|
||||||
|
docUrl: '文档链接',
|
||||||
|
docUrlHint: '文档网站的链接。留空则隐藏文档链接。',
|
||||||
siteLogo: '站点Logo',
|
siteLogo: '站点Logo',
|
||||||
uploadImage: '上传图片',
|
uploadImage: '上传图片',
|
||||||
remove: '移除',
|
remove: '移除',
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export const useAppStore = defineStore('app', () => {
|
|||||||
const siteVersion = ref<string>('');
|
const siteVersion = ref<string>('');
|
||||||
const contactInfo = ref<string>('');
|
const contactInfo = ref<string>('');
|
||||||
const apiBaseUrl = ref<string>('');
|
const apiBaseUrl = ref<string>('');
|
||||||
|
const docUrl = ref<string>('');
|
||||||
|
|
||||||
// Version cache state
|
// Version cache state
|
||||||
const versionLoaded = ref<boolean>(false);
|
const versionLoaded = ref<boolean>(false);
|
||||||
@@ -297,6 +298,7 @@ export const useAppStore = defineStore('app', () => {
|
|||||||
site_subtitle: '',
|
site_subtitle: '',
|
||||||
api_base_url: apiBaseUrl.value,
|
api_base_url: apiBaseUrl.value,
|
||||||
contact_info: contactInfo.value,
|
contact_info: contactInfo.value,
|
||||||
|
doc_url: docUrl.value,
|
||||||
version: siteVersion.value,
|
version: siteVersion.value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -314,6 +316,7 @@ export const useAppStore = defineStore('app', () => {
|
|||||||
siteVersion.value = data.version || '';
|
siteVersion.value = data.version || '';
|
||||||
contactInfo.value = data.contact_info || '';
|
contactInfo.value = data.contact_info || '';
|
||||||
apiBaseUrl.value = data.api_base_url || '';
|
apiBaseUrl.value = data.api_base_url || '';
|
||||||
|
docUrl.value = data.doc_url || '';
|
||||||
publicSettingsLoaded.value = true;
|
publicSettingsLoaded.value = true;
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -347,6 +350,7 @@ export const useAppStore = defineStore('app', () => {
|
|||||||
siteVersion,
|
siteVersion,
|
||||||
contactInfo,
|
contactInfo,
|
||||||
apiBaseUrl,
|
apiBaseUrl,
|
||||||
|
docUrl,
|
||||||
|
|
||||||
// Version state
|
// Version state
|
||||||
versionLoaded,
|
versionLoaded,
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export interface PublicSettings {
|
|||||||
site_subtitle: string;
|
site_subtitle: string;
|
||||||
api_base_url: string;
|
api_base_url: string;
|
||||||
contact_info: string;
|
contact_info: string;
|
||||||
|
doc_url: string;
|
||||||
version: string;
|
version: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,16 +24,17 @@
|
|||||||
<!-- Language Switcher -->
|
<!-- Language Switcher -->
|
||||||
<LocaleSwitcher />
|
<LocaleSwitcher />
|
||||||
|
|
||||||
<!-- GitHub Link -->
|
<!-- Doc Link -->
|
||||||
<a
|
<a
|
||||||
:href="githubUrl"
|
v-if="docUrl"
|
||||||
|
:href="docUrl"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="p-2 rounded-lg text-gray-500 hover:text-gray-700 dark:text-dark-400 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-dark-800 transition-colors"
|
class="p-2 rounded-lg text-gray-500 hover:text-gray-700 dark:text-dark-400 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-dark-800 transition-colors"
|
||||||
:title="t('home.viewOnGithub')"
|
:title="t('home.viewDocs')"
|
||||||
>
|
>
|
||||||
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
|
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.17 6.839 9.49.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.464-1.11-1.464-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.831.092-.646.35-1.086.636-1.336-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0112 6.836c.85.004 1.705.114 2.504.336 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.578.688.48C19.138 20.167 22 16.418 22 12c0-5.523-4.477-10-10-10z" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25" />
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@@ -253,10 +254,29 @@
|
|||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<footer class="relative z-10 px-6 py-8 border-t border-gray-200/50 dark:border-dark-800/50">
|
<footer class="relative z-10 px-6 py-8 border-t border-gray-200/50 dark:border-dark-800/50">
|
||||||
<div class="max-w-6xl mx-auto text-center">
|
<div class="max-w-6xl mx-auto flex flex-col sm:flex-row items-center justify-center gap-4 text-center sm:text-left">
|
||||||
<p class="text-sm text-gray-500 dark:text-dark-400">
|
<p class="text-sm text-gray-500 dark:text-dark-400">
|
||||||
© {{ currentYear }} {{ siteName }}. {{ t('home.footer.allRightsReserved') }}
|
© {{ currentYear }} {{ siteName }}. {{ t('home.footer.allRightsReserved') }}
|
||||||
</p>
|
</p>
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<a
|
||||||
|
v-if="docUrl"
|
||||||
|
:href="docUrl"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="text-sm text-gray-500 hover:text-gray-700 dark:text-dark-400 dark:hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
{{ t('home.docs') }}
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
:href="githubUrl"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="text-sm text-gray-500 hover:text-gray-700 dark:text-dark-400 dark:hover:text-white transition-colors"
|
||||||
|
>
|
||||||
|
GitHub
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
@@ -277,6 +297,7 @@ const authStore = useAuthStore();
|
|||||||
const siteName = ref('Sub2API');
|
const siteName = ref('Sub2API');
|
||||||
const siteLogo = ref('');
|
const siteLogo = ref('');
|
||||||
const siteSubtitle = ref('AI API Gateway Platform');
|
const siteSubtitle = ref('AI API Gateway Platform');
|
||||||
|
const docUrl = ref('');
|
||||||
|
|
||||||
// Theme
|
// Theme
|
||||||
const isDark = ref(document.documentElement.classList.contains('dark'));
|
const isDark = ref(document.documentElement.classList.contains('dark'));
|
||||||
@@ -322,6 +343,7 @@ onMounted(async () => {
|
|||||||
siteName.value = settings.site_name || 'Sub2API';
|
siteName.value = settings.site_name || 'Sub2API';
|
||||||
siteLogo.value = settings.site_logo || '';
|
siteLogo.value = settings.site_logo || '';
|
||||||
siteSubtitle.value = settings.site_subtitle || 'AI API Gateway Platform';
|
siteSubtitle.value = settings.site_subtitle || 'AI API Gateway Platform';
|
||||||
|
docUrl.value = settings.doc_url || '';
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load public settings:', error);
|
console.error('Failed to load public settings:', error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -287,6 +287,20 @@
|
|||||||
<p class="mt-1.5 text-xs text-gray-500 dark:text-gray-400">{{ t('admin.settings.site.contactInfoHint') }}</p>
|
<p class="mt-1.5 text-xs text-gray-500 dark:text-gray-400">{{ t('admin.settings.site.contactInfoHint') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Doc URL -->
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||||
|
{{ t('admin.settings.site.docUrl') }}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
v-model="form.doc_url"
|
||||||
|
type="url"
|
||||||
|
class="input font-mono text-sm"
|
||||||
|
placeholder="https://docs.example.com"
|
||||||
|
/>
|
||||||
|
<p class="mt-1.5 text-xs text-gray-500 dark:text-gray-400">{{ t('admin.settings.site.docUrlHint') }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Site Logo Upload -->
|
<!-- Site Logo Upload -->
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||||
@@ -541,6 +555,7 @@ const form = reactive<SystemSettings>({
|
|||||||
site_subtitle: 'Subscription to API Conversion Platform',
|
site_subtitle: 'Subscription to API Conversion Platform',
|
||||||
api_base_url: '',
|
api_base_url: '',
|
||||||
contact_info: '',
|
contact_info: '',
|
||||||
|
doc_url: '',
|
||||||
smtp_host: '',
|
smtp_host: '',
|
||||||
smtp_port: 587,
|
smtp_port: 587,
|
||||||
smtp_username: '',
|
smtp_username: '',
|
||||||
|
|||||||
Reference in New Issue
Block a user