feat: restore ai app context support

This commit is contained in:
2026-06-12 10:04:48 +08:00
parent faa14fd3dd
commit 16490392b2
10 changed files with 154 additions and 12 deletions
@@ -13,6 +13,12 @@ const routes: RouteRecordRaw[] = [
path: '/ai-platform/knowledge', path: '/ai-platform/knowledge',
redirect: '/ai-platform/knowledge-base', redirect: '/ai-platform/knowledge-base',
}, },
{
meta: { hideInMenu: true },
name: 'SubAppAIKnowledgeLegacyRedirect',
path: '/app/:appCode/ai-platform/knowledge',
redirect: (to) => `/app/${to.params.appCode}/ai-platform/knowledge-base`,
},
{ {
meta: { meta: {
hideInMenu: true, hideInMenu: true,
@@ -22,6 +28,15 @@ const routes: RouteRecordRaw[] = [
path: '/agent-chat/:code', path: '/agent-chat/:code',
component: () => import('#/views/_core/agent-chat/index.vue'), component: () => import('#/views/_core/agent-chat/index.vue'),
}, },
{
meta: {
hideInMenu: true,
title: '智能体对话',
},
name: 'SubAppAgentChat',
path: '/app/:appCode/agent-chat/:code',
component: () => import('#/views/_core/agent-chat/index.vue'),
},
{ {
meta: { meta: {
hideInMenu: true, hideInMenu: true,
@@ -31,12 +46,28 @@ const routes: RouteRecordRaw[] = [
path: '/ai-platform/model', path: '/ai-platform/model',
component: () => import('#/views/ai-platform/model/index.vue'), component: () => import('#/views/ai-platform/model/index.vue'),
}, },
{
meta: {
hideInMenu: true,
title: '模型配置',
},
name: 'SubAppAIModelConfig',
path: '/app/:appCode/ai-platform/model',
component: () => import('#/views/ai-platform/model/index.vue'),
},
{ {
meta: { hideInMenu: true }, meta: { hideInMenu: true },
name: 'AIKnowledgeLegacyDetailRedirect', name: 'AIKnowledgeLegacyDetailRedirect',
path: '/ai-platform/knowledge/detail/:id', path: '/ai-platform/knowledge/detail/:id',
redirect: (to) => `/ai-platform/knowledge-base/detail/${to.params.id}`, redirect: (to) => `/ai-platform/knowledge-base/detail/${to.params.id}`,
}, },
{
meta: { hideInMenu: true },
name: 'SubAppAIKnowledgeLegacyDetailRedirect',
path: '/app/:appCode/ai-platform/knowledge/detail/:id',
redirect: (to) =>
`/app/${to.params.appCode}/ai-platform/knowledge-base/detail/${to.params.id}`,
},
{ {
meta: { meta: {
hideInMenu: true, hideInMenu: true,
@@ -46,6 +77,15 @@ const routes: RouteRecordRaw[] = [
path: '/ai-platform/workflow-runs', path: '/ai-platform/workflow-runs',
component: () => import('#/views/ai-platform/workflow-runs/index.vue'), component: () => import('#/views/ai-platform/workflow-runs/index.vue'),
}, },
{
meta: {
hideInMenu: true,
title: '执行历史',
},
name: 'SubAppAIWorkflowRuns',
path: '/app/:appCode/ai-platform/workflow-runs',
component: () => import('#/views/ai-platform/workflow-runs/index.vue'),
},
// 工作流编辑器 // 工作流编辑器
{ {
meta: { meta: {
@@ -57,6 +97,16 @@ const routes: RouteRecordRaw[] = [
path: '/ai-platform/workflow/editor/:id', path: '/ai-platform/workflow/editor/:id',
component: () => import('#/views/ai-platform/workflow/editor/index.vue'), component: () => import('#/views/ai-platform/workflow/editor/index.vue'),
}, },
{
meta: {
hideInMenu: true,
title: '工作流编辑器',
noBasicLayout: true,
},
name: 'SubAppAIWorkflowEditor',
path: '/app/:appCode/ai-platform/workflow/editor/:id',
component: () => import('#/views/ai-platform/workflow/editor/index.vue'),
},
// 智能体编辑器 // 智能体编辑器
{ {
meta: { meta: {
@@ -68,6 +118,16 @@ const routes: RouteRecordRaw[] = [
path: '/ai-platform/agent/editor/:id', path: '/ai-platform/agent/editor/:id',
component: () => import('#/views/ai-platform/agent/editor/index.vue'), component: () => import('#/views/ai-platform/agent/editor/index.vue'),
}, },
{
meta: {
hideInMenu: true,
title: '智能体编辑器',
noBasicLayout: true,
},
name: 'SubAppAIAgentEditor',
path: '/app/:appCode/ai-platform/agent/editor/:id',
component: () => import('#/views/ai-platform/agent/editor/index.vue'),
},
// 知识库详情 // 知识库详情
{ {
meta: { meta: {
@@ -78,6 +138,15 @@ const routes: RouteRecordRaw[] = [
path: '/ai-platform/knowledge-base/detail/:id', path: '/ai-platform/knowledge-base/detail/:id',
component: () => import('#/views/ai-platform/knowledge/detail/index.vue'), component: () => import('#/views/ai-platform/knowledge/detail/index.vue'),
}, },
{
meta: {
hideInMenu: true,
title: '知识库详情',
},
name: 'SubAppAIKnowledgeDetail',
path: '/app/:appCode/ai-platform/knowledge-base/detail/:id',
component: () => import('#/views/ai-platform/knowledge/detail/index.vue'),
},
]; ];
export default routes; export default routes;
@@ -19,6 +19,7 @@ import {
import { publishAgentToMenuApi } from '#/api/ai-platform/ai-platform'; import { publishAgentToMenuApi } from '#/api/ai-platform/ai-platform';
import { ZqIconPicker } from '#/components/zq-form/zq-icon-picker'; import { ZqIconPicker } from '#/components/zq-form/zq-icon-picker';
import { ZqMenuSelector } from '#/components/zq-form/zq-menu-selector'; import { ZqMenuSelector } from '#/components/zq-form/zq-menu-selector';
import { useAppContextStore } from '#/store/app-context';
interface Props { interface Props {
modelValue: boolean; modelValue: boolean;
@@ -34,6 +35,8 @@ const emit = defineEmits<{
'update:modelValue': [value: boolean]; 'update:modelValue': [value: boolean];
}>(); }>();
const appContextStore = useAppContextStore();
const dialogVisible = computed({ const dialogVisible = computed({
get: () => props.modelValue, get: () => props.modelValue,
set: (val) => emit('update:modelValue', val), set: (val) => emit('update:modelValue', val),
@@ -130,6 +133,7 @@ async function handlePublish() {
:placeholder=" :placeholder="
$t('ai-platform.agent.publishDialog.parentMenuPlaceholder') $t('ai-platform.agent.publishDialog.parentMenuPlaceholder')
" "
:application-id="appContextStore.currentApp?.id"
@change="handleMenuChange" @change="handleMenuChange"
/> />
</ElFormItem> </ElFormItem>
@@ -49,10 +49,12 @@ import {
publishAgentApi, publishAgentApi,
updateAgentApi, updateAgentApi,
} from '#/api/ai-platform/ai-platform'; } from '#/api/ai-platform/ai-platform';
import { useAppContextStore } from '#/store/app-context';
const AiChatPanel = defineAsyncComponent(() => const AiChatPanel = defineAsyncComponent(() =>
import('#/components/ai-chat-panel/AiChatPanel.vue'), import('#/components/ai-chat-panel/AiChatPanel.vue'),
); );
const appContextStore = useAppContextStore();
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
@@ -217,7 +219,7 @@ async function handlePublish() {
// 返回 // 返回
function handleBack() { function handleBack() {
router.push('/ai-platform/agent'); router.push(appContextStore.getContextPath('/ai-platform/agent'));
} }
// 添加性格特点 // 添加性格特点
@@ -4,7 +4,7 @@ import type {
AgentListItem, AgentListItem,
} from '#/api/ai-platform/ai-platform'; } from '#/api/ai-platform/ai-platform';
import { onMounted, reactive, ref } from 'vue'; import { computed, onMounted, reactive, ref } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { Page } from '@vben/common-ui/es/page'; import { Page } from '@vben/common-ui/es/page';
@@ -39,6 +39,7 @@ import {
ElOption, ElOption,
ElPagination, ElPagination,
ElSelect, ElSelect,
ElSwitch,
ElTag, ElTag,
ElTooltip, ElTooltip,
} from 'element-plus'; } from 'element-plus';
@@ -52,6 +53,7 @@ import {
publishAgentApi, publishAgentApi,
unpublishAgentMenuApi, unpublishAgentMenuApi,
} from '#/api/ai-platform/ai-platform'; } from '#/api/ai-platform/ai-platform';
import { useAppContextStore } from '#/store/app-context';
import PublishToMenuDialog from './components/PublishToMenuDialog.vue'; import PublishToMenuDialog from './components/PublishToMenuDialog.vue';
import ImportDialog from './modules/import-dialog.vue'; import ImportDialog from './modules/import-dialog.vue';
@@ -59,6 +61,8 @@ import ImportDialog from './modules/import-dialog.vue';
defineOptions({ name: 'AgentList' }); defineOptions({ name: 'AgentList' });
const router = useRouter(); const router = useRouter();
const appContextStore = useAppContextStore();
const isMainApp = computed(() => appContextStore.isMainApp);
// 搜索关键词 // 搜索关键词
const searchKeyword = ref(''); const searchKeyword = ref('');
@@ -100,6 +104,7 @@ async function fetchList() {
page: pagination.current, page: pagination.current,
pageSize: pagination.pageSize, pageSize: pagination.pageSize,
name: searchKeyword.value || undefined, name: searchKeyword.value || undefined,
applicationId: appContextStore.currentApp?.id,
}); });
agentList.value = res.items; agentList.value = res.items;
pagination.total = res.total; pagination.total = res.total;
@@ -156,10 +161,13 @@ async function handleCreate() {
try { try {
const agent = await createAgentApi({ const agent = await createAgentApi({
...createForm, ...createForm,
application_id: appContextStore.currentApp?.id,
}); });
ElMessage.success($t('ai-platform.agent.createSuccess')); ElMessage.success($t('ai-platform.agent.createSuccess'));
createDialogVisible.value = false; createDialogVisible.value = false;
router.push(`/ai-platform/agent/editor/${agent.id}`); router.push(
appContextStore.getContextPath(`/ai-platform/agent/editor/${agent.id}`),
);
} catch (error: any) { } catch (error: any) {
ElMessage.error(error.message || $t('ai-platform.agent.createFailed')); ElMessage.error(error.message || $t('ai-platform.agent.createFailed'));
} finally { } finally {
@@ -169,12 +177,14 @@ async function handleCreate() {
// 编辑智能体 // 编辑智能体
function handleEdit(agent: AgentListItem) { function handleEdit(agent: AgentListItem) {
router.push(`/ai-platform/agent/editor/${agent.id}`); router.push(
appContextStore.getContextPath(`/ai-platform/agent/editor/${agent.id}`),
);
} }
// 对话 // 对话
function handleChat(agent: AgentListItem) { function handleChat(agent: AgentListItem) {
router.push(`/agent-chat/${agent.code}`); router.push(appContextStore.getContextPath(`/agent-chat/${agent.code}`));
} }
// 发布 // 发布
@@ -497,8 +507,10 @@ onMounted(() => {
<!-- 应用名称 + 创建时间 --> <!-- 应用名称 + 创建时间 -->
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="text-muted-foreground text-xs"> <div class="text-muted-foreground text-xs">
{{ agent.model_name || '未配置模型' }} · <span v-if="agent.application_name">
{{ agent.conversation_count || 0 }} 次对话 {{ agent.application_name }}
</span>
<span v-else>{{ $t('ai-platform.agent.mainApp') }}</span>
</div> </div>
<span class="text-muted-foreground text-xs"> <span class="text-muted-foreground text-xs">
{{ agent.sys_create_datetime }} {{ agent.sys_create_datetime }}
@@ -599,6 +611,12 @@ onMounted(() => {
type="textarea" type="textarea"
/> />
</ElFormItem> </ElFormItem>
<ElFormItem
v-if="isMainApp"
:label="$t('ai-platform.agent.form.globalVisible')"
>
<ElSwitch v-model="createForm.is_global" />
</ElFormItem>
</ElForm> </ElForm>
<template #footer> <template #footer>
<ElButton @click="createDialogVisible = false"> <ElButton @click="createDialogVisible = false">
@@ -24,6 +24,7 @@ import {
importAgentConfigApi, importAgentConfigApi,
} from '#/api/ai-platform/ai-platform'; } from '#/api/ai-platform/ai-platform';
import { ZqDialog } from '#/components/zq-dialog'; import { ZqDialog } from '#/components/zq-dialog';
import { useAppContextStore } from '#/store/app-context';
interface Props { interface Props {
modelValue: boolean; modelValue: boolean;
@@ -36,6 +37,8 @@ const emit = defineEmits<{
'update:modelValue': [value: boolean]; 'update:modelValue': [value: boolean];
}>(); }>();
const appContextStore = useAppContextStore();
const dialogVisible = computed({ const dialogVisible = computed({
get: () => props.modelValue, get: () => props.modelValue,
set: (val) => emit('update:modelValue', val), set: (val) => emit('update:modelValue', val),
@@ -129,6 +132,7 @@ async function handleConfirmImport() {
try { try {
const payload: AgentImportInput = { const payload: AgentImportInput = {
...importData.value, ...importData.value,
application_id: appContextStore.currentApp?.id,
code: checkResult.value?.code_exists code: checkResult.value?.code_exists
? newCode.value.trim() ? newCode.value.trim()
: importData.value.code, : importData.value.code,
@@ -97,9 +97,11 @@ import { ZqDialog } from '#/components/zq-dialog';
import FileSelector from '#/components/zq-form/file-selector/file-selector.vue'; import FileSelector from '#/components/zq-form/file-selector/file-selector.vue';
import { ZqTabs } from '#/components/zq-tabs/index'; import { ZqTabs } from '#/components/zq-tabs/index';
import { getFileUrl } from '#/composables/useFileUrl'; import { getFileUrl } from '#/composables/useFileUrl';
import { useAppContextStore } from '#/store/app-context';
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const appContextStore = useAppContextStore();
const kbId = computed(() => route.params.id as string); const kbId = computed(() => route.params.id as string);
const activeTab = ref('documents'); const activeTab = ref('documents');
@@ -865,7 +867,7 @@ async function handleSaveSettings() {
} }
function handleBack() { function handleBack() {
router.push('/ai-platform/knowledge-base'); router.push(appContextStore.getContextPath('/ai-platform/knowledge-base'));
} }
// Tab 切换时加载数据 // Tab 切换时加载数据
@@ -5,7 +5,7 @@ import type {
ModelListItem, ModelListItem,
} from '#/api/ai-platform/ai-platform'; } from '#/api/ai-platform/ai-platform';
import { onMounted, reactive, ref } from 'vue'; import { computed, onMounted, reactive, ref } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { Page } from '@vben/common-ui/es/page'; import { Page } from '@vben/common-ui/es/page';
@@ -35,6 +35,7 @@ import {
ElOption, ElOption,
ElPagination, ElPagination,
ElSelect, ElSelect,
ElSwitch,
ElTag, ElTag,
ElTooltip, ElTooltip,
} from 'element-plus'; } from 'element-plus';
@@ -47,10 +48,13 @@ import {
updateKnowledgeBaseApi, updateKnowledgeBaseApi,
} from '#/api/ai-platform/ai-platform'; } from '#/api/ai-platform/ai-platform';
import { ZqDialog } from '#/components/zq-dialog'; import { ZqDialog } from '#/components/zq-dialog';
import { useAppContextStore } from '#/store/app-context';
defineOptions({ name: 'KnowledgeBase' }); defineOptions({ name: 'KnowledgeBase' });
const router = useRouter(); const router = useRouter();
const appContextStore = useAppContextStore();
const isMainApp = computed(() => appContextStore.isMainApp);
// 表单 // 表单
const createForm = reactive<KnowledgeBaseCreateInput & { id?: string }>({ const createForm = reactive<KnowledgeBaseCreateInput & { id?: string }>({
@@ -104,6 +108,7 @@ async function fetchList() {
page: pagination.current, page: pagination.current,
pageSize: pagination.pageSize, pageSize: pagination.pageSize,
name: searchKeyword.value || undefined, name: searchKeyword.value || undefined,
applicationId: appContextStore.currentApp?.id,
}); });
kbList.value = res.items; kbList.value = res.items;
pagination.total = res.total; pagination.total = res.total;
@@ -176,6 +181,7 @@ async function handleConfirm() {
ElMessage.success($t('ai-platform.knowledge.saveSuccess')); ElMessage.success($t('ai-platform.knowledge.saveSuccess'));
} else { } else {
const res = await createKnowledgeBaseApi({ const res = await createKnowledgeBaseApi({
application_id: appContextStore.currentApp?.id,
is_global: createForm.is_global, is_global: createForm.is_global,
name: createForm.name, name: createForm.name,
code: createForm.code, code: createForm.code,
@@ -191,7 +197,11 @@ async function handleConfirm() {
indexing_technique: createForm.indexing_technique, indexing_technique: createForm.indexing_technique,
}); });
ElMessage.success($t('ai-platform.knowledge.createSuccess')); ElMessage.success($t('ai-platform.knowledge.createSuccess'));
router.push(`/ai-platform/knowledge-base/detail/${res.id}`); router.push(
appContextStore.getContextPath(
`/ai-platform/knowledge-base/detail/${res.id}`,
),
);
} }
dialogVisible.value = false; dialogVisible.value = false;
fetchList(); fetchList();
@@ -204,7 +214,11 @@ async function handleConfirm() {
} }
function handleDetail(item: KnowledgeBaseListItem) { function handleDetail(item: KnowledgeBaseListItem) {
router.push(`/ai-platform/knowledge-base/detail/${item.id}`); router.push(
appContextStore.getContextPath(
`/ai-platform/knowledge-base/detail/${item.id}`,
),
);
} }
async function handleDelete(item: KnowledgeBaseListItem) { async function handleDelete(item: KnowledgeBaseListItem) {
@@ -654,6 +668,22 @@ onMounted(() => {
</div> </div>
</template> </template>
<ElFormItem v-if="isMainApp">
<template #label>
<div class="flex items-center gap-1">
<span>{{ $t('ai-platform.knowledge.globalVisible') }}</span>
<ElTooltip
placement="top"
:content="$t('ai-platform.knowledge.globalVisibleTooltip')"
>
<CircleHelp
class="text-muted-foreground h-3.5 w-3.5 cursor-help"
/>
</ElTooltip>
</div>
</template>
<ElSwitch v-model="createForm.is_global" />
</ElFormItem>
</ElForm> </ElForm>
</ZqDialog> </ZqDialog>
</div> </div>
@@ -5,6 +5,7 @@ import type { VbenFormSchema } from '#/adapter/form';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { getWorkflowListApi } from '#/api/ai-platform/ai-platform'; import { getWorkflowListApi } from '#/api/ai-platform/ai-platform';
import { useAppContextStore } from '#/store/app-context';
type TagType = 'danger' | 'info' | 'primary' | 'success' | 'warning'; type TagType = 'danger' | 'info' | 'primary' | 'success' | 'warning';
@@ -51,6 +52,8 @@ export function formatTime(value?: string) {
} }
export function useSearchFormSchema(): VbenFormSchema[] { export function useSearchFormSchema(): VbenFormSchema[] {
const appContextStore = useAppContextStore();
return [ return [
{ {
component: 'ApiSelect', component: 'ApiSelect',
@@ -61,6 +64,7 @@ export function useSearchFormSchema(): VbenFormSchema[] {
const res = await getWorkflowListApi({ const res = await getWorkflowListApi({
page: 1, page: 1,
pageSize: 200, pageSize: 200,
applicationId: appContextStore.currentApp?.id,
}); });
return (res.items || []).map((item) => ({ return (res.items || []).map((item) => ({
label: item.name, label: item.name,
@@ -11,6 +11,7 @@ import { ElButton, ElTag } from 'element-plus';
import { getAllWorkflowRunsApi } from '#/api/ai-platform/ai-platform'; import { getAllWorkflowRunsApi } from '#/api/ai-platform/ai-platform';
import { useZqTable } from '#/components/zq-table'; import { useZqTable } from '#/components/zq-table';
import { useAppContextStore } from '#/store/app-context';
import RunStatusTag from './components/RunStatusTag.vue'; import RunStatusTag from './components/RunStatusTag.vue';
import { import {
@@ -28,6 +29,7 @@ defineOptions({ name: 'WorkflowRunHistory' });
const detailRef = ref<InstanceType<typeof DetailDialog>>(); const detailRef = ref<InstanceType<typeof DetailDialog>>();
const triggerOptions = getTriggerOptions(); const triggerOptions = getTriggerOptions();
const appContextStore = useAppContextStore();
const fetchRunList = async (params: any) => { const fetchRunList = async (params: any) => {
const res = await getAllWorkflowRunsApi({ const res = await getAllWorkflowRunsApi({
@@ -36,6 +38,7 @@ const fetchRunList = async (params: any) => {
workflowId: params.form?.workflowId || undefined, workflowId: params.form?.workflowId || undefined,
status: params.form?.status || undefined, status: params.form?.status || undefined,
triggerType: params.form?.triggerType || undefined, triggerType: params.form?.triggerType || undefined,
applicationId: appContextStore.currentApp?.id,
}); });
return { return {
items: res.items, items: res.items,
@@ -14,6 +14,7 @@ import { ElAlert, ElButton, ElTag } from 'element-plus';
import { getWorkflowDetailApi, getWorkflowRunDetailApi } from '#/api/ai-platform/ai-platform'; import { getWorkflowDetailApi, getWorkflowRunDetailApi } from '#/api/ai-platform/ai-platform';
import { ZqDesc, ZqDescItem } from '#/components/zq-desc'; import { ZqDesc, ZqDescItem } from '#/components/zq-desc';
import { ZqDialog } from '#/components/zq-dialog'; import { ZqDialog } from '#/components/zq-dialog';
import { useAppContextStore } from '#/store/app-context';
import { definitionToFlowElements } from '../../workflow/shared/loadDefinition'; import { definitionToFlowElements } from '../../workflow/shared/loadDefinition';
import { applyRunReplay } from '../../workflow/shared/useRunReplay'; import { applyRunReplay } from '../../workflow/shared/useRunReplay';
@@ -28,6 +29,7 @@ import {
} from '../data'; } from '../data';
const router = useRouter(); const router = useRouter();
const appContextStore = useAppContextStore();
const triggerOptions = getTriggerOptions(); const triggerOptions = getTriggerOptions();
const visible = ref(false); const visible = ref(false);
@@ -92,7 +94,11 @@ function handleOpen() {
function openEditor() { function openEditor() {
if (!run.value?.workflow_id) return; if (!run.value?.workflow_id) return;
visible.value = false; visible.value = false;
router.push(`/ai-platform/workflow/editor/${run.value.workflow_id}`); router.push(
appContextStore.getContextPath(
`/ai-platform/workflow/editor/${run.value.workflow_id}`,
),
);
} }
function triggerLabel(value?: string) { function triggerLabel(value?: string) {