Vue 前端项目实战:为 Z-Image-Turbo-rinaiqiao-huiyewunv 构建一个社区风格的 Prompt 分享平台

张开发
2026/4/19 3:20:37 15 分钟阅读

分享文章

Vue 前端项目实战:为 Z-Image-Turbo-rinaiqiao-huiyewunv 构建一个社区风格的 Prompt 分享平台
Vue 前端项目实战为 Z-Image-Turbo-rinaiqiao-huiyewunv 构建一个社区风格的 Prompt 分享平台1. 项目缘起当 AI 绘画遇上社区分享不知道你有没有这样的经历看到别人用 AI 画出了惊艳的作品自己也想试试但对着输入框憋了半天只写出“一个女孩很美风景高清”这种干巴巴的描述结果生成的图片总是不尽如人意。好的 AI 绘画一半靠模型另一半就靠那个神奇的“咒语”——Prompt。Z-Image-Turbo-rinaiqiao-huiyewunv我们后面简称 Z-Image-Turbo是一个在特定风格上表现很出色的图像生成模型。但它的潜力需要高质量的 Prompt 来激发。于是一个想法诞生了为什么不搭建一个社区让大家能自由地分享、发现和讨论那些能让 Z-Image-Turbo “大放异彩”的 Prompt 呢这就是我们今天要聊的项目一个用 Vue 构建的、社区风格的 Prompt 分享平台。它不仅仅是一个展示列表更是一个能让创作者获得反馈、让新手快速上手的互动空间。我们将一起探讨如何用前端技术为这些复杂的文本“配方”和它们背后的精彩作品设计一个既美观又实用的家。2. 核心功能规划不止于展示在动手写代码之前我们先想清楚这个平台要解决什么问题。一个成功的社区产品功能设计必须围绕用户的核心行为展开。2.1 用户的核心旅程想象一下用户如何使用这个平台探索者他可能刚接触 Z-Image-Turbo想找找灵感。他会浏览热门、最新的 Prompt看看别人生成了什么好看的图。实践者他找到了一个喜欢的 Prompt 和效果图想立刻试试。他需要一键复制这个复杂的 Prompt包括里面可能的各种参数和权重。贡献者他用 Z-Image-Turbo 生成了一张满意的图片并精心调试了 Prompt。他愿意分享出来并希望得到点赞、收藏甚至和其他人讨论如何优化。搜寻者他有明确的需求比如想画“赛博朋克风格的猫”他需要通过标签或搜索快速定位到相关资源。2.2 功能模块拆解基于以上旅程我们规划出几个核心功能模块Prompt 卡片这是内容的原子单元。每张卡片需要清晰展示Prompt 文本、生成的示例图片、作者、发布时间、标签、点赞数、收藏数。发布与编辑提供一个友好的表单让用户能输入 Prompt、上传生成的效果图、选择标签、添加描述。重点是让输入长文本和参数变得轻松。发现与筛选包括首页信息流最新、最热、按标签分类浏览、以及一个强大的搜索功能支持对 Prompt 文本、标签、描述的模糊搜索。互动机制点赞、收藏、评论。这些是社区活力的来源。用户中心个人主页展示自己发布的、点赞过的、收藏过的 Prompt。其中如何优雅地展示和操作复杂的 Prompt 文本是我们前端设计面临的最大挑战也是本项目 UI 设计的亮点所在。3. 技术选型与项目初始化我们选择 Vue 3 生态系统因为它灵活、高效并且拥有丰富的社区资源非常适合构建这类交互复杂的单页面应用。3.1 技术栈清单框架Vue 3 (Composition API) - 提供更好的逻辑组织和类型推断。构建工具Vite - 极速的启动和热更新提升开发体验。UI 组件库Element Plus - 提供大量成熟、美观的桌面端组件能快速搭建基础框架。当然你也可以选择 Naive UI、Ant Design Vue 等。状态管理Pinia - Vue 官方推荐的状态管理库比 Vuex 更简洁直观。路由Vue Router 4 - 管理页面导航。HTTP 客户端Axios - 处理与后端 API 的通信。图标Iconify 或 Element Plus 自带图标 - 提供丰富的图标资源。代码高亮Prism.js 或 highlight.js - 用于高亮显示 Prompt 中的关键参数如权重(word:1.5)。3.2 快速初始化项目打开终端让我们快速创建一个项目骨架# 使用 Vite 官方模板创建 Vue 项目 npm create vuelatest prompt-share-platform # 按照提示选择需要的功能TypeScript, Vue Router, Pinia, ESLint # 进入项目目录并安装依赖 cd prompt-share-platform npm install # 安装额外的核心依赖 npm install element-plus axios element-plus/icons-vue npm install prismjs --save-dev # 用于代码高亮 # 启动开发服务器 npm run dev3.3 基础配置在main.ts中引入 Element Plus 和样式import { createApp } from vue import { createPinia } from pinia import ElementPlus from element-plus import element-plus/dist/index.css import * as ElementPlusIconsVue from element-plus/icons-vue import App from ./App.vue import router from ./router const app createApp(App) app.use(createPinia()) app.use(router) app.use(ElementPlus) // 注册所有 Element Plus 图标 for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.mount(#app)4. UI/UX 设计攻坚让 Prompt 自己会说话这是本项目前端部分最有趣也最具挑战的一环。一个干巴巴的文本框里塞满上百个单词和括号对用户来说是灾难。我们的目标是让 Prompt 变得“可读”、“可操作”。4.1 Prompt 卡片设计我们设计一个PromptCard.vue组件。它的核心是清晰的信息层次。!-- components/PromptCard.vue -- template div classprompt-card !-- 顶部图片展示 -- div classimage-container el-image :srccardData.imageUrl fitcover :preview-src-list[cardData.imageUrl] hide-on-click-modal template #error div classimage-error图片加载失败/div /template /el-image div classimage-overlay el-button sizesmall typeprimary :iconCopyDocument click.stopcopyPrompt 复制咒语/el-button /div /div !-- 中部Prompt内容与交互 -- div classcard-body h4 classprompt-title{{ cardData.title }}/h4 !-- 可展开/收起的Prompt文本区域 -- div classprompt-content-wrapper pre refpromptTextRef classprompt-text :class{ collapsed: isCollapsed !isExpanded } {{ cardData.promptText }}/pre el-button v-ifshouldShowToggle link typeprimary clicktoggleExpand {{ isExpanded ? 收起 : 展开更多 }} /el-button /div !-- 标签云 -- div classtag-list el-tag v-fortag in cardData.tags :keytag sizesmall typeinfo click$emit(tag-click, tag) {{ tag }} /el-tag /div !-- 底部元数据与互动 -- div classcard-footer div classauthor-info el-avatar :size24 :srccardData.author.avatar / span classauthor-name{{ cardData.author.name }}/span span classpublish-time{{ formatTime(cardData.publishTime) }}/span /div div classaction-buttons el-button :iconThumb :typecardData.isLiked ? primary : default text clickhandleLike {{ cardData.likeCount }} /el-button el-button :iconStar :typecardData.isFavorited ? warning : default text clickhandleFavorite {{ cardData.favoriteCount }} /el-button el-button :iconChatDotRound text clickhandleComment {{ cardData.commentCount }} /el-button /div /div /div /div /template script setup langts import { ref, computed } from vue import { CopyDocument, Thumb, Star, ChatDotRound } from element-plus/icons-vue import { ElMessage } from element-plus import type { PromptCardData } from /types interface Props { cardData: PromptCardData } const props definePropsProps() const isExpanded ref(false) const promptTextRef refHTMLElement() // 计算是否需要显示“展开/收起”按钮 const shouldShowToggle computed(() { if (!promptTextRef.value) return false // 简单通过行数判断实际可根据高度计算 const lineCount props.cardData.promptText.split(\n).length return lineCount 4 // 超过4行则显示 }) const isCollapsed computed(() !isExpanded.value) const toggleExpand () { isExpanded.value !isExpanded.value } const copyPrompt async () { try { await navigator.clipboard.writeText(props.cardData.promptText) ElMessage.success(Prompt 已复制到剪贴板) } catch (err) { ElMessage.error(复制失败请手动选择文本复制) } } const handleLike () { // 调用 Pinia action 或 API console.log(like) } const handleFavorite () { console.log(favorite) } const handleComment () { console.log(comment) } const formatTime (time: string) { // 使用 day.js 或自己实现时间格式化 return new Date(time).toLocaleDateString() } /script style scoped .prompt-card { border-radius: 12px; overflow: hidden; background: #fff; box-shadow: 0 2px 12px rgba(0,0,0,0.08); transition: transform 0.2s, box-shadow 0.2s; } .prompt-card:hover { transform: translateY(-4px); box-shadow: 0 8px 24px rgba(0,0,0,0.12); } .image-container { position: relative; aspect-ratio: 1 / 1; /* 正方形图片区域 */ overflow: hidden; } .image-overlay { position: absolute; bottom: 12px; right: 12px; opacity: 0; transition: opacity 0.3s; } .image-container:hover .image-overlay { opacity: 1; } .card-body { padding: 16px; } .prompt-title { margin: 0 0 10px 0; font-size: 1.1rem; color: #333; } .prompt-content-wrapper { margin-bottom: 12px; } .prompt-text { margin: 0; font-family: Monaco, Menlo, Consolas, monospace; font-size: 0.9rem; line-height: 1.5; color: #555; background-color: #f8f9fa; padding: 10px; border-radius: 6px; white-space: pre-wrap; word-break: break-all; max-height: 6em; /* 约4行高度 */ overflow: hidden; transition: max-height 0.3s ease; } .prompt-text.collapsed { max-height: 6em; -webkit-mask-image: linear-gradient(to bottom, black 50%, transparent 100%); mask-image: linear-gradient(to bottom, black 50%, transparent 100%); } .prompt-text:not(.collapsed) { max-height: 500px; /* 足够大的值 */ -webkit-mask-image: none; mask-image: none; } .tag-list { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 16px; } .card-footer { display: flex; justify-content: space-between; align-items: center; font-size: 0.85rem; color: #666; } .author-info { display: flex; align-items: center; gap: 8px; } .action-buttons { display: flex; gap: 4px; } /style设计要点解析视觉焦点大图预览吸引点击悬停显示“复制”按钮符合直觉。Prompt 文本处理代码样式使用等宽字体和浅灰色背景将其与描述性文本区分开暗示其“可执行代码”的属性。展开/收起长 Prompt 默认折叠通过渐变遮罩营造“未完待续”的感觉避免页面被长篇文本破坏布局。一键复制最核心的交互按钮位置醒目。标签系统标签可点击作为重要的内容导航入口。互动设计点赞、收藏、评论按钮直观并通过颜色变化如点赞变蓝提供即时反馈。4.2 发布页面的表单设计发布页面 (PublishPage.vue) 的表单需要引导用户输入结构化信息。!-- views/PublishPage.vue 部分代码 -- template div classpublish-container h2分享你的魔法配方/h2 el-form :modelform :rulesrules label-width100px el-form-item label作品标题 proptitle el-input v-modelform.title placeholder给你的作品起个吸引人的名字 / /el-form-item el-form-item labelPrompt 咒语 proppromptText div classprompt-editor el-input v-modelform.promptText typetextarea :rows8 placeholder请输入完整的 Prompt支持权重 (word:1.5)、负面提示等... resizenone / div classeditor-tips span小提示使用英文逗号分隔关键词用括号控制权重。/span el-button link typeprimary clickinsertTemplate插入常用参数模板/el-button /div /div /el-form-item el-form-item label生成图片 propimageUrl el-upload classimage-uploader action/api/upload !-- 替换为你的上传接口 -- :show-file-listfalse :on-successhandleUploadSuccess :before-uploadbeforeUpload img v-ifform.imageUrl :srcform.imageUrl classuploaded-image / el-icon v-else classuploader-iconPlus //el-icon /el-upload /el-form-item el-form-item label标签 proptags el-select v-modelform.tags multiple filterable allow-create default-first-option placeholder选择或创建标签如人物、风景、赛博朋克 el-option v-fortag in popularTags :keytag :labeltag :valuetag / /el-select /el-form-item el-form-item label模型参数 propmodelParams el-input v-modelform.modelParams typetextarea :rows3 placeholder可选的模型参数如steps: 30, sampler: DPM 2M Karras, cfg_scale: 7 / /el-form-item el-form-item label作品描述 propdescription el-input v-modelform.description typetextarea :rows4 placeholder分享一下创作思路、调参心得或者这个 Prompt 的特别之处... / /el-form-item el-form-item el-button typeprimary clicksubmitForm发布分享/el-button el-button clickresetForm重置/el-button /el-form-item /el-form /div /template设计要点解析引导性输入在 Prompt 输入框下方提供小提示和“插入模板”按钮降低新手门槛。图片上传使用 Element Plus 的 Upload 组件提供清晰的预览。灵活的标签系统支持选择热门标签和创建新标签方便内容分类。分离模型参数将steps,sampler等参数与核心 Prompt 文本分开使数据结构更清晰也方便高级用户交流。5. 状态管理与页面路由5.1 使用 Pinia 管理全局状态我们创建一个usePromptStore来集中管理 Prompt 相关的数据和逻辑。// stores/prompt.ts import { defineStore } from pinia import { ref, computed } from vue import type { PromptCardData, PromptListParams } from /types import { fetchPromptList, likePrompt, favoritePrompt } from /api/prompt export const usePromptStore defineStore(prompt, () { // 状态 const promptList refPromptCardData[]([]) const currentPage ref(1) const total ref(0) const isLoading ref(false) const currentFilter reflatest | hottest(latest) // Getter (计算属性) const totalPages computed(() Math.ceil(total.value / 20)) // Actions (方法) const loadPrompts async (params: PromptListParams {}) { isLoading.value true try { const res await fetchPromptList({ page: currentPage.value, ...params }) promptList.value res.data.list total.value res.data.total if (params.sort) { currentFilter.value params.sort } } catch (error) { console.error(加载Prompt列表失败:, error) } finally { isLoading.value false } } const toggleLike async (promptId: string) { const prompt promptList.value.find(p p.id promptId) if (!prompt) return const oldStatus prompt.isLiked const oldCount prompt.likeCount // 乐观更新 prompt.isLiked !oldStatus prompt.likeCount oldStatus ? oldCount - 1 : oldCount 1 try { await likePrompt(promptId) } catch (error) { // 失败则回滚 prompt.isLiked oldStatus prompt.likeCount oldCount console.error(点赞操作失败:, error) } } const toggleFavorite async (promptId: string) { // 逻辑与 toggleLike 类似 } // 初始化加载 loadPrompts() return { promptList, currentPage, totalPages, isLoading, currentFilter, loadPrompts, toggleLike, toggleFavorite, } })5.2 配置页面路由在router/index.ts中定义主要页面。import { createRouter, createWebHistory } from vue-router const router createRouter({ history: createWebHistory(), routes: [ { path: /, name: Home, component: () import(/views/HomePage.vue), }, { path: /publish, name: Publish, component: () import(/views/PublishPage.vue), meta: { requiresAuth: true } // 需要登录 }, { path: /prompt/:id, name: PromptDetail, component: () import(/views/PromptDetailPage.vue), props: true }, { path: /tag/:tagName, name: Tag, component: () import(/views/TagPage.vue), props: true }, { path: /user/:userId, name: User, component: () import(/views/UserPage.vue), props: true }, { path: /search, name: Search, component: () import(/views/SearchPage.vue), }, ], }) // 可以在这里添加全局路由守卫用于权限检查等 export default router6. 首页与发现页面的实现首页 (HomePage.vue) 是平台的门面需要兼顾信息密度和浏览体验。!-- views/HomePage.vue -- template div classhome-page !-- 顶部 Banner 和搜索栏 -- div classhero-section h1发现 Z-Image-Turbo 的无限可能/h1 p探索、分享、创作高质量的 AI 绘画 Prompt/p div classsearch-box el-input v-modelsearchKeyword placeholder搜索 Prompt、标签或描述... :prefix-iconSearch keyup.enterhandleSearch template #append el-button :iconSearch clickhandleSearch / /template /el-input /div /div !-- 内容区 -- div classmain-content !-- 侧边栏分类筛选 -- aside classsidebar div classfilter-section h3排序方式/h3 el-radio-group v-modelactiveSort changehandleSortChange el-radio-button labellatest最新发布/el-radio-button el-radio-button labelhottest最受欢迎/el-radio-button /el-radio-group /div div classfilter-section h3热门标签/h3 div classtag-cloud el-tag v-fortag in hotTags :keytag.name :typeactiveTag tag.name ? primary : info sizelarge clickhandleTagClick(tag.name) {{ tag.name }} ({{ tag.count }}) /el-tag /div /div /aside !-- 主内容区Prompt 列表 -- main classprompt-list-container div v-ifisLoading classloading-state el-skeleton :rows6 animated / /div div v-else-ifpromptList.length 0 classempty-state el-empty description暂无内容快来发布第一个 Prompt 吧 / /div div v-else classprompt-grid PromptCard v-foritem in promptList :keyitem.id :card-dataitem tag-clickhandleTagClick classgrid-item / /div !-- 分页 -- div classpagination-wrapper el-pagination v-model:current-pagecurrentPage :page-size20 :totaltotal layoutprev, pager, next, jumper current-changehandlePageChange / /div /main /div /div /template script setup langts import { ref, computed, watch } from vue import { useRouter } from vue-router import { Search } from element-plus/icons-vue import PromptCard from /components/PromptCard.vue import { usePromptStore } from /stores/prompt const router useRouter() const promptStore usePromptStore() const searchKeyword ref() const activeSort reflatest | hottest(latest) const activeTag refstring | null(null) // 从 store 中获取数据 const promptList computed(() promptStore.promptList) const isLoading computed(() promptStore.isLoading) const currentPage computed({ get: () promptStore.currentPage, set: (val) { promptStore.currentPage val } }) const total computed(() promptStore.total) // 模拟热门标签数据 const hotTags ref([ { name: 人物肖像, count: 1245 }, { name: 风景, count: 892 }, { name: 赛博朋克, count: 567 }, { name: 动漫, count: 1203 }, { name: 概念艺术, count: 441 }, // ... 更多标签 ]) const handleSearch () { if (searchKeyword.value.trim()) { router.push({ name: Search, query: { q: searchKeyword.value } }) } } const handleSortChange (sort: latest | hottest) { promptStore.loadPrompts({ sort }) } const handleTagClick (tagName: string) { activeTag.value activeTag.value tagName ? null : tagName router.push({ name: Tag, params: { tagName } }) } const handlePageChange (page: number) { promptStore.loadPrompts({ page }) } /script7. 总结通过这个项目我们完成了一个功能相对完整的 Prompt 分享社区前端。从技术实现上看我们利用了 Vue 3 的响应式特性和 Composition API 来构建清晰、可维护的组件逻辑通过 Pinia 进行集中的状态管理让点赞、收藏等交互的“乐观更新”变得简单借助 Element Plus 组件库我们快速搭建了美观且一致的用户界面。但更重要的是我们深入思考了如何为“Prompt”这种特殊内容设计用户体验。通过可折叠的代码块、一键复制、标签云、结构化的发布表单我们让原本晦涩难懂的参数文本变得友好、易用、易于传播。这不仅仅是前端技术的堆砌更是对用户需求和内容特性的深度理解。当然这只是一个起点。在实际项目中你还需要考虑更多比如用户系统的集成登录、注册、图片的懒加载与 CDN 优化、更复杂的搜索过滤、移动端的响应式适配、以及接入真实的后端 API。希望这个实战案例能为你构建自己的 AI 工具社区提供一些有用的思路和扎实的代码基础。动手试试看也许下一个爆款的 AI 创作社区就出自你的手中。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

更多文章