|
|
@@ -1,17 +1,17 @@
|
|
|
<template>
|
|
|
<div class="window">
|
|
|
- <div class="title">
|
|
|
+ <p class="title">
|
|
|
<span class="title">发现<span class="title_2">智能体</span></span>
|
|
|
<div class="add_button" @click="createAgent">
|
|
|
<icon-plus />
|
|
|
创建我的智能体
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ </p>
|
|
|
<div class="select_btn_box">
|
|
|
<a-space size="medium">
|
|
|
<div
|
|
|
class="select_btn"
|
|
|
- v-for="category in categories"
|
|
|
+ v-for="category in ['全部', ...categories]"
|
|
|
:key="category"
|
|
|
:class="{ selected: selectedCategory === category }"
|
|
|
@click="selectCategory(category)"
|
|
|
@@ -21,10 +21,10 @@
|
|
|
</a-space>
|
|
|
</div>
|
|
|
<div class="agents-list-container" ref="agentsListContainer">
|
|
|
- <div class="agent-card" v-for="app in filter_agent_list" :key="app.id" @click="handleAgentClick(app)">
|
|
|
- <Icon :name="app.mode" alt="icon" class="agent-icon" />
|
|
|
+ <div class="agent-card" v-for="app in filteredApps" :key="app.app.id" @click="handleAgentClick(app)">
|
|
|
+ <Icon :name="app.category" alt="icon" class="agent-icon" />
|
|
|
<div class="agent-info">
|
|
|
- <div class="agent-title">{{ app.name }}</div>
|
|
|
+ <div class="agent-title">{{ app.app.name }}</div>
|
|
|
<div class="agent-subtitle">{{ app.description }}</div>
|
|
|
</div>
|
|
|
<a-button shape="circle" class="arrow-button">
|
|
|
@@ -36,7 +36,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, onMounted, nextTick, onUnmounted } from 'vue';
|
|
|
+import { ref, onMounted, nextTick, onUnmounted, computed } from 'vue';
|
|
|
import { fetchApps, fetchAppInfo } from '../../apis/home';
|
|
|
import { Message } from '../../3rd-libs/arco-vue-libs';
|
|
|
import Icon from '../../components/Icon/Icon.vue';
|
|
|
@@ -44,75 +44,26 @@ import { getConfigField } from '../../config'; // 假设 getConfigField 是从
|
|
|
import { access_token } from '../../base/storage';
|
|
|
|
|
|
const categories = ref([]);
|
|
|
-const agent_list = ref([]);
|
|
|
-const filter_agent_list = ref([]);
|
|
|
+const recommendedApps = ref([]);
|
|
|
const agentsListContainer = ref(null);
|
|
|
-const selectedCategory = ref('全部'); // 默认选中“全部”
|
|
|
const baseurl = getConfigField('API_BASE_URL');
|
|
|
-const page = ref(1); // 当前页码
|
|
|
-const limit = 30; // 每页显示的数量
|
|
|
+const selectedCategory = ref('全部'); // 默认选中“全部”
|
|
|
|
|
|
const loadApps = async () => {
|
|
|
try {
|
|
|
- const params = {
|
|
|
- page: page.value,
|
|
|
- limit,
|
|
|
- name: ''
|
|
|
- };
|
|
|
- const response = await fetchApps(params);
|
|
|
- agent_list.value = [...agent_list.value, ...response.data]; // 追加新数据
|
|
|
-
|
|
|
- // 遍历 mode 并去重
|
|
|
- const modes = new Set(agent_list.value.map((app) => app.mode));
|
|
|
- const modeMap = {
|
|
|
- workflow: '工作流',
|
|
|
- 'advanced-chat': '高级助手',
|
|
|
- chat: '聊天助手',
|
|
|
- 'agent-chat': 'Agent'
|
|
|
- };
|
|
|
- categories.value = ['全部', ...Array.from(modes).map((mode) => modeMap[mode] || mode)];
|
|
|
-
|
|
|
- filterApps(); // 加载应用后进行初次筛选
|
|
|
+ const response = await fetchApps();
|
|
|
+ recommendedApps.value = response.recommended_apps || [];
|
|
|
+ categories.value = response.categories || [];
|
|
|
} catch (error) {
|
|
|
Message.error('加载数据失败');
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-const loadMoreApps = async () => {
|
|
|
- page.value += 1; // 增加页码
|
|
|
- await loadApps(); // 加载更多数据
|
|
|
-};
|
|
|
-
|
|
|
-const filterApps = () => {
|
|
|
- if (selectedCategory.value === '全部') {
|
|
|
- filter_agent_list.value = agent_list.value;
|
|
|
- } else {
|
|
|
- const modeMap = {
|
|
|
- 工作流: 'workflow',
|
|
|
- 高级聊天: 'advanced-chat',
|
|
|
- 聊天助手: 'chat',
|
|
|
- Agent: 'agent-chat'
|
|
|
- };
|
|
|
- const selectedMode = modeMap[selectedCategory.value] || selectedCategory.value;
|
|
|
- filter_agent_list.value = agent_list.value.filter((app) => app.mode === selectedMode);
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
const adjustContainerHeight = () => {
|
|
|
if (agentsListContainer.value) {
|
|
|
const windowHeight = window.innerHeight;
|
|
|
const containerTop = agentsListContainer.value.getBoundingClientRect().top;
|
|
|
- agentsListContainer.value.style.height = `${windowHeight - containerTop - 80}px`; // 20px 是底部的间距
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-const handleScroll = () => {
|
|
|
- if (agentsListContainer.value) {
|
|
|
- const { scrollTop, scrollHeight, clientHeight } = agentsListContainer.value;
|
|
|
- if (scrollTop + clientHeight >= scrollHeight - 10) {
|
|
|
- // 距离底部10px时加载更多
|
|
|
- loadMoreApps();
|
|
|
- }
|
|
|
+ agentsListContainer.value.style.height = `${windowHeight - containerTop - 20}px`; // 20px 是底部的间距
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -120,40 +71,31 @@ onMounted(() => {
|
|
|
loadApps();
|
|
|
nextTick(adjustContainerHeight);
|
|
|
window.addEventListener('resize', adjustContainerHeight);
|
|
|
- agentsListContainer.value.addEventListener('scroll', handleScroll);
|
|
|
});
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
window.removeEventListener('resize', adjustContainerHeight);
|
|
|
- if (agentsListContainer.value) {
|
|
|
- agentsListContainer.value.removeEventListener('scroll', handleScroll);
|
|
|
- }
|
|
|
});
|
|
|
|
|
|
const createAgent = () => {
|
|
|
- const dify_token = access_token.get();
|
|
|
- const workflowUrl = `${baseurl}/apps?token=${dify_token}`;
|
|
|
- window.open(workflowUrl, '_blank');
|
|
|
+
|
|
|
};
|
|
|
|
|
|
+const filteredApps = computed(() => {
|
|
|
+ if (selectedCategory.value === '全部') {
|
|
|
+ return recommendedApps.value;
|
|
|
+ }
|
|
|
+ return recommendedApps.value.filter(app => app.category === selectedCategory.value);
|
|
|
+});
|
|
|
+
|
|
|
const selectCategory = (category) => {
|
|
|
selectedCategory.value = category;
|
|
|
- filterApps(); // 选择分类后进行筛选
|
|
|
};
|
|
|
|
|
|
const handleAgentClick = async (app) => {
|
|
|
- const appId = app.id;
|
|
|
- try {
|
|
|
- const { site } = await fetchAppInfo(appId);
|
|
|
- const dify_token = access_token.get();
|
|
|
- const chatUrl = `${baseurl}/chat/${site.code}?token=${dify_token}`;
|
|
|
- window.open(chatUrl, '_blank');
|
|
|
- } catch (error) {
|
|
|
- Message.error('加载数据失败');
|
|
|
- }
|
|
|
+ // TODO 弹窗
|
|
|
};
|
|
|
</script>
|
|
|
-
|
|
|
<style scoped>
|
|
|
.window {
|
|
|
width: 90%;
|