feat: 新增统一模型网关与模型管理工作区

- 新增 OpenAI 兼容统一模型调用链路、模型发布配置与批量发布能力

- 重构模型管理页面入口与统一网关工作区,更新服务商 logo 资源与模型 ID 文案

- 收口全新库初始化脚本,仅保留服务商种子并整理统一网关 migration
This commit is contained in:
2026-03-26 20:48:18 +08:00
parent b777cb3641
commit aaf4c61ff8
80 changed files with 4786 additions and 362 deletions

View File

@@ -1,9 +1,9 @@
<script setup lang="ts">
import {computed, onMounted, ref} from 'vue';
import { computed, onMounted, ref } from 'vue';
import {$t} from '@easyflow/locales';
import { $t } from '@easyflow/locales';
import {Delete, Edit, Plus, Select, Setting} from '@element-plus/icons-vue';
import { Delete, Edit, Plus, Select, Setting } from '@element-plus/icons-vue';
import {
ElButton,
ElEmpty,
@@ -15,15 +15,15 @@ import {
ElTag,
} from 'element-plus';
import {getLlmProviderList} from '#/api/ai/llm.js';
import {api} from '#/api/request.js';
import { getLlmProviderList } from '#/api/ai/llm.js';
import { api } from '#/api/request.js';
import ListPageShell from '#/components/page/ListPageShell.vue';
import ActiveModelWorkspace from '#/views/ai/model/ActiveModelWorkspace.vue';
import AddModelModal from '#/views/ai/model/AddModelModal.vue';
import AddModelProviderModal from '#/views/ai/model/AddModelProviderModal.vue';
import ModelProviderBadge from '#/views/ai/model/ModelProviderBadge.vue';
import {getProviderPresetByValue} from '#/views/ai/model/modelUtils/defaultIcon';
import {modelTypes} from '#/views/ai/model/modelUtils/modelTypes';
import { getProviderPresetByValue } from '#/views/ai/model/modelUtils/defaultIcon';
import { modelTypes } from '#/views/ai/model/modelUtils/modelTypes';
import {
createProviderDraft,
getProviderConfigMetrics,
@@ -31,8 +31,9 @@ import {
} from '#/views/ai/model/modelUtils/providerDraft';
import ModelVerifyConfig from '#/views/ai/model/ModelVerifyConfig.vue';
import ModelViewItemOperation from '#/views/ai/model/ModelViewItemOperation.vue';
import UnifiedGatewayWorkspace from '#/views/ai/model/UnifiedGatewayWorkspace.vue';
type ModelWorkspaceView = 'active' | 'provider';
type ModelWorkspaceView = 'active' | 'gateway' | 'provider';
interface ModelGroup {
groupName: string;
@@ -59,6 +60,7 @@ const addLlmProviderRef = ref();
const llmVerifyConfigRef = ref();
const addLlmRef = ref();
const activeWorkspaceRef = ref();
const unifiedGatewayWorkspaceRef = ref();
const selectedProvider = computed(() =>
providers.value.find((item) => item.id === selectedProviderId.value),
@@ -194,7 +196,10 @@ const loadProviderDetail = async (
isDetailLoading.value = true;
try {
const res = await api.get(`/api/v1/model/getList?providerId=${providerId}`, {});
const res = await api.get(
`/api/v1/model/getList?providerId=${providerId}`,
{},
);
if (res.errorCode === 0) {
syncGroupedModels(res.data);
@@ -282,15 +287,30 @@ const switchWorkspaceView = async (target: ModelWorkspaceView) => {
return;
}
if (!(await confirmDiscardProviderDraft())) {
if (
workspaceView.value === 'provider' &&
!(await confirmDiscardProviderDraft())
) {
return;
}
if (workspaceView.value === 'gateway') {
const canLeaveGateway =
(await unifiedGatewayWorkspaceRef.value?.confirmBeforeLeave?.()) ?? true;
if (!canLeaveGateway) {
return;
}
}
workspaceView.value = target;
if (target === 'active') {
await activeWorkspaceRef.value?.reloadData?.();
}
if (target === 'gateway') {
await unifiedGatewayWorkspaceRef.value?.reloadData?.();
}
};
const selectProvider = async (provider: any) => {
@@ -357,6 +377,7 @@ const handleDeleteProvider = async (provider: any) => {
const remaining = providers.value.find((item) => item.id !== provider.id);
await loadProviders(remaining?.id, true);
await activeWorkspaceRef.value?.reloadData?.();
await unifiedGatewayWorkspaceRef.value?.reloadData?.();
}
};
@@ -375,6 +396,7 @@ const openEditProviderDialog = () => {
const handleProviderModalReload = async () => {
await loadProviders(selectedProviderId.value, true);
await activeWorkspaceRef.value?.reloadData?.();
await unifiedGatewayWorkspaceRef.value?.reloadData?.();
};
const handleAddLlm = (modelType = activeModelType.value) => {
@@ -444,6 +466,7 @@ const handleModelDataReload = async () => {
keepDraft: isProviderDirty.value,
});
await activeWorkspaceRef.value?.reloadData?.();
await unifiedGatewayWorkspaceRef.value?.reloadData?.();
await refreshProviderCounts(providers.value);
};
@@ -481,6 +504,14 @@ onMounted(() => {
>
已配置模型
</button>
<button
type="button"
class="workspace-switch__item"
:class="{ 'is-active': workspaceView === 'gateway' }"
@click="switchWorkspaceView('gateway')"
>
统一网关
</button>
</div>
</template>
@@ -770,7 +801,7 @@ onMounted(() => {
</section>
<ActiveModelWorkspace
v-else
v-else-if="workspaceView === 'active'"
ref="activeWorkspaceRef"
:providers="providers"
@create-model="handleAddLlm"
@@ -778,6 +809,12 @@ onMounted(() => {
@refresh-provider-stats="handleActiveProviderStatsRefresh"
/>
<UnifiedGatewayWorkspace
v-else
ref="unifiedGatewayWorkspaceRef"
@updated="handleModelDataReload"
/>
<AddModelProviderModal
ref="addLlmProviderRef"
@reload="handleProviderModalReload"