当前位置: 首页 > news >正文

AI书签管理工具开发全记录(九):用户端页面集成与展示

文章目录

  • AI书签管理工具开发全记录(九):用户端页面集成与展示
    • 前言
    • 设计思路
    • 实现步骤
      • 1. 路由配置
      • 2. 全局样式设置
      • 3. 首页实现
      • 4. Vite配置
    • 设计说明
      • 1. 部分UI设计
      • 2. 响应式布局
      • 3. 加载更多功能
    • 效果展示
      • 效果展示

AI书签管理工具开发全记录(九):用户端页面集成与展示

前言

在之前的文章中,我们完成了书签管理后台和AI智能创建功能。本文将重点介绍用户端页面的设计与实现,该页面是普通用户访问和管理书签的主界面。界面美观是最重要特点之一,因为是定位是个人使用的工具,所以一切从简,和后台管理集成在一个应用中。

设计思路

用户端页面需要实现以下核心功能:

  1. ​书签展示​​:网格形式展示书签
  2. ​搜索功能​​:通过关键词查找书签
  3. ​分类筛选​​:按类别展示相关书签
  4. ​响应式设计​​:适配不同设备

实现步骤

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>

效果展示

用户端页面采用现代化设计:

  • 渐变背景配合科技感效果
  • 书签卡片具有毛玻璃效果
  • 悬停动画增强交互体验
  • 响应式布局适配各种设备

效果展示

image.png

点击后台管理可以访问原先后台管理页面,默认用户端页面

相关文章:

  • 排序算法——详解
  • 4.大语言模型预备数学知识
  • 【iOS(swift)笔记-13】App版本不升级时本地数据库sqlite更新逻辑一
  • 企业展示型网站模板HTML5网站模板下载指南
  • PostgreSQL 在生物信息学中的应用
  • Java并发编程实战 Day 4:线程间通信机制
  • 网络节点排查
  • cpper 转 Golang
  • 家政维修平台实战12搭建服务详情功能
  • 实现按天更新vintage并热力图可视化
  • 如何用AI写作?
  • SQL Views(视图)
  • SQL快速入门【转自牛客网】
  • MCP协议学习
  • Transformer核心技术深度解析:多头注意力机制与架构精粹
  • 20250602在荣品的PRO-RK3566开发板的Android13下打开关机对话框
  • 【Linux】网络--网络层--IP协议
  • 抖音商城抓包 分析
  • 基于Python学习《Head First设计模式》第三章 装饰者模式
  • Android基于LiquidFun引擎实现软体碰撞效果
  • 杭州网站开发公司/百度新闻app
  • 河北建筑工程信息网站/优化推广seo
  • 购物网站如何建设/网络推广平台哪家公司最好
  • 安徽网站建设费用/推广网站推广
  • 做的网站在百度上搜不出来/网站推广优化排名教程
  • 企业首次建设网站方案流程/代理推广