dify历史记录

This commit is contained in:
2026-02-25 18:21:25 +08:00
parent 0efca50be3
commit 2e93211697
13 changed files with 1328 additions and 74 deletions

View File

@@ -19,7 +19,6 @@
type="text"
placeholder="搜索智能体..."
class="search-input"
@keydown.enter="handleSearch"
/>
<transition name="fade">
<button v-if="searchKeyword" class="search-clear" @click="clearSearch">
@@ -34,7 +33,7 @@
<div class="category-scroll" ref="categoryScrollRef">
<div class="category-track">
<button
v-for="category in visibleCategories"
v-for="category in categories"
:key="category.id"
:data-category-id="category.id"
class="category-chip"
@@ -46,10 +45,54 @@
</button>
</div>
</div>
<!-- 展开按钮 -->
<button
ref="expandTriggerRef"
class="expand-trigger"
:class="{ 'expand-trigger--active': showCategoryPanel }"
@click="toggleCategoryPanel"
>
<span class="expand-icon">
<AppstoreOutlined />
</span>
</button>
</div>
</div>
</header>
<!-- 分类展开面板 - Teleport body -->
<Teleport to="body">
<!-- 遮罩层 -->
<transition name="mask">
<div v-if="showCategoryPanel" class="category-mask" @click="showCategoryPanel = false"></div>
</transition>
<!-- 面板 -->
<transition name="panel">
<div v-if="showCategoryPanel" class="category-panel" :style="panelStyle">
<div class="panel-header">
<span class="panel-title">快速选择</span>
<button class="panel-close" @click="showCategoryPanel = false">
<CloseOutlined />
</button>
</div>
<div class="panel-grid">
<button
v-for="category in categories"
:key="category.id"
class="panel-chip"
:class="{ 'panel-chip--active': activeCategory === category.id }"
@click="handleCategoryFromPanel(category.id)"
>
<span class="panel-chip-name">{{ category.name }}</span>
<span class="panel-chip-count">{{ category.count }}</span>
</button>
</div>
</div>
</transition>
</Teleport>
<!-- 主内容区域 -->
<main class="agents-main">
<a-spin :spinning="loading" class="loading-spinner">
@@ -128,7 +171,8 @@ import {
RobotOutlined,
CloseOutlined,
ArrowRightOutlined,
MessageOutlined
MessageOutlined,
AppstoreOutlined
} from '@ant-design/icons-vue'
import { message } from 'ant-design-vue'
import FullWidthLayout from '@/layouts/components/FullWidthLayout.vue'
@@ -142,6 +186,14 @@ const searchKeyword = ref('')
const chatDrawerVisible = ref(false)
const currentAgent = ref(null)
const categoryScrollRef = ref(null)
const expandTriggerRef = ref(null)
const showCategoryPanel = ref(false)
const panelTop = ref(0)
// 面板样式
const panelStyle = computed(() => ({
top: `${panelTop.value}px`
}))
// 智能体列表数据
const agentList = ref([])
@@ -166,9 +218,6 @@ const categories = computed(() => {
return cats
})
// 可见分类(用于展示)
const visibleCategories = computed(() => categories.value)
// 过滤后的列表
const filteredAgentList = computed(() => {
let list = agentList.value
@@ -242,8 +291,18 @@ const handleCategoryChange = (categoryId) => {
scrollCategoryIntoView(categoryId)
}
const handleSearch = () => {
// 搜索逻辑通过 computed 自动处理
const toggleCategoryPanel = () => {
if (!showCategoryPanel.value && expandTriggerRef.value) {
const rect = expandTriggerRef.value.getBoundingClientRect()
panelTop.value = rect.bottom + 8
}
showCategoryPanel.value = !showCategoryPanel.value
}
const handleCategoryFromPanel = (categoryId) => {
activeCategory.value = categoryId
scrollCategoryIntoView(categoryId)
showCategoryPanel.value = false
}
const clearSearch = () => {
@@ -334,6 +393,7 @@ onMounted(() => {
padding: 48px 0 32px;
background: var(--surface-primary);
border-bottom: 1px solid var(--border-light);
z-index: 10;
}
.header-decoration {
@@ -456,6 +516,9 @@ onMounted(() => {
.category-filter {
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
position: relative;
}
.category-scroll {
@@ -832,4 +895,206 @@ onMounted(() => {
.fade-leave-to {
opacity: 0;
}
// ============================================
// 展开按钮
// ============================================
.expand-trigger {
flex-shrink: 0;
width: 34px;
height: 34px;
border: 1px solid var(--border-subtle);
border-radius: 10px;
background: var(--surface-elevated);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-tertiary);
transition: all var(--transition-fast);
&:hover {
border-color: var(--text-tertiary);
color: var(--text-secondary);
background: var(--surface-muted);
}
&--active {
background: var(--text-primary);
border-color: var(--text-primary);
color: white;
}
}
.expand-icon {
font-size: 14px;
}
</style>
<!-- 全局样式 - 用于 Teleport body 的元素 -->
<style lang="less">
.category-panel {
position: fixed;
left: 50%;
transform: translateX(-50%);
width: 320px;
max-width: calc(100vw - 48px);
background: #FFFFFF;
border-radius: 14px;
box-shadow: 0 12px 32px rgba(0,0,0,0.12), 0 4px 12px rgba(0,0,0,0.08);
border: 1px solid #F0F0F0;
z-index: 1001;
overflow: hidden;
}
.category-panel .panel-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
border-bottom: 1px solid #F0F0F0;
}
.category-panel .panel-title {
font-size: 13px;
font-weight: 600;
color: #171717;
}
.category-panel .panel-close {
width: 24px;
height: 24px;
border: none;
background: transparent;
border-radius: 6px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: #A3A3A3;
font-size: 10px;
transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
}
.category-panel .panel-close:hover {
background: #F5F5F5;
color: #525252;
}
.category-panel .panel-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
padding: 12px;
max-height: 280px;
overflow-y: auto;
}
.category-panel .panel-grid::-webkit-scrollbar {
width: 4px;
}
.category-panel .panel-grid::-webkit-scrollbar-thumb {
background: #E5E5E5;
border-radius: 2px;
}
.category-panel .panel-chip {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 12px;
border: 1px solid #F0F0F0;
border-radius: 10px;
background: #FFFFFF;
cursor: pointer;
transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
}
.category-panel .panel-chip:hover:not(.panel-chip--active) {
border-color: #E5E5E5;
background: #F5F5F5;
}
.category-panel .panel-chip--active {
background: #171717;
border-color: #171717;
}
.category-panel .panel-chip--active .panel-chip-name,
.category-panel .panel-chip--active .panel-chip-count {
color: white;
}
.category-panel .panel-chip--active .panel-chip-count {
background: rgba(255,255,255,0.2);
}
.category-panel .panel-chip-name {
font-size: 13px;
font-weight: 500;
color: #171717;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.category-panel .panel-chip-count {
font-size: 11px;
font-weight: 600;
padding: 2px 6px;
border-radius: 6px;
background: #F5F5F5;
color: #525252;
flex-shrink: 0;
margin-left: 8px;
}
.category-mask {
position: fixed;
inset: 0;
z-index: 1000;
background: transparent;
}
// 面板动画 - 全局
.panel-enter-active {
animation: panelIn 0.2s ease-out;
}
.panel-leave-active {
animation: panelOut 0.15s ease-in;
}
@keyframes panelIn {
from {
opacity: 0;
transform: translateX(-50%) translateY(-8px) scale(0.96);
}
to {
opacity: 1;
transform: translateX(-50%) translateY(0) scale(1);
}
}
@keyframes panelOut {
from {
opacity: 1;
transform: translateX(-50%) translateY(0) scale(1);
}
to {
opacity: 0;
transform: translateX(-50%) translateY(-8px) scale(0.96);
}
}
.mask-enter-active,
.mask-leave-active {
transition: opacity 0.15s ease;
}
.mask-enter-from,
.mask-leave-to {
opacity: 0;
}
</style>

View File

@@ -292,7 +292,10 @@ const handleFileSelectWrapper = (e: Event) => {
}
onMounted(async () => {
await voiceStore.refresh()
await Promise.all([
voiceStore.refresh(),
userStore.fetchUserProfile()
])
})
</script>