AI书签管理工具开发全记录(九):用户端页面集成与展示
文章目录
- AI书签管理工具开发全记录(九):用户端页面集成与展示
- 前言
- 设计思路
- 实现步骤
- 1. 路由配置
- 2. 全局样式设置
- 3. 首页实现
- 4. Vite配置
- 设计说明
- 1. 部分UI设计
- 2. 响应式布局
- 3. 加载更多功能
- 效果展示
- 效果展示
AI书签管理工具开发全记录(九):用户端页面集成与展示
前言
在之前的文章中,我们完成了书签管理后台和AI智能创建功能。本文将重点介绍用户端页面的设计与实现,该页面是普通用户访问和管理书签的主界面。界面美观是最重要特点之一,因为是定位是个人使用的工具,所以一切从简,和后台管理集成在一个应用中。
设计思路
用户端页面需要实现以下核心功能:
- 书签展示:网格形式展示书签
- 搜索功能:通过关键词查找书签
- 分类筛选:按类别展示相关书签
- 响应式设计:适配不同设备
实现步骤
1. 路由配置
文件:src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import MainLayout from '/@/layout/index.vue'const routes = [{path: '/',name: 'Home',component: () => import('/@/views/home/index.vue'),meta: { title: '首页' }},{path: '/',component: MainLayout,children: [{path: 'categories',name: 'Categories',component: () => import('/@/views/category/index.vue'),meta: { title: '分类管理' }},{path: 'bookmarks',name: 'Bookmarks',component: () => import('/@/views/bookmark/index.vue'),meta: { title: '书签管理' }}]}
]const router = createRouter({history: createWebHistory(),routes
})export default router
2. 全局样式设置
文件:src/style.css
:root {--primary: #0ff;--secondary: #f0f;--dark-bg: #121826;--card-bg: rgba(25, 30, 50, 0.7);--card-border: rgba(0, 255, 255, 0.2);--text: #e0e0ff;--text-light: #a0a0c0;--success: #0f0;--warning: #ff0;--danger: #f00;
}* {margin: 0;padding: 0;box-sizing: border-box;font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}html, body {margin: 0;padding: 0;min-height: 100vh;background: var(--dark-bg);color: var(--text);
}#app {min-height: 100vh;
}/* 修复 Element Plus 弹出层样式 */
body {position: relative;
}.el-overlay {position: fixed !important;z-index: 9999 !important;
}.el-message-box {position: relative !important;z-index: 10000 !important;
}.el-popper {z-index: 10001 !important;
}
3. 首页实现
文件:src/views/home/index.vue
<template><div class="container"><header><h1>Ai<span>Bookmarks</span></h1><p>一个具有科技感的响应式书签管理器,方便你管理您书签</p><router-link to="/bookmarks" class="admin-btn"><i class="fas fa-cog"></i>后台管理</router-link></header><div class="controls"><div class="search-box"><i class="fas fa-search"></i><input type="text" placeholder="搜索书签..." v-model="searchTerm"></div><div class="filter-container"><div class="filter-scroll-wrapper"><div class="filter-buttons"><button v-for="category in categories" :key="category.id"class="filter-btn":class="{ active: currentCategory === category.id }"@click="filterByCategory(category.id)">{{ category.name }}</button></div></div></div></div><div class="bookmarks-container"><div class="bookmarks-grid"><div v-for="bookmark in filteredBookmarks" :key="bookmark.id"class="bookmark-card"@click="openBookmark(bookmark.url)"><div class="bookmark-icon"><i :class="getBookmarkIcon(bookmark)"></i></div><h3 class="bookmark-title">{{ bookmark.title }}</h3><p class="bookmark-desc">{{ bookmark.description }}</p><div class="bookmark-meta"><span class="bookmark-category">{{ getCategoryName(bookmark.category_id) }}</span><span>{{ formatDate(bookmark.created_at) }}</span></div></div></div><div v-if="loading" class="loading"><el-icon class="is-loading"><Loading /></el-icon>加载中...</div><div v-if="!loading && hasMore" class="load-more" @click="loadMore">加载更多</div></div></div>
</template><script setup>
import { ref, computed, onMounted, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { Loading } from '@element-plus/icons-vue'
import { listBookmarks } from '/@/api/bookmark'
import { listCategories } from '/@/api/category'const bookmarks = ref([])
const categories = ref([])
const searchTerm = ref('')
const currentCategory = ref(null)
const loading = ref(false)
const page = ref(1)
const pageSize = 20
const hasMore = ref(true)// 获取书签列表
const fetchBookmarks = async (isLoadMore = false) => {if (loading.value || !hasMore.value) returnloading.value = truetry {const { data } = await listBookmarks({page: page.value,page_size: pageSize})if (isLoadMore) {bookmarks.value = [...bookmarks.value, ...data]} else {bookmarks.value = data}hasMore.value = data.length === pageSizeif (hasMore.value) {page.value++}} catch (error) {ElMessage.error('获取书签列表失败')} finally {loading.value = false}
}// 获取分类列表
const fetchCategories = async () => {try {const { data } = await listCategories()categories.value = data} catch (error) {ElMessage.error('获取分类列表失败')}
}// 过滤书签
const filteredBookmarks = computed(() => {let filtered = bookmarks.value// 按分类过滤if (currentCategory.value) {filtered = filtered.filter(b => b.category_id === currentCategory.value)}// 按搜索词过滤if (searchTerm.value) {const term = searchTerm.value.toLowerCase()filtered = filtered.filter(b => b.title.toLowerCase().includes(term) || b.description.toLowerCase().includes(term))}return filtered
})// 获取分类名称
const getCategoryName = (categoryId) => {const category = categories.value.find(c => c.id === categoryId)return category ? category.name : '未分类'
}// 获取书签图标
const getBookmarkIcon = (bookmark) => {return 'fas fa-link'
}// 格式化日期
const formatDate = (date) => {return new Date(date).toLocaleDateString()
}// 打开书签
const openBookmark = (url) => {window.open(url, '_blank')
}// 按分类过滤
const filterByCategory = (categoryId) => {currentCategory.value = categoryId === currentCategory.value ? null : categoryIdpage.value = 1hasMore.value = truefetchBookmarks()
}// 加载更多
const loadMore = () => {if (!loading.value && hasMore.value) {fetchBookmarks(true)}
}// 初始化
onMounted(() => {fetchBookmarks()fetchCategories()
})
</script><style scoped>
/* 详细样式见文件内容 */
</style>
4. Vite配置
文件:web/vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'export default defineConfig({plugins: [vue(),AutoImport({resolvers: [ElementPlusResolver()]}),Components({resolvers: [ElementPlusResolver()]})],resolve: {alias: {'/@': path.resolve(__dirname, './src/')}},css: {preprocessorOptions: {scss: {additionalData: `@import "/@/styles/variable.scss";`}}}
})
代码较多,详情见完整项目。
设计说明
1. 部分UI设计
.bookmark-card {background: rgba(25, 30, 50, 0.7);border: 1px solid rgba(0, 255, 255, 0.2);backdrop-filter: blur(10px);transition: all 0.3s ease;
}.bookmark-card:hover {transform: translateY(-5px);border-color: var(--primary);box-shadow: 0 10px 25px rgba(0, 200, 255, 0.2);
}
2. 响应式布局
@media (max-width: 1024px) {.bookmarks-grid {grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));}
}@media (max-width: 480px) {.bookmarks-grid {grid-template-columns: 1fr;}
}
3. 加载更多功能
<div v-if="!loading && hasMore" class="load-more" @click="loadMore">加载更多
</div>
效果展示
用户端页面采用现代化设计:
- 渐变背景配合科技感效果
- 书签卡片具有毛玻璃效果
- 悬停动画增强交互体验
- 响应式布局适配各种设备
效果展示
点击后台管理可以访问原先后台管理页面,默认用户端页面