feat: 画布解耦合
This commit is contained in:
100
src/components/LabCanvas/ItemList.vue
Normal file
100
src/components/LabCanvas/ItemList.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user