feat: 支持尊享包渠道
This commit is contained in:
100
Yi.Ai.Vue3/src/api/channel/index.ts
Normal file
100
Yi.Ai.Vue3/src/api/channel/index.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { del, get, post, put } from '@/utils/request';
|
||||
import type {
|
||||
AiAppDto,
|
||||
AiAppCreateInput,
|
||||
AiAppUpdateInput,
|
||||
AiAppGetListInput,
|
||||
AiModelDto,
|
||||
AiModelCreateInput,
|
||||
AiModelUpdateInput,
|
||||
AiModelGetListInput,
|
||||
PagedResultDto,
|
||||
} from './types';
|
||||
|
||||
// ==================== AI应用管理 ====================
|
||||
|
||||
// 获取AI应用列表
|
||||
export function getAppList(params?: AiAppGetListInput) {
|
||||
const queryParams = new URLSearchParams();
|
||||
if (params?.searchKey) {
|
||||
queryParams.append('SearchKey', params.searchKey);
|
||||
}
|
||||
if (params?.skipCount !== undefined) {
|
||||
queryParams.append('SkipCount', params.skipCount.toString());
|
||||
}
|
||||
if (params?.maxResultCount !== undefined) {
|
||||
queryParams.append('MaxResultCount', params.maxResultCount.toString());
|
||||
}
|
||||
|
||||
const queryString = queryParams.toString();
|
||||
const url = queryString ? `/channel/app?${queryString}` : '/channel/app';
|
||||
|
||||
return get<PagedResultDto<AiAppDto>>(url).json();
|
||||
}
|
||||
|
||||
// 根据ID获取AI应用
|
||||
export function getAppById(id: string) {
|
||||
return get<AiAppDto>(`/channel/app/${id}`).json();
|
||||
}
|
||||
|
||||
// 创建AI应用
|
||||
export function createApp(data: AiAppCreateInput) {
|
||||
return post<AiAppDto>('/channel/app', data).json();
|
||||
}
|
||||
|
||||
// 更新AI应用
|
||||
export function updateApp(data: AiAppUpdateInput) {
|
||||
return put<AiAppDto>('/channel/app', data).json();
|
||||
}
|
||||
|
||||
// 删除AI应用
|
||||
export function deleteApp(id: string) {
|
||||
return del(`/channel/app/${id}`).json();
|
||||
}
|
||||
|
||||
// ==================== AI模型管理 ====================
|
||||
|
||||
// 获取AI模型列表
|
||||
export function getModelList(params?: AiModelGetListInput) {
|
||||
const queryParams = new URLSearchParams();
|
||||
if (params?.searchKey) {
|
||||
queryParams.append('SearchKey', params.searchKey);
|
||||
}
|
||||
if (params?.aiAppId) {
|
||||
queryParams.append('AiAppId', params.aiAppId);
|
||||
}
|
||||
if (params?.isPremiumOnly !== undefined) {
|
||||
queryParams.append('IsPremiumOnly', params.isPremiumOnly.toString());
|
||||
}
|
||||
if (params?.skipCount !== undefined) {
|
||||
queryParams.append('SkipCount', params.skipCount.toString());
|
||||
}
|
||||
if (params?.maxResultCount !== undefined) {
|
||||
queryParams.append('MaxResultCount', params.maxResultCount.toString());
|
||||
}
|
||||
|
||||
const queryString = queryParams.toString();
|
||||
const url = queryString ? `/channel/model?${queryString}` : '/channel/model';
|
||||
|
||||
return get<PagedResultDto<AiModelDto>>(url).json();
|
||||
}
|
||||
|
||||
// 根据ID获取AI模型
|
||||
export function getModelById(id: string) {
|
||||
return get<AiModelDto>(`/channel/model/${id}`).json();
|
||||
}
|
||||
|
||||
// 创建AI模型
|
||||
export function createModel(data: AiModelCreateInput) {
|
||||
return post<AiModelDto>('/channel/model', data).json();
|
||||
}
|
||||
|
||||
// 更新AI模型
|
||||
export function updateModel(data: AiModelUpdateInput) {
|
||||
return put<AiModelDto>('/channel/model', data).json();
|
||||
}
|
||||
|
||||
// 删除AI模型
|
||||
export function deleteModel(id: string) {
|
||||
return del(`/channel/model/${id}`).json();
|
||||
}
|
||||
121
Yi.Ai.Vue3/src/api/channel/types.ts
Normal file
121
Yi.Ai.Vue3/src/api/channel/types.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
// 模型类型枚举
|
||||
export enum ModelTypeEnum {
|
||||
Chat = 0,
|
||||
Image = 1,
|
||||
Embedding = 2,
|
||||
PremiumChat = 3,
|
||||
}
|
||||
|
||||
// 模型API类型枚举
|
||||
export enum ModelApiTypeEnum {
|
||||
OpenAi = 0,
|
||||
Claude = 1,
|
||||
}
|
||||
|
||||
// AI应用DTO
|
||||
export interface AiAppDto {
|
||||
id: string;
|
||||
name: string;
|
||||
endpoint: string;
|
||||
extraUrl?: string;
|
||||
apiKey: string;
|
||||
orderNum: number;
|
||||
creationTime: string;
|
||||
}
|
||||
|
||||
// 创建AI应用输入
|
||||
export interface AiAppCreateInput {
|
||||
name: string;
|
||||
endpoint: string;
|
||||
extraUrl?: string;
|
||||
apiKey: string;
|
||||
orderNum: number;
|
||||
}
|
||||
|
||||
// 更新AI应用输入
|
||||
export interface AiAppUpdateInput {
|
||||
id: string;
|
||||
name: string;
|
||||
endpoint: string;
|
||||
extraUrl?: string;
|
||||
apiKey: string;
|
||||
orderNum: number;
|
||||
}
|
||||
|
||||
// 获取AI应用列表输入
|
||||
export interface AiAppGetListInput {
|
||||
searchKey?: string;
|
||||
skipCount?: number;
|
||||
maxResultCount?: number;
|
||||
}
|
||||
|
||||
// AI模型DTO
|
||||
export interface AiModelDto {
|
||||
id: string;
|
||||
handlerName: string;
|
||||
modelId: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
orderNum: number;
|
||||
aiAppId: string;
|
||||
extraInfo?: string;
|
||||
modelType: ModelTypeEnum;
|
||||
modelApiType: ModelApiTypeEnum;
|
||||
multiplier: number;
|
||||
multiplierShow: number;
|
||||
providerName?: string;
|
||||
iconUrl?: string;
|
||||
isPremium: boolean;
|
||||
}
|
||||
|
||||
// 创建AI模型输入
|
||||
export interface AiModelCreateInput {
|
||||
handlerName: string;
|
||||
modelId: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
orderNum: number;
|
||||
aiAppId: string;
|
||||
extraInfo?: string;
|
||||
modelType: ModelTypeEnum;
|
||||
modelApiType: ModelApiTypeEnum;
|
||||
multiplier: number;
|
||||
multiplierShow: number;
|
||||
providerName?: string;
|
||||
iconUrl?: string;
|
||||
isPremium: boolean;
|
||||
}
|
||||
|
||||
// 更新AI模型输入
|
||||
export interface AiModelUpdateInput {
|
||||
id: string;
|
||||
handlerName: string;
|
||||
modelId: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
orderNum: number;
|
||||
aiAppId: string;
|
||||
extraInfo?: string;
|
||||
modelType: ModelTypeEnum;
|
||||
modelApiType: ModelApiTypeEnum;
|
||||
multiplier: number;
|
||||
multiplierShow: number;
|
||||
providerName?: string;
|
||||
iconUrl?: string;
|
||||
isPremium: boolean;
|
||||
}
|
||||
|
||||
// 获取AI模型列表输入
|
||||
export interface AiModelGetListInput {
|
||||
searchKey?: string;
|
||||
aiAppId?: string;
|
||||
isPremiumOnly?: boolean;
|
||||
skipCount?: number;
|
||||
maxResultCount?: number;
|
||||
}
|
||||
|
||||
// 分页结果
|
||||
export interface PagedResultDto<T> {
|
||||
items: T[];
|
||||
totalCount: number;
|
||||
}
|
||||
548
Yi.Ai.Vue3/src/pages/console/channel/index.vue
Normal file
548
Yi.Ai.Vue3/src/pages/console/channel/index.vue
Normal file
@@ -0,0 +1,548 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Delete, Edit, Plus, Refresh, View } from '@element-plus/icons-vue';
|
||||
import type { AiAppDto, AiModelDto } from '@/api/channel/types';
|
||||
import {
|
||||
getAppList,
|
||||
createApp,
|
||||
updateApp,
|
||||
deleteApp,
|
||||
getModelList,
|
||||
createModel,
|
||||
updateModel,
|
||||
deleteModel,
|
||||
} from '@/api/channel';
|
||||
|
||||
// ==================== 应用管理 ====================
|
||||
const appList = ref<AiAppDto[]>([]);
|
||||
const appLoading = ref(false);
|
||||
const selectedAppId = ref<string>('');
|
||||
|
||||
// 应用对话框
|
||||
const appDialogVisible = ref(false);
|
||||
const appDialogTitle = ref('');
|
||||
const appForm = ref<Partial<AiAppDto>>({});
|
||||
const appDetailDialogVisible = ref(false);
|
||||
const appDetailData = ref<AiAppDto | null>(null);
|
||||
|
||||
// 获取应用列表
|
||||
async function fetchAppList() {
|
||||
appLoading.value = true;
|
||||
try {
|
||||
const res = await getAppList({
|
||||
skipCount: 0,
|
||||
maxResultCount: 100,
|
||||
});
|
||||
appList.value = res.data.items;
|
||||
|
||||
// 默认选中第一个应用
|
||||
if (appList.value.length > 0 && !selectedAppId.value) {
|
||||
selectedAppId.value = appList.value[0].id;
|
||||
fetchModelList();
|
||||
}
|
||||
}
|
||||
catch (error: any) {
|
||||
ElMessage.error(error.message || '获取应用列表失败');
|
||||
}
|
||||
finally {
|
||||
appLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 选择应用
|
||||
function handleSelectApp(appId: string) {
|
||||
selectedAppId.value = appId;
|
||||
fetchModelList();
|
||||
}
|
||||
|
||||
// 查看应用详情
|
||||
function handleViewAppDetail(app: AiAppDto) {
|
||||
appDetailData.value = app;
|
||||
appDetailDialogVisible.value = true;
|
||||
}
|
||||
|
||||
// 打开应用对话框
|
||||
function openAppDialog(type: 'create' | 'edit', row?: AiAppDto) {
|
||||
appDialogTitle.value = type === 'create' ? '创建应用' : '编辑应用';
|
||||
if (type === 'create') {
|
||||
appForm.value = {
|
||||
name: '',
|
||||
endpoint: '',
|
||||
extraUrl: '',
|
||||
apiKey: '',
|
||||
orderNum: 0,
|
||||
};
|
||||
}
|
||||
else {
|
||||
appForm.value = { ...row };
|
||||
}
|
||||
appDialogVisible.value = true;
|
||||
}
|
||||
|
||||
// 保存应用
|
||||
async function saveApp() {
|
||||
try {
|
||||
if (appForm.value.id) {
|
||||
await updateApp(appForm.value as any);
|
||||
ElMessage.success('更新成功');
|
||||
}
|
||||
else {
|
||||
await createApp(appForm.value as any);
|
||||
ElMessage.success('创建成功');
|
||||
}
|
||||
appDialogVisible.value = false;
|
||||
fetchAppList();
|
||||
}
|
||||
catch (error: any) {
|
||||
ElMessage.error(error.message || '保存失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 删除应用
|
||||
async function handleDeleteApp(row: AiAppDto) {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除该应用吗?删除后该应用下的所有模型将无法使用。', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
});
|
||||
await deleteApp(row.id);
|
||||
ElMessage.success('删除成功');
|
||||
|
||||
// 如果删除的是当前选中的应用,清空选中状态
|
||||
if (selectedAppId.value === row.id) {
|
||||
selectedAppId.value = '';
|
||||
modelList.value = [];
|
||||
}
|
||||
|
||||
fetchAppList();
|
||||
}
|
||||
catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
ElMessage.error(error.message || '删除失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 模型管理 ====================
|
||||
const modelList = ref<AiModelDto[]>([]);
|
||||
const modelLoading = ref(false);
|
||||
const modelSearchKey = ref('');
|
||||
const modelDialogVisible = ref(false);
|
||||
const modelDialogTitle = ref('');
|
||||
const modelForm = ref<Partial<AiModelDto>>({});
|
||||
|
||||
// 获取模型列表
|
||||
async function fetchModelList() {
|
||||
if (!selectedAppId.value) {
|
||||
modelList.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
modelLoading.value = true;
|
||||
try {
|
||||
const res = await getModelList({
|
||||
aiAppId: selectedAppId.value,
|
||||
searchKey: modelSearchKey.value,
|
||||
skipCount: 0,
|
||||
maxResultCount: 100,
|
||||
});
|
||||
modelList.value = res.data.items;
|
||||
}
|
||||
catch (error: any) {
|
||||
ElMessage.error(error.message || '获取模型列表失败');
|
||||
}
|
||||
finally {
|
||||
modelLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 打开模型对话框
|
||||
function openModelDialog(type: 'create' | 'edit', row?: AiModelDto) {
|
||||
if (!selectedAppId.value) {
|
||||
ElMessage.warning('请先选择一个应用');
|
||||
return;
|
||||
}
|
||||
|
||||
modelDialogTitle.value = type === 'create' ? '创建模型' : '编辑模型';
|
||||
if (type === 'create') {
|
||||
modelForm.value = {
|
||||
handlerName: '',
|
||||
modelId: '',
|
||||
name: '',
|
||||
description: '',
|
||||
orderNum: 0,
|
||||
aiAppId: selectedAppId.value,
|
||||
extraInfo: '',
|
||||
modelType: 0,
|
||||
modelApiType: 0,
|
||||
multiplier: 1,
|
||||
multiplierShow: 1,
|
||||
providerName: '',
|
||||
iconUrl: '',
|
||||
isPremium: false,
|
||||
};
|
||||
}
|
||||
else {
|
||||
modelForm.value = { ...row };
|
||||
}
|
||||
modelDialogVisible.value = true;
|
||||
}
|
||||
|
||||
// 保存模型
|
||||
async function saveModel() {
|
||||
try {
|
||||
if (modelForm.value.id) {
|
||||
await updateModel(modelForm.value as any);
|
||||
ElMessage.success('更新成功');
|
||||
}
|
||||
else {
|
||||
await createModel(modelForm.value as any);
|
||||
ElMessage.success('创建成功');
|
||||
}
|
||||
modelDialogVisible.value = false;
|
||||
fetchModelList();
|
||||
}
|
||||
catch (error: any) {
|
||||
ElMessage.error(error.message || '保存失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 删除模型
|
||||
async function handleDeleteModel(row: AiModelDto) {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除该模型吗?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
});
|
||||
await deleteModel(row.id);
|
||||
ElMessage.success('删除成功');
|
||||
fetchModelList();
|
||||
}
|
||||
catch (error: any) {
|
||||
if (error !== 'cancel') {
|
||||
ElMessage.error(error.message || '删除失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
fetchAppList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="channel-management">
|
||||
<div class="channel-container">
|
||||
<!-- 左侧应用列表 -->
|
||||
<div class="app-list-panel">
|
||||
<div class="panel-header">
|
||||
<h3>应用列表</h3>
|
||||
<el-button type="primary" size="small" :icon="Plus" @click="openAppDialog('create')">
|
||||
新建
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-scrollbar class="app-list-scrollbar">
|
||||
<div v-loading="appLoading" class="app-list">
|
||||
<div
|
||||
v-for="app in appList"
|
||||
:key="app.id"
|
||||
class="app-item"
|
||||
:class="{ active: selectedAppId === app.id }"
|
||||
@click="handleSelectApp(app.id)"
|
||||
>
|
||||
<div class="app-item-content">
|
||||
<div class="app-name">{{ app.name }}</div>
|
||||
<div class="app-actions">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
:icon="View"
|
||||
@click.stop="handleViewAppDetail(app)"
|
||||
>
|
||||
详情
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
:icon="Edit"
|
||||
@click.stop="openAppDialog('edit', app)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
size="small"
|
||||
:icon="Delete"
|
||||
@click.stop="handleDeleteApp(app)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-empty v-if="!appLoading && appList.length === 0" description="暂无应用" />
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
|
||||
<!-- 右侧模型列表 -->
|
||||
<div class="model-list-panel">
|
||||
<div class="panel-header">
|
||||
<h3>模型列表</h3>
|
||||
<div class="header-actions">
|
||||
<el-input
|
||||
v-model="modelSearchKey"
|
||||
placeholder="搜索模型"
|
||||
style="width: 200px; margin-right: 10px"
|
||||
clearable
|
||||
@keyup.enter="fetchModelList"
|
||||
/>
|
||||
<el-button type="primary" size="small" :icon="Plus" @click="openModelDialog('create')">
|
||||
新建
|
||||
</el-button>
|
||||
<el-button size="small" :icon="Refresh" @click="fetchModelList">
|
||||
刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!selectedAppId" class="empty-tip">
|
||||
<el-empty description="请先选择左侧的应用" />
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-else
|
||||
v-loading="modelLoading"
|
||||
:data="modelList"
|
||||
border
|
||||
stripe
|
||||
height="calc(100vh - 220px)"
|
||||
>
|
||||
<el-table-column prop="name" label="模型名称" min-width="150" />
|
||||
<el-table-column prop="modelId" label="模型ID" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column prop="handlerName" label="处理名" min-width="120" />
|
||||
<el-table-column prop="providerName" label="供应商" width="100" />
|
||||
<el-table-column label="是否尊享" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.isPremium ? 'warning' : 'info'">
|
||||
{{ row.isPremium ? '尊享' : '普通' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="multiplierShow" label="显示倍率" width="100" />
|
||||
<el-table-column prop="orderNum" label="排序" width="80" />
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" :icon="Edit" @click="openModelDialog('edit', row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button link type="danger" :icon="Delete" @click="handleDeleteModel(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 应用详情对话框 -->
|
||||
<el-dialog v-model="appDetailDialogVisible" title="应用详情" width="600px">
|
||||
<el-descriptions v-if="appDetailData" :column="1" border>
|
||||
<el-descriptions-item label="应用名称">{{ appDetailData.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="终结点">{{ appDetailData.endpoint }}</el-descriptions-item>
|
||||
<el-descriptions-item label="额外URL">{{ appDetailData.extraUrl || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="API Key">
|
||||
<el-input :model-value="appDetailData.apiKey" type="textarea" readonly />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="排序">{{ appDetailData.orderNum }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ appDetailData.creationTime }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 应用编辑对话框 -->
|
||||
<el-dialog v-model="appDialogVisible" :title="appDialogTitle" width="600px">
|
||||
<el-form :model="appForm" label-width="120px">
|
||||
<el-form-item label="应用名称" required>
|
||||
<el-input v-model="appForm.name" placeholder="请输入应用名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="终结点" required>
|
||||
<el-input v-model="appForm.endpoint" placeholder="请输入应用终结点URL" />
|
||||
</el-form-item>
|
||||
<el-form-item label="额外URL">
|
||||
<el-input v-model="appForm.extraUrl" placeholder="请输入额外URL(可选)" />
|
||||
</el-form-item>
|
||||
<el-form-item label="API Key" required>
|
||||
<el-input v-model="appForm.apiKey" type="textarea" placeholder="请输入API Key" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序">
|
||||
<el-input-number v-model="appForm.orderNum" :min="0" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="appDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveApp">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 模型编辑对话框 -->
|
||||
<el-dialog v-model="modelDialogVisible" :title="modelDialogTitle" width="700px">
|
||||
<el-form :model="modelForm" label-width="120px">
|
||||
<el-form-item label="模型名称" required>
|
||||
<el-input v-model="modelForm.name" placeholder="请输入模型名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="模型ID" required>
|
||||
<el-input v-model="modelForm.modelId" placeholder="请输入模型ID" />
|
||||
</el-form-item>
|
||||
<el-form-item label="处理名" required>
|
||||
<el-input v-model="modelForm.handlerName" placeholder="请输入处理名" />
|
||||
</el-form-item>
|
||||
<el-form-item label="供应商名称">
|
||||
<el-input v-model="modelForm.providerName" placeholder="如:OpenAI、Anthropic等" />
|
||||
</el-form-item>
|
||||
<el-form-item label="模型描述">
|
||||
<el-input v-model="modelForm.description" type="textarea" placeholder="请输入模型描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否尊享模型">
|
||||
<el-switch v-model="modelForm.isPremium" />
|
||||
</el-form-item>
|
||||
<el-form-item label="模型倍率">
|
||||
<el-input-number v-model="modelForm.multiplier" :min="0.01" :step="0.1" />
|
||||
</el-form-item>
|
||||
<el-form-item label="显示倍率">
|
||||
<el-input-number v-model="modelForm.multiplierShow" :min="0.01" :step="0.1" />
|
||||
</el-form-item>
|
||||
<el-form-item label="模型类型" required>
|
||||
<el-select v-model="modelForm.modelType" placeholder="请选择模型类型">
|
||||
<el-option label="聊天" :value="0" />
|
||||
<el-option label="图片" :value="1" />
|
||||
<el-option label="嵌入" :value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="API类型" required>
|
||||
<el-select v-model="modelForm.modelApiType" placeholder="请选择API类型">
|
||||
<el-option label="OpenAI" :value="0" />
|
||||
<el-option label="Claude" :value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="图标URL">
|
||||
<el-input v-model="modelForm.iconUrl" placeholder="请输入模型图标URL" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序">
|
||||
<el-input-number v-model="modelForm.orderNum" :min="0" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="modelDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveModel">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.channel-management {
|
||||
padding: 20px;
|
||||
height: calc(100vh - 40px);
|
||||
|
||||
.channel-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.app-list-panel {
|
||||
width: 350px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.model-list-panel {
|
||||
flex: 1;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
padding: 16px 20px;
|
||||
border-bottom: 1px solid #eee;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.app-list-scrollbar {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.app-list {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.app-item {
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 8px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
border-color: #409eff;
|
||||
background: #f0f9ff;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: #409eff;
|
||||
background: #ecf5ff;
|
||||
}
|
||||
|
||||
.app-item-content {
|
||||
.app-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.app-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -19,6 +19,7 @@ const navItems = [
|
||||
{ name: 'daily-task', label: '每日任务(限时)', icon: 'Trophy', path: '/console/daily-task' },
|
||||
{ name: 'invite', label: '每周邀请(限时)', icon: 'Present', path: '/console/invite' },
|
||||
{ name: 'activation', label: '激活码兑换', icon: 'MagicStick', path: '/console/activation' },
|
||||
{ name: 'channel', label: '渠道商管理', icon: 'Setting', path: '/console/channel' },
|
||||
];
|
||||
|
||||
// 当前激活的菜单
|
||||
|
||||
@@ -207,6 +207,14 @@ export const layoutRouter: RouteRecordRaw[] = [
|
||||
title: '激活码兑换',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'channel',
|
||||
name: 'consoleChannel',
|
||||
component: () => import('@/pages/console/channel/index.vue'),
|
||||
meta: {
|
||||
title: '渠道商管理',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user