type CardType = 'text' | 'image' | 'image-text' | 'portfolio' | 'project' interface CardData { id: number type: CardType image?: string images?: string[] title: string description?: string tags?: string[] aspectRatio: number } const images = [ 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=600&h=800&fit=crop', 'https://images.unsplash.com/photo-1469474968028-56623f02e42e?w=600&h=500&fit=crop', 'https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=600&h=700&fit=crop', 'https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=600&h=900&fit=crop', 'https://images.unsplash.com/photo-1447752875215-b2761acb3c5d?w=600&h=550&fit=crop', 'https://images.unsplash.com/photo-1433086966358-54859d0ed716?w=600&h=650&fit=crop', 'https://images.unsplash.com/photo-1501854140801-50d01698950b?w=600&h=750&fit=crop', 'https://images.unsplash.com/photo-1518173946687-a1e4e09e68b7?w=600&h=850&fit=crop', 'https://images.unsplash.com/photo-1472214103451-9374bd1c798e?w=600&h=580&fit=crop', 'https://images.unsplash.com/photo-1505144808419-1957a94ca61e?w=600&h=720&fit=crop', 'https://images.unsplash.com/photo-1465146344425-f00d5f5c8f07?w=600&h=600&fit=crop', 'https://images.unsplash.com/photo-1490730141103-6cac27aaab94?w=600&h=780&fit=crop', ] const portfolioSets = [ [images[0], images[1], images[4], images[5]], [images[2], images[3], images[6], images[7]], [images[8], images[9], images[10], images[11]], [images[1], images[3], images[8], images[10]], [images[0], images[2], images[6], images[9]], [images[4], images[5], images[7], images[11]], ] const titles = [ '山间晨雾', '海岸余晖', '林深见鹿', '星河滚烫', '秋日私语', '雪落无声', '荒漠孤烟', '江南水乡', '云海翻涌', '古城旧梦', '樱花吹雪', '冰川纪行', ] const descriptions = [ '清晨第一缕阳光穿透薄雾,洒在青翠的山谷间', '海浪轻抚沙滩,余晖将天际染成珊瑚般的暖色调', '密林深处,光影斑驳,万物有灵且美', '浩瀚星空下,篝火噼啪作响,银河横亘天际', '金黄落叶铺满小径,秋风携来果实成熟的芬芳', '白雪覆盖大地,万物归于宁静与纯粹', '大漠无垠,风沙雕刻出千年的纹路', '小桥流水,烟雨朦胧中白墙黛瓦静静伫立', '云层如海浪般在山巅之下翻涌奔腾', '石板路上回响着千年前的足音,城墙斑驳诉说时光', '粉色花瓣如雪般飘落,短暂而绚烂', '万年冰层透着幽蓝的光,仿佛地球深处的呼吸', ] const textContents = [ { title: '关于灵感', description: '灵感从不拜访懒人。它像一只羞怯的鸟,只在你俯首案前、笔耕不辍时,悄然落在你的肩头。那些看似神来之笔的瞬间,不过是千百次练习后的水到渠成。' }, { title: '时间之河', description: '时间是一条沉默的河流,我们都是河中的石头。水流磨平了棱角,却让内里的纹理愈发清晰。每一道纹路都是一个故事,每一次冲刷都是一次重生。' }, { title: '留白之美', description: '最好的设计往往藏在未说之处。一幅山水画的力量不在墨色,而在无墨之间的云雾与山岚。界面如此,人生亦然——空间不是空缺,是呼吸的节拍。' }, { title: '光与影', description: '没有阴影,光便失去了意义。创作的本质不是消除黑暗,而是让明暗相互成全。一幅好的摄影作品,阴影里藏着比亮处更多的故事。' }, { title: '旅途随笔', description: '旅行不是从一个地方到另一个地方,而是从一种视角切换到另一种视角。同一轮明月,在不同的经度里,会映出不同的乡愁。' }, { title: '匠人之心', description: '一把好刀,从锻打到淬火,每一步都不可省略。手艺人知道:没有什么捷径能绕过时间的沉淀。代码与器物,皆是时间的艺术。' }, { title: '草木人间', description: '每一片叶子都是一个小小的宇宙——脉络如河流,叶肉如大地。阳光穿过叶片的时候,像是神在端详自己的指纹。我们不过是这绿色剧场里的观众。' }, { title: '夜的独白', description: '凌晨三点,城市终于安静下来。窗外零星灯火像是失眠者彼此交换的暗号。在这万籁俱寂的时刻,思绪反而最清晰——或许黑夜才是思考者真正的白昼。' }, ] const projectTags = [ ['Vue', 'TypeScript', 'Nuxt'], ['React', 'Next.js', 'Tailwind'], ['Node.js', 'PostgreSQL', 'Docker'], ['Figma', 'Design System', 'UI/UX'], ['Python', 'FastAPI', 'MongoDB'], ['Swift', 'SwiftUI', 'iOS'], ['Go', 'gRPC', 'Kubernetes'], ['Rust', 'WebAssembly', 'Canvas'], ['Three.js', 'WebGL', 'GLSL'], ['Svelte', 'SvelteKit', 'Prisma'], ['Astro', 'MDX', 'Contentful'], ['Flutter', 'Dart', 'Firebase'], ] const typeCycle: CardType[] = [ 'image-text', 'text', 'image-text', 'portfolio', 'image-text', 'image', 'image-text', 'project', 'text', 'image-text', 'portfolio', 'image-text', 'image', 'image-text', 'project', 'text', 'image-text', 'portfolio', 'image-text', 'image', ] function genItem(globalIdx: number): CardData { const type = typeCycle[globalIdx % typeCycle.length] const idx = globalIdx % 12 const base: Pick = { id: globalIdx + 1, type, title: titles[idx], aspectRatio: 0.6 + Math.random() * 0.7, } switch (type) { case 'text': { const tc = textContents[globalIdx % textContents.length] return { ...base, title: tc.title, description: tc.description, aspectRatio: 0.6 + Math.random() * 0.5 } } case 'image': return { ...base, image: images[idx], description: undefined, aspectRatio: 0.65 + Math.random() * 0.55 } case 'image-text': return { ...base, image: images[idx], description: descriptions[idx], aspectRatio: 0.6 + Math.random() * 0.7 } case 'portfolio': { const set = portfolioSets[globalIdx % portfolioSets.length] const ar = 0.85 + Math.random() * 0.15 return { ...base, images: set, description: descriptions[idx], aspectRatio: ar } } case 'project': return { ...base, image: images[idx], description: descriptions[idx], tags: projectTags[globalIdx % projectTags.length], aspectRatio: 0.65 + Math.random() * 0.45, } } } export default defineEventHandler(async (event) => { const query = getQuery(event) const page = Math.max(1, parseInt(String(query.page || '1'))) const pageSize = Math.min(30, Math.max(1, parseInt(String(query.pageSize || '12')))) const start = (page - 1) * pageSize const items = Array.from({ length: pageSize }, (_, i) => genItem(start + i)) const total = 60 const hasMore = start + pageSize < total return { items, hasMore, page } })