发布 v1.10 #5
@@ -0,0 +1,75 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, watch } from 'vue';
|
||||
|
||||
import { AuthenticationLoginExpiredModal } from '@easyflow/common-ui';
|
||||
import { useWatermark } from '@easyflow/hooks';
|
||||
import { LockScreen } from '@easyflow/layouts';
|
||||
import { preferences } from '@easyflow/preferences';
|
||||
import { useAccessStore, useUserStore } from '@easyflow/stores';
|
||||
|
||||
import { useAuthStore } from '#/store';
|
||||
import LoginForm from '#/views/_core/authentication/login.vue';
|
||||
|
||||
const accessStore = useAccessStore();
|
||||
const userStore = useUserStore();
|
||||
const authStore = useAuthStore();
|
||||
const { destroyWatermark, updateWatermark } = useWatermark();
|
||||
|
||||
const avatar = computed(
|
||||
() => userStore.userInfo?.avatar ?? preferences.app.defaultAvatar,
|
||||
);
|
||||
|
||||
async function handleLogout() {
|
||||
await authStore.logout(false);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => ({
|
||||
enable: preferences.app.watermark,
|
||||
content: preferences.app.watermarkContent,
|
||||
}),
|
||||
async ({ enable, content }) => {
|
||||
if (enable) {
|
||||
await updateWatermark({
|
||||
content:
|
||||
content ||
|
||||
`${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
|
||||
});
|
||||
} else {
|
||||
destroyWatermark();
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="authenticated-standalone-layout">
|
||||
<slot></slot>
|
||||
<AuthenticationLoginExpiredModal
|
||||
v-model:open="accessStore.loginExpired"
|
||||
:avatar
|
||||
>
|
||||
<LoginForm />
|
||||
</AuthenticationLoginExpiredModal>
|
||||
<Transition v-if="preferences.widget.lockScreen" name="slide-up">
|
||||
<LockScreen
|
||||
v-if="accessStore.isLockScreen"
|
||||
:avatar
|
||||
@to-login="handleLogout"
|
||||
/>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.authenticated-standalone-layout {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
background-color: var(--el-bg-color);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,28 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import agentDesignRoutes from '../routes/external/agent-design';
|
||||
import agentRoutes from '../routes/modules/agent';
|
||||
|
||||
describe('agent design route', () => {
|
||||
it('keeps the standalone designer outside dynamic menu routes', () => {
|
||||
const designRoute = agentDesignRoutes.find(
|
||||
(route) => route.name === 'AgentDesigner',
|
||||
);
|
||||
|
||||
expect(agentRoutes.some((route) => route.name === 'AgentDesigner')).toBe(
|
||||
false,
|
||||
);
|
||||
expect(designRoute).toBeDefined();
|
||||
expect(designRoute?.path).toBe('/ai/agents/designer/:id');
|
||||
expect(designRoute?.meta).toMatchObject({
|
||||
activePath: '/ai/agents',
|
||||
hideInBreadcrumb: true,
|
||||
hideInMenu: true,
|
||||
hideInTab: true,
|
||||
noBasicLayout: true,
|
||||
openInNewWindow: true,
|
||||
});
|
||||
expect(designRoute?.meta?.ignoreAccess).not.toBe(true);
|
||||
expect(designRoute?.component).toBeTypeOf('function');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import workflowRoutes from '../routes/modules/workflow';
|
||||
|
||||
describe('workflow design route', () => {
|
||||
it('uses the authenticated standalone shell without exposing a menu or tab', () => {
|
||||
const designRoute = workflowRoutes.find(
|
||||
(route) => route.name === 'WorkflowDesign',
|
||||
);
|
||||
|
||||
expect(designRoute).toBeDefined();
|
||||
expect(designRoute?.path).toBe('/ai/workflow/design');
|
||||
expect(designRoute?.meta).toMatchObject({
|
||||
activePath: '/ai/workflow',
|
||||
hideInBreadcrumb: true,
|
||||
hideInMenu: true,
|
||||
hideInTab: true,
|
||||
noBasicLayout: true,
|
||||
});
|
||||
expect(designRoute?.meta?.ignoreAccess).not.toBe(true);
|
||||
expect(designRoute?.component).toBeTypeOf('function');
|
||||
});
|
||||
});
|
||||
23
easyflow-ui-admin/app/src/router/routes/external/agent-design.ts
vendored
Normal file
23
easyflow-ui-admin/app/src/router/routes/external/agent-design.ts
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
name: 'AgentDesigner',
|
||||
path: '/ai/agents/designer/:id',
|
||||
component: () =>
|
||||
import('#/views/ai/agents/AgentDesignerStandaloneView.vue'),
|
||||
meta: {
|
||||
title: $t('menus.ai.agents'),
|
||||
openInNewWindow: true,
|
||||
hideInMenu: true,
|
||||
hideInTab: true,
|
||||
hideInBreadcrumb: true,
|
||||
noBasicLayout: true,
|
||||
activePath: '/ai/agents',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
@@ -1,19 +1,6 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
import {$t} from '#/locales';
|
||||
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
name: 'AgentDesigner',
|
||||
path: '/ai/agents/designer/:id',
|
||||
component: () => import('#/views/ai/agents/AgentDesigner.vue'),
|
||||
meta: {
|
||||
title: $t('menus.ai.agents'),
|
||||
openInNewWindow: true,
|
||||
hideInMenu: true,
|
||||
activePath: '/ai/agents',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'AgentChat',
|
||||
path: '/ai/agent-chat',
|
||||
|
||||
@@ -8,11 +8,15 @@ const routes: RouteRecordRaw[] = [
|
||||
icon: 'ant-design:apartment-outlined',
|
||||
title: '工作流设计',
|
||||
hideInMenu: true,
|
||||
hideInTab: true,
|
||||
hideInBreadcrumb: true,
|
||||
noBasicLayout: true,
|
||||
activePath: '/ai/workflow',
|
||||
},
|
||||
name: 'WorkflowDesign',
|
||||
path: '/ai/workflow/design',
|
||||
component: () => import('#/views/ai/workflow/WorkflowDesign.vue'),
|
||||
component: () =>
|
||||
import('#/views/ai/workflow/WorkflowDesignStandaloneView.vue'),
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import AuthenticatedStandaloneLayout from '#/layouts/AuthenticatedStandaloneLayout.vue';
|
||||
|
||||
import AgentDesigner from './AgentDesigner.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AuthenticatedStandaloneLayout>
|
||||
<AgentDesigner />
|
||||
</AuthenticatedStandaloneLayout>
|
||||
</template>
|
||||
@@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import AuthenticatedStandaloneLayout from '#/layouts/AuthenticatedStandaloneLayout.vue';
|
||||
|
||||
import WorkflowDesign from './WorkflowDesign.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AuthenticatedStandaloneLayout class="workflow-design-standalone">
|
||||
<WorkflowDesign />
|
||||
</AuthenticatedStandaloneLayout>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.workflow-design-standalone :deep(.head-div) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.workflow-design-standalone :deep(.agentsflow) {
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
.workflow-design-standalone :deep(.tiny-flow-container) {
|
||||
flex: 1;
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user