This repository has been archived on 2025-10-29. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
FPGA_WebLab/src/components/LabCanvas/ItemList.vue
2025-07-12 21:55:58 +08:00

101 lines
3.0 KiB
Vue

<template>
<div class="px-6 py-4 overflow-auto flex-1">
<div v-if="filteredItems.length > 0" class="grid grid-cols-2 gap-4">
<div
v-for="(item, index) in filteredItems"
:key="index"
class="card bg-base-200 hover:bg-base-300 transition-all duration-300 hover:shadow-md cursor-pointer"
@click="handleItemClick(item)"
>
<div class="card-body p-3 items-center text-center">
<div
class="bg-base-100 rounded-lg w-full h-[90px] flex items-center justify-center overflow-hidden p-2"
>
<!-- 组件预览 -->
<component
v-if="item.type && componentModules[item.type]"
:is="componentModules[item.type].default"
class="component-preview"
:size="getPreviewSize(item.type)"
/>
<!-- 模板预览 -->
<img
v-else-if="item.thumbnailUrl"
:src="item.thumbnailUrl || '/placeholder-template.png'"
alt="Template thumbnail"
class="max-h-full max-w-full object-contain"
/>
<!-- 加载中状态 -->
<span v-else class="text-xs text-gray-400">加载中...</span>
</div>
<h3 class="card-title text-sm mt-2">{{ item.name }}</h3>
<p class="text-xs opacity-70">
{{ item.description || item.type || getItemSubtitle(item) }}
</p>
</div>
</div>
</div>
<!-- 无搜索结果 -->
<div v-else class="py-16 text-center">
<SearchX :size="48" class="mx-auto text-base-300 mb-3" />
<p class="text-base-content opacity-70">{{ noResultsMessage }}</p>
<button class="btn btn-sm btn-ghost mt-3" @click="$emit('clear-search')">
清除搜索
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { computed } from "vue";
import { SearchX } from "lucide-vue-next";
import { getPreviewSize } from "./index.ts";
interface Props {
items: any[];
searchQuery: string;
componentModules: Record<string, any>;
noResultsMessage: string;
itemType: "component" | "template" | "virtual";
}
const props = defineProps<Props>();
const emit = defineEmits(["item-click", "clear-search"]);
// 过滤后的项目列表
const filteredItems = computed(() => {
if (!props.searchQuery) {
return props.items;
}
const query = props.searchQuery.toLowerCase();
return props.items.filter(
(item) =>
item.name.toLowerCase().includes(query) ||
(item.type && item.type.toLowerCase().includes(query)) ||
(item.description && item.description.toLowerCase().includes(query)),
);
});
// 获取项目副标题
function getItemSubtitle(item: any): string {
if (props.itemType === "template") {
return "模板";
}
return item.type || "";
}
// 处理项目点击
function handleItemClick(item: any) {
emit("item-click", item);
}
</script>
<style scoped>
.component-preview {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
</style>