This commit is contained in:
+103
@@ -0,0 +1,103 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* 应用外壳:左侧导航栏 + 右侧内容区。
|
||||
*
|
||||
* 职责划分:
|
||||
* - 这里统一拉取「当前用户」和「能力目录」(两者都是全局信息,侧栏和对话页都要用),
|
||||
* 再往下传,避免各组件各拉一次。
|
||||
* - 会话状态留在对话页内部,侧栏的「新建对话」通过组件 ref 调用它的方法触发。
|
||||
*/
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { Message } from "view-ui-plus";
|
||||
import { agentApi, baseApi } from "@/api/module/agent";
|
||||
import AppSidebar from "@/components/AppSidebar.vue";
|
||||
import type { AgentToolCatalog, UserInfoVo } from "@/types/agent";
|
||||
|
||||
const user = ref<UserInfoVo | null>(null);
|
||||
const catalog = ref<AgentToolCatalog | null>(null);
|
||||
const loading = ref(true);
|
||||
const online = ref(false);
|
||||
/** 对话页组件实例(用于从侧栏触发"新建对话") */
|
||||
const chatRef = ref<any>(null);
|
||||
|
||||
const statusText = computed(() => (loading.value ? "正在连接" : online.value ? "服务已连接" : "服务未连接"));
|
||||
|
||||
const toolCount = computed(() => catalog.value?.total ?? 0);
|
||||
const writeCount = computed(() => {
|
||||
const mods = catalog.value?.modules || {};
|
||||
let n = 0;
|
||||
Object.keys(mods).forEach(k => mods[k].forEach(t => t.write && n++));
|
||||
return n;
|
||||
});
|
||||
|
||||
/**
|
||||
* 拉取当前登录用户(平台 CAS 会话)。
|
||||
*
|
||||
* 未登录时后端 CasSsoApiRedirectFilter 返回 code=302 + urlToRedirectTo,
|
||||
* alova 的 errorHandle 收到 302 会直接整页跳转登录页,并返回一个**永不 resolve** 的 Promise,
|
||||
* 所以跳转链路根本不会落到下面的 catch —— 这里只需处理「服务没起来 / 接口报错」。
|
||||
*/
|
||||
async function loadUser() {
|
||||
try {
|
||||
const info = await baseApi.getUserInfo().send();
|
||||
user.value = (info || null) as UserInfoVo;
|
||||
online.value = true;
|
||||
} catch (e: any) {
|
||||
console.warn("载入用户信息失败", e);
|
||||
Message.error("载入用户信息失败,请检查智能体服务是否已启动");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** 能力目录:拉取失败不阻塞对话(只是侧栏模块列表为空) */
|
||||
async function loadCatalog() {
|
||||
try {
|
||||
catalog.value = (await agentApi.tools().send()) as AgentToolCatalog;
|
||||
online.value = true;
|
||||
} catch (e) {
|
||||
console.warn("能力目录拉取失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadUser();
|
||||
loadCatalog();
|
||||
});
|
||||
|
||||
function onNewSession() {
|
||||
chatRef.value?.newSession?.();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="agent-shell">
|
||||
<AppSidebar :catalog="catalog" :user="user" @pick="t => chatRef?.pickSample?.(t)" @new-session="onNewSession" />
|
||||
|
||||
<section class="agent-main">
|
||||
<header class="agent-topbar">
|
||||
<div class="agent-topbar__titles">
|
||||
<h1>智能对话</h1>
|
||||
<p>说出需求,我直接调用系统接口帮你办</p>
|
||||
</div>
|
||||
<div class="agent-topbar__right">
|
||||
<span class="agent-status" :class="{ 'agent-status--off': !loading && !online }">
|
||||
<i class="agent-status__dot" />
|
||||
{{ statusText }}
|
||||
</span>
|
||||
<span v-if="toolCount" class="agent-capsule">
|
||||
可用能力 <strong class="agent-num">{{ toolCount }}</strong> 项
|
||||
<i class="agent-capsule__sep" />
|
||||
可直接提交 <strong class="agent-num">{{ writeCount }}</strong> 项
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="agent-body">
|
||||
<router-view v-slot="{ Component }">
|
||||
<component :is="Component" ref="chatRef" :user="user" :catalog="catalog" />
|
||||
</router-view>
|
||||
</main>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user