feat: 支持自定义端点配置与展示
This commit is contained in:
@@ -1248,6 +1248,81 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Custom Endpoints -->
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{{ t('admin.settings.site.customEndpoints.title') }}
|
||||
</label>
|
||||
<p class="mb-3 text-xs text-gray-500 dark:text-gray-400">
|
||||
{{ t('admin.settings.site.customEndpoints.description') }}
|
||||
</p>
|
||||
|
||||
<div class="space-y-3">
|
||||
<div
|
||||
v-for="(ep, index) in form.custom_endpoints"
|
||||
:key="index"
|
||||
class="rounded-lg border border-gray-200 p-4 dark:border-dark-600"
|
||||
>
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{{ t('admin.settings.site.customEndpoints.itemLabel', { n: index + 1 }) }}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
class="rounded p-1 text-red-400 hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-900/20"
|
||||
@click="removeEndpoint(index)"
|
||||
>
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">
|
||||
{{ t('admin.settings.site.customEndpoints.name') }}
|
||||
</label>
|
||||
<input
|
||||
v-model="ep.name"
|
||||
type="text"
|
||||
class="input text-sm"
|
||||
:placeholder="t('admin.settings.site.customEndpoints.namePlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">
|
||||
{{ t('admin.settings.site.customEndpoints.endpointUrl') }}
|
||||
</label>
|
||||
<input
|
||||
v-model="ep.endpoint"
|
||||
type="url"
|
||||
class="input font-mono text-sm"
|
||||
:placeholder="t('admin.settings.site.customEndpoints.endpointUrlPlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="mb-1 block text-xs font-medium text-gray-600 dark:text-gray-400">
|
||||
{{ t('admin.settings.site.customEndpoints.descriptionLabel') }}
|
||||
</label>
|
||||
<input
|
||||
v-model="ep.description"
|
||||
type="text"
|
||||
class="input text-sm"
|
||||
:placeholder="t('admin.settings.site.customEndpoints.descriptionPlaceholder')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="mt-3 flex w-full items-center justify-center gap-2 rounded-lg border-2 border-dashed border-gray-300 px-4 py-2.5 text-sm text-gray-500 transition-colors hover:border-primary-400 hover:text-primary-600 dark:border-dark-600 dark:text-gray-400 dark:hover:border-primary-500 dark:hover:text-primary-400"
|
||||
@click="addEndpoint"
|
||||
>
|
||||
<svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4" /></svg>
|
||||
{{ t('admin.settings.site.customEndpoints.add') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Contact Info -->
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
@@ -1945,6 +2020,7 @@ const form = reactive<SettingsForm>({
|
||||
purchase_subscription_url: '',
|
||||
sora_client_enabled: false,
|
||||
custom_menu_items: [] as Array<{id: string; label: string; icon_svg: string; url: string; visibility: 'user' | 'admin'; sort_order: number}>,
|
||||
custom_endpoints: [] as Array<{name: string; endpoint: string; description: string}>,
|
||||
frontend_url: '',
|
||||
smtp_host: '',
|
||||
smtp_port: 587,
|
||||
@@ -2114,6 +2190,15 @@ function moveMenuItem(index: number, direction: -1 | 1) {
|
||||
})
|
||||
}
|
||||
|
||||
// Custom endpoint management
|
||||
function addEndpoint() {
|
||||
form.custom_endpoints.push({ name: '', endpoint: '', description: '' })
|
||||
}
|
||||
|
||||
function removeEndpoint(index: number) {
|
||||
form.custom_endpoints.splice(index, 1)
|
||||
}
|
||||
|
||||
async function loadSettings() {
|
||||
loading.value = true
|
||||
try {
|
||||
@@ -2253,6 +2338,7 @@ async function saveSettings() {
|
||||
purchase_subscription_url: form.purchase_subscription_url,
|
||||
sora_client_enabled: form.sora_client_enabled,
|
||||
custom_menu_items: form.custom_menu_items,
|
||||
custom_endpoints: form.custom_endpoints,
|
||||
frontend_url: form.frontend_url,
|
||||
smtp_host: form.smtp_host,
|
||||
smtp_port: form.smtp_port,
|
||||
|
||||
@@ -2,24 +2,31 @@
|
||||
<AppLayout>
|
||||
<TablePageLayout>
|
||||
<template #filters>
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<SearchInput
|
||||
v-model="filterSearch"
|
||||
:placeholder="t('keys.searchPlaceholder')"
|
||||
class="w-full sm:w-64"
|
||||
@search="onFilterChange"
|
||||
/>
|
||||
<Select
|
||||
:model-value="filterGroupId"
|
||||
class="w-40"
|
||||
:options="groupFilterOptions"
|
||||
@update:model-value="onGroupFilterChange"
|
||||
/>
|
||||
<Select
|
||||
:model-value="filterStatus"
|
||||
class="w-40"
|
||||
:options="statusFilterOptions"
|
||||
@update:model-value="onStatusFilterChange"
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<SearchInput
|
||||
v-model="filterSearch"
|
||||
:placeholder="t('keys.searchPlaceholder')"
|
||||
class="w-full sm:w-64"
|
||||
@search="onFilterChange"
|
||||
/>
|
||||
<Select
|
||||
:model-value="filterGroupId"
|
||||
class="w-40"
|
||||
:options="groupFilterOptions"
|
||||
@update:model-value="onGroupFilterChange"
|
||||
/>
|
||||
<Select
|
||||
:model-value="filterStatus"
|
||||
class="w-40"
|
||||
:options="statusFilterOptions"
|
||||
@update:model-value="onStatusFilterChange"
|
||||
/>
|
||||
</div>
|
||||
<EndpointPopover
|
||||
v-if="publicSettings?.api_base_url || (publicSettings?.custom_endpoints?.length ?? 0) > 0"
|
||||
:api-base-url="publicSettings?.api_base_url || ''"
|
||||
:custom-endpoints="publicSettings?.custom_endpoints || []"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1050,6 +1057,7 @@ import TablePageLayout from '@/components/layout/TablePageLayout.vue'
|
||||
import SearchInput from '@/components/common/SearchInput.vue'
|
||||
import Icon from '@/components/icons/Icon.vue'
|
||||
import UseKeyModal from '@/components/keys/UseKeyModal.vue'
|
||||
import EndpointPopover from '@/components/keys/EndpointPopover.vue'
|
||||
import GroupBadge from '@/components/common/GroupBadge.vue'
|
||||
import GroupOptionItem from '@/components/common/GroupOptionItem.vue'
|
||||
import type { ApiKey, Group, PublicSettings, SubscriptionType, GroupPlatform } from '@/types'
|
||||
|
||||
Reference in New Issue
Block a user