You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

154 lines
4.2 KiB

/**
* 卡片类型注册表
*
* 新增卡片类型只需在此文件中:
* 1. 在 CardTypes 数组中添加类型值
* 2. 在 cardTypeRegistry 中添加对应配置
* 3. 创建对应的瀑布流渲染组件
*
* 无需修改 index.vue / CardFormModal / CardDetailModal。
*/
import type { Component } from 'vue'
import WaterfallCard from '~/components/index/WaterfallCard.vue'
import WaterfallTextCard from '~/components/index/WaterfallTextCard.vue'
import WaterfallImageCard from '~/components/index/WaterfallImageCard.vue'
import WaterfallPortfolioCard from '~/components/index/WaterfallPortfolioCard.vue'
import WaterfallProjectCard from '~/components/index/WaterfallProjectCard.vue'
// ============ CardType 常量(与 drizzle schema 保持同步) ============
export const CardTypes = [
'text',
'image',
'image-text',
'portfolio',
'project',
] as const
export type CardType = (typeof CardTypes)[number]
// ============ 前端卡片数据接口 ============
export interface CardItemLike {
id?: number
type: CardType
image?: string
images?: string[]
title: string
description?: string
tags?: string[]
aspectRatio: number
categoryId?: string | null
createdAt?: string
}
// ============ 类型配置 ============
export interface CardTypeFormFields {
/** 是否需要单张图片 */
image?: boolean
/** 是否需要多张图片 */
multiImage?: boolean
/** 是否需要描述字段 */
description?: boolean
/** 是否需要标签选择 */
tags?: boolean
}
export interface CardTypeConfig {
/** 瀑布流渲染组件 */
component: Component
/** 中文标签 */
label: string
/** 图标名 (lucide:xxx) */
icon: string
/** 表单字段需求 */
formFields: CardTypeFormFields
/** 从 CardItemLike 提取该组件需要的 props */
mapProps: (item: CardItemLike) => Record<string, unknown>
}
// ============ 注册表 ============
export const cardTypeRegistry: Record<CardType, CardTypeConfig> = {
text: {
component: WaterfallTextCard,
label: '纯文本',
icon: 'lucide:text',
formFields: { description: true },
mapProps: (item) => ({
title: item.title,
description: item.description ?? '',
aspectRatio: item.aspectRatio,
}),
},
image: {
component: WaterfallImageCard,
label: '图片',
icon: 'lucide:image',
formFields: { image: true },
mapProps: (item) => ({
image: item.image ?? '',
title: item.title,
aspectRatio: item.aspectRatio,
}),
},
'image-text': {
component: WaterfallCard,
label: '图文',
icon: 'lucide:image-plus',
formFields: { image: true, description: true },
mapProps: (item) => ({
image: item.image ?? '',
title: item.title,
description: item.description ?? '',
aspectRatio: item.aspectRatio,
}),
},
portfolio: {
component: WaterfallPortfolioCard,
label: '图集',
icon: 'lucide:layout-grid',
formFields: { multiImage: true, description: true },
mapProps: (item) => ({
images: item.images ?? [],
title: item.title,
description: item.description ?? '',
aspectRatio: item.aspectRatio,
}),
},
project: {
component: WaterfallProjectCard,
label: '项目',
icon: 'lucide:folder-kanban',
formFields: { image: true, description: true, tags: true },
mapProps: (item) => ({
image: item.image ?? '',
title: item.title,
description: item.description ?? '',
tags: item.tags ?? [],
aspectRatio: item.aspectRatio,
}),
},
}
// ============ 派生数据(供表单等使用) ============
/** 类型选项列表(用于表单类型选择器) */
export const CARD_TYPE_OPTIONS = CardTypes.map((t) => ({
value: t,
label: cardTypeRegistry[t].label,
icon: cardTypeRegistry[t].icon,
}))
/** 获取类型中文标签 */
export function getTypeLabel(type: CardType | string): string {
return cardTypeRegistry[type as CardType]?.label ?? type
}
/**
* 运行时校验:确保注册表覆盖了所有 CardType
* 如果注册表缺少某个类型,会在控制台警告
*/
if (import.meta.dev) {
for (const t of CardTypes) {
if (!cardTypeRegistry[t]) {
console.warn(`[cardTypes] 注册表缺少类型 "${t}" 的配置`)
}
}
}