更新创作模型配置功能

- 在创作页面中新增会话ID和流ID字段,增强执行流的管理能力。
- 在模型配置页面中添加模型类型字段,提升模型管理的灵活性。
- 优化模型选择器,更新API Key配置弹窗,改善用户交互体验。
This commit is contained in:
2026-05-12 11:24:42 +08:00
parent b0e62fb966
commit 87b25dee42
5 changed files with 183 additions and 35 deletions

View File

@@ -76,6 +76,8 @@ export interface ExecutionItem {
export interface ExecutionFlowItem {
flowName: string;
flowId?: number | string;
sessionId?: string;
items: ExecutionItem[];
}

View File

@@ -4,6 +4,7 @@ export interface ModelModuleListParams {
pageNum?: number;
pageSize?: number;
modelName?: string;
modelsType?: number | string;
}
export interface ModelFormItem {

View File

@@ -222,7 +222,7 @@ const handleSelectModel = (model: ModelItem) => {
if (model.tenantId === 1) {
// 系统模型,需要用户配置 API Key
systemModelToClone.value = model;
apiKeyForm.modelName = `${model.modelName} - 副本`;
apiKeyForm.modelName = model.modelName;
apiKeyForm.apiKey = '';
apiKeyDialogVisible.value = true;
} else {

View File

@@ -341,8 +341,23 @@
<!-- 输入框 -->
<el-input v-model="userInput" placeholder="说点什么..." class="chat-input" @keydown.enter="sendMessage" />
<!-- 右侧发送按钮 -->
<el-button type="primary" :icon="Promotion" :loading="isCreating" @click="sendMessage" class="send-btn" circle />
<!-- 右侧发送/暂停按钮 -->
<el-button
v-if="!isCreating"
type="primary"
:icon="Promotion"
@click="sendMessage"
class="send-btn"
circle
/>
<el-button
v-else
type="danger"
:icon="VideoPause"
@click="stopExecution"
class="send-btn"
circle
/>
</div>
<!-- 已选技能标签 -->
@@ -556,7 +571,7 @@
<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Document, Plus, Paperclip, MagicStick, Promotion, Check, Setting, Search, CircleCheck } from '@element-plus/icons-vue';
import { Document, Plus, Paperclip, MagicStick, Promotion, Check, Setting, Search, CircleCheck, VideoPause } from '@element-plus/icons-vue';
import LogicFlow from '@logicflow/core';
import { Control, SelectionSelect } from '@logicflow/extension';
import '@logicflow/core/dist/index.css';
@@ -1287,8 +1302,22 @@ const sendMessage = async () => {
isCreating.value = false;
}
};
// 终止执行(接口预留)
const stopExecution = async () => {
try {
// TODO: 调用终止执行的接口
// await stopExecutionApi({ sessionId: getSessionId() });
ElMessage.warning('终止执行功能开发中...');
// 暂时直接停止加载状态
isCreating.value = false;
} catch (error) {
ElMessage.error('终止执行失败');
}
};
// 判断节点是否有可见字段
const hasVisibleFields = (node: any) => {
const _hasVisibleFields = (node: any) => {
if (!node.config) return false;
const excludeKeys = ['nodeCode', 'width', 'height', 'x', 'y', 'formConfig', 'inputSource', 'fieldMetadata', 'selectedModel'];
return Object.keys(node.config).some((key) => !excludeKeys.includes(key));
@@ -1305,7 +1334,7 @@ const handleTreeNodeClick = async (data: TreeNode) => {
if (data.nodeType === 'contentType' && data.flowId) {
try {
// 调用详情接口获取工作流数据,使用 flowId
const res = await getWorkflowDetail(data.flowId);
const res = await getWorkflowDetail(String(data.flowId));
if (res.data) {
// 设置当前会话的 sessionId从工作空间进入
currentSessionId.value = data.sessionId || null;

View File

@@ -35,17 +35,6 @@
</template>
</el-table-column>
<el-table-column prop="httpMethod" label="请求方式" width="100"></el-table-column>
<el-table-column label="会话模型" width="110" align="center">
<template #default="{ row }">
<el-switch
v-if="isInferenceModel(row.modelsType)"
size="small"
:model-value="Number(row.isChatModel) === 1"
:before-change="() => onChatModelSwitchRequest(row)"
/>
<span v-else style="color: #999;">—</span>
</template>
</el-table-column>
<el-table-column prop="enabled" label="状态" width="100">
<template #default="scope">
<el-tag :type="scope.row.enabled === 1 ? 'success' : 'danger'">{{ scope.row.enabled === 1 ? '启用' : '禁用' }}</el-tag>
@@ -56,10 +45,29 @@
<el-table-column prop="remark" label="备注" show-overflow-tooltip></el-table-column>
<el-table-column prop="createdAt" label="创建时间" show-overflow-tooltip width="160"></el-table-column>
<el-table-column prop="updatedAt" label="修改时间" show-overflow-tooltip width="160"></el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<el-table-column label="操作" width="300" fixed="right">
<template #default="scope">
<el-button size="small" text type="primary" @click="onOpenEditModule('edit', scope.row)">修改</el-button>
<el-button size="small" text type="danger" @click="onRowDel(scope.row)">删除</el-button>
<div class="action-buttons">
<el-button size="small" text type="primary" @click="onOpenEditModule('edit', scope.row)">修改</el-button>
<el-button
v-if="isInferenceModel(scope.row.modelsType) && Number(scope.row.isChatModel) !== 1"
size="small"
text
type="warning"
@click="onSetChatModel(scope.row)"
>
设为会话模型
</el-button>
<el-tag
v-if="isInferenceModel(scope.row.modelsType) && Number(scope.row.isChatModel) === 1"
type="success"
effect="dark"
size="default"
>
当前会话模型
</el-tag>
<el-button size="small" text type="danger" @click="onRowDel(scope.row)">删除</el-button>
</div>
</template>
</el-table-column>
</el-table>
@@ -78,19 +86,45 @@
</el-pagination>
</el-card>
<EditModule ref="editModuleRef" :model-types="state.modelTypes" @refresh="getTableData()" />
<!-- 系统模型 API Key 输入弹窗 -->
<el-dialog v-model="apiKeyDialogVisible" title="配置系统模型" width="500px" :close-on-click-modal="false">
<el-alert type="info" :closable="false" style="margin-bottom: 16px">
<template #title>
<div style="line-height: 1.6">
您选择的是系统模型需要配置您自己的 API Key<br />
系统将为您创建一个模型副本并设置为会话模型
</div>
</template>
</el-alert>
<el-form :model="apiKeyForm" :rules="apiKeyRules" ref="apiKeyFormRef" label-width="100px">
<el-form-item label="模型名称" prop="modelName">
<el-input v-model="apiKeyForm.modelName" placeholder="请输入模型名称" />
</el-form-item>
<el-form-item label="API Key" prop="apiKey">
<el-input v-model="apiKeyForm.apiKey" type="password" show-password placeholder="请输入您的 API Key" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="apiKeyDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleCreatePrivateModelAndSetChat" :loading="creatingModel">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="digitalHumanModelModule">
import { defineAsyncComponent, reactive, onMounted, ref } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import { ElMessageBox, ElMessage, type FormInstance, type FormRules } from 'element-plus';
import {
getModelModuleList,
getModelTypeList,
deleteModelModule,
normalizeModelTypeOptions,
updateChatModel,
addModelModule,
} from '/@/api/digitalHuman/modelConfig/modelModule/index';
import { getApiErrorMessage } from '/@/utils/request';
const EditModule = defineAsyncComponent(() => import('/@/views/digitalHuman/modelConfig/modelModule/component/editModule.vue'));
@@ -110,7 +144,21 @@ const state = reactive({
},
});
// 判断是否为推理模型(只有推理模型才显示会话模型开关)
// 系统模型 API Key 配置
const apiKeyDialogVisible = ref(false);
const apiKeyFormRef = ref<FormInstance>();
const apiKeyForm = reactive({
modelName: '',
apiKey: '',
});
const apiKeyRules: FormRules = {
modelName: [{ required: true, message: '请输入模型名称', trigger: 'blur' }],
apiKey: [{ required: true, message: '请输入 API Key', trigger: 'blur' }],
};
const creatingModel = ref(false);
const systemModelToClone = ref<any>(null);
// 判断是否为推理模型(只有推理模型才能设置为会话模型)
const isInferenceModel = (modelsType: number | string | undefined | null) => {
if (modelsType === undefined || modelsType === null || modelsType === '') {
return false;
@@ -120,21 +168,79 @@ const isInferenceModel = (modelsType: number | string | undefined | null) => {
return typeInfo?.label === '推理模型' || String(modelsType) === '1';
};
// 会话模型开关切换
const onChatModelSwitchRequest = async (row: { id?: number | string; isChatModel?: number }) => {
// 设置为会话模型
const onSetChatModel = async (row: any) => {
// 判断是否是系统模型tenantId === 1
if (row.tenantId === 1) {
// 系统模型,需要用户配置 API Key
systemModelToClone.value = row;
apiKeyForm.modelName = row.modelName;
apiKeyForm.apiKey = '';
apiKeyDialogVisible.value = true;
} else {
// 非系统模型,直接设置为会话模型
try {
await updateChatModel({
id: row.id!,
isChatModel: 1,
});
ElMessage.success('已设置为会话模型');
await getTableData();
} catch {
// 错误已由全局拦截器处理
}
}
};
// 创建私有模型并设置为会话模型
const handleCreatePrivateModelAndSetChat = async () => {
if (!apiKeyFormRef.value || !systemModelToClone.value) return;
try {
const newStatus = Number(row.isChatModel) === 1 ? 0 : 1;
await updateChatModel({
id: row.id!,
isChatModel: newStatus as 0 | 1,
});
ElMessage.success(newStatus === 1 ? '已设置为会话模型' : '已取消会话模型');
// 重新获取列表数据
await apiKeyFormRef.value.validate();
creatingModel.value = true;
// 基于系统模型创建新模型(继承原模型的所有配置,只替换 apiKey
const systemModel = systemModelToClone.value;
const createParams = {
modelName: apiKeyForm.modelName,
modelsType: systemModel.modelsType,
baseUrl: systemModel.baseUrl,
httpMethod: systemModel.httpMethod || 'POST',
headMsg: systemModel.headMsg || '',
isPrivate: systemModel.isPrivate ?? 1,
enabled: systemModel.enabled ?? 1,
isChatModel: 1, // 设置为会话模型
apiKey: apiKeyForm.apiKey,
form: systemModel.form || [],
requestMapping: systemModel.requestMapping || {},
responseMapping: systemModel.responseMapping || {},
maxConcurrency: systemModel.maxConcurrency || 10,
queueLimit: systemModel.queueLimit || 100,
timeoutSeconds: systemModel.timeoutSeconds || 30,
expectedSeconds: systemModel.expectedSeconds || 15,
retryTimes: systemModel.retryTimes || 3,
retryQueueMaxSeconds: systemModel.retryQueueMaxSeconds || 60,
autoCleanSeconds: systemModel.autoCleanSeconds || 300,
remark: systemModel.remark || '',
};
await addModelModule(createParams);
ElMessage.success('模型创建成功并已设置为会话模型');
// 关闭对话框
apiKeyDialogVisible.value = false;
// 刷新列表
await getTableData();
return true;
} catch {
// 接口错误由 request 全局提示后端 message
return false;
} catch (error: any) {
if (error !== 'cancel') {
ElMessage.error(getApiErrorMessage(error, '创建模型失败'));
}
} finally {
creatingModel.value = false;
}
};
@@ -237,4 +343,14 @@ onMounted(async () => {
}
}
}
.action-buttons {
display: flex;
align-items: center;
gap: 8px;
.el-button {
margin: 0;
}
}
</style>