当前位置: 首页 > 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

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

http://www.lqws.cn/news/85717.html

相关文章:

  • 智慧政务标准规范介绍:构建高效、协同的政务信息体系
  • 【nm】nm命令的使用:查看.so中的符号信息
  • 构建高性能风控指标系统
  • YARN应用日志查看
  • ubuntu安装devkitPro
  • DAX权威指南6:DAX 高级概念(扩展表)、DAX 计算常见优化
  • 7.文本内容处理sort,uniq,out,cat,comm,diff
  • 前端面经高阶组件HOC 和 HOOKS Redux
  • 小白的进阶之路系列之十----人工智能从初步到精通pytorch综合运用的讲解第三部分
  • cnn训练并用grad-cam可视化
  • 云服务器突发宕机或无响应怎么办
  • MCP (模型上下文协议):AI界的“USB-C”标准,开启大模型应用新纪元
  • URP - 水效果Shader
  • 动中通天线跟踪性能指标的测试
  • 密码学:解析Feistel网络结构及实现代码
  • imx6ull(0):烧录、启动
  • 《软件项目管理》第二章(项目准备与启动)期末周复习总结笔记
  • C++ list代码练习、set基础概念、set对象创建、set大小操作
  • 2025GDCPC广东省赛游记(附赛时代码)
  • 基于LangChain的AI助手开发:从零到上线
  • 天机学堂-分页查询
  • 21-CS61B-lab6:java文件操作以及持久化一见
  • PNG文件格式
  • 【技术支持】安卓开发中queryUsageStats不准确的问题
  • 【latex】易遗忘的表达
  • cpper 转 Golang
  • 【LLM】AI Agents vs. Agentic AI(概念应用挑战)
  • K-匿名模型
  • 英语中什么时候用that?
  • 电磁场与电磁波公式汇总