feat: 提交校园智能助手前端工程源码(Vue3 + Vite + view-ui-plus)
build / build (push) Successful in 3m14s

This commit is contained in:
lijing
2026-10-12 14:45:00 +08:00
parent 96bb37c89f
commit 37d292c5d2
51 changed files with 13430 additions and 2 deletions
+103
View File
@@ -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>