AI书签管理工具开发全记录(七):页面编写与接口对接

article/2025/6/23 16:01:14

文章目录

  • AI书签管理工具开发全记录(七):页面编写与接口对接
    • 前言 📝
    • 1. 页面功能规划 📌
    • 2. 接口api编写 📡
      • 2.1 创建`.env`,设置环境变量
      • 2.2 增加`axios`拦截器
      • 2.3 创建接口
    • 2. 页面编写 📄
      • 2.1 示例代码
    • 3. 跨域问题 🌐
      • 3.1 配置cors中间件
    • 4.页面效果 ✨
      • 4.1 分类管理页面
      • 4.2 书签管理页面
    • 总结 📚

AI书签管理工具开发全记录(七):页面编写与接口对接

前言 📝

在上一篇博客中,我们完成了前端基础框架的搭建。现在,我们将实现书签和分类管理的具体页面,并与后端API进行对接,完成整个前后端交互流程。

1. 页面功能规划 📌

我们需要实现以下核心功能页面:

  1. ​书签管理页面​
    • 书签列表展示
    • 书签增删改查​
  2. ​分类管理页面​
    • 分类列表展示
    • 分类增删改查

2. 接口api编写 📡

2.1 创建.env,设置环境变量

VITE_API_BASE_URL=http://localhost:8080

2.2 增加axios拦截器

import axios from 'axios'
import { ElMessage } from 'element-plus'// 创建 axios 实例
const service = axios.create({baseURL: import.meta.env.VITE_API_BASE_URL,timeout: 10000
})// 请求拦截器
service.interceptors.request.use((config) => {// 在这里可以添加请求前的处理逻辑return config},(error) => {return Promise.reject(error)}
)// 响应拦截器
service.interceptors.response.use((response) => {// 在这里可以添加响应后的处理逻辑const res = response.data// 如果返回的状态码不是200,说明接口请求有误if (response.status !== 200) {ElMessage.error(res.message || '请求失败')return Promise.reject(new Error(res.message || '请求失败'))}return res},(error) => {ElMessage.error(error.message || '请求失败')return Promise.reject(error)}
)export default service

2.3 创建接口

以分类为例,书签相似。

// src/api/category/index.js
import request from '/@/utils/request'// 创建分类
export function createCategory(data) {return request({url: '/api/categories',method: 'post',data})
}// 获取分类列表
export function listCategories(params) {return request({url: '/api/categories',method: 'get',params})
}// 获取单个分类
export function getCategory(id) {return request({url: `/api/categories/${id}`,method: 'get'})
}// 更新分类
export function updateCategory(id, data) {return request({url: `/api/categories/${id}`,method: 'put',data})
}// 删除分类
export function deleteCategory(id) {return request({url: `/api/categories/${id}`,method: 'delete'})
}

2. 页面编写 📄

2.1 示例代码

以分类为例

<!--/src/views/category/index.vue -->
<template><div class="category-container"><div class="header"><h2>分类管理</h2></div><div class="toolbar"><div class="search-row"><el-inputv-model="searchName"placeholder="请输入分类名称"class="search-input"clearable@clear="handleSearch"@keyup.enter="handleSearch"><template #append><el-button @click="handleSearch"><el-icon><Search /></el-icon></el-button></template></el-input></div><div class="button-row"><el-button type="primary" @click="handleAdd">新增分类</el-button></div></div><el-table :data="categoryList" style="width: 100%" v-loading="loading"><el-table-column prop="id" label="ID" width="80" /><el-table-column prop="name" label="分类名称" /><el-table-column prop="description" label="描述" /><el-table-column label="操作" width="200"><template #default="{ row }"><el-button type="primary" link @click="handleEdit(row)">编辑</el-button><el-button type="danger" link @click="handleDelete(row)">删除</el-button></template></el-table-column></el-table><div class="pagination"><el-paginationv-model:current-page="currentPage"v-model:page-size="pageSize":page-sizes="[10, 20, 50, 100]"layout="total, sizes, prev, pager, next":total="total"@size-change="handleSizeChange"@current-change="handleCurrentChange"/></div><!-- 新增/编辑对话框 --><el-dialogv-model="dialogVisible":title="dialogType === 'add' ? '新增分类' : '编辑分类'"width="500px"><el-formref="formRef":model="form":rules="rules"label-width="80px"><el-form-item label="分类名称" prop="name"><el-input v-model="form.name" placeholder="请输入分类名称" /></el-form-item><el-form-item label="描述" prop="description"><el-inputv-model="form.description"type="textarea"placeholder="请输入分类描述"/></el-form-item></el-form><template #footer><span class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="handleSubmit">确定</el-button></span></template></el-dialog></div>
</template><script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Search } from '@element-plus/icons-vue'
import { listCategories, createCategory, updateCategory, deleteCategory } from '/@/api/category'// 数据列表
const categoryList = ref([])
const loading = ref(false)
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
const searchName = ref('')// 对话框相关
const dialogVisible = ref(false)
const dialogType = ref('add')
const formRef = ref(null)
const form = ref({name: '',description: ''
})// 表单验证规则
const rules = {name: [{ required: true, message: '请输入分类名称', trigger: ['blur', 'change'] },{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: ['blur', 'change'] }],description: [{ max: 200, message: '长度不能超过 200 个字符', trigger: ['blur', 'change'] }]
}// 获取分类列表
const getList = async () => {loading.value = truetry {const res = await listCategories({page: currentPage.value,size: pageSize.value,name: searchName.value})categoryList.value = res.datatotal.value = res.total} catch (error) {ElMessage.error('获取分类列表失败')} finally {loading.value = false}
}// 处理搜索
const handleSearch = () => {currentPage.value = 1getList()
}// 处理新增
const handleAdd = () => {dialogType.value = 'add'form.value = {name: '',description: ''}dialogVisible.value = true
}// 处理编辑
const handleEdit = (row) => {dialogType.value = 'edit'form.value = {id: row.id,name: row.name,description: row.description}dialogVisible.value = true
}// 处理删除
const handleDelete = (row) => {ElMessageBox.confirm('确认删除该分类吗?', '提示', {confirmButtonText: '确定',cancelButtonText: '取消',type: 'warning'}).then(async () => {try {await deleteCategory(row.id)ElMessage.success('删除成功')getList()} catch (error) {ElMessage.error('删除失败')}})
}// 处理提交
const handleSubmit = async () => {if (!formRef.value) returnawait formRef.value.validate(async (valid) => {if (valid) {try {if (dialogType.value === 'add') {await createCategory(form.value)ElMessage.success('新增成功')} else {await updateCategory(form.value.id, form.value)ElMessage.success('更新成功')}dialogVisible.value = falsegetList()} catch (error) {ElMessage.error(dialogType.value === 'add' ? '新增失败' : '更新失败')}}})
}// 处理分页
const handleSizeChange = (val) => {pageSize.value = valgetList()
}const handleCurrentChange = (val) => {currentPage.value = valgetList()
}// 初始化
onMounted(() => {getList()
})
</script><style scoped>
.category-container {padding: 20px;
}.header {margin-bottom: 16px;
}.header h2 {margin: 0;font-size: 20px;font-weight: 500;
}.toolbar {margin-bottom: 16px;
}.search-row {margin-bottom: 12px;
}.search-input {width: 240px;
}.pagination {margin-top: 20px;display: flex;justify-content: flex-end;
}
</style>

3. 跨域问题 🌐

3.1 配置cors中间件

// internal/api/api.go:NewServer// 配置CORS
router.Use(func(c *gin.Context) {c.Writer.Header().Set("Access-Control-Allow-Origin", "*")c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")if c.Request.Method == "OPTIONS" {c.AbortWithStatus(204)return}c.Next()
})

4.页面效果 ✨

4.1 分类管理页面

image.png

4.2 书签管理页面

image.png

总结 📚

本文详细介绍了AI书签管理工具的前端页面实现和接口对接过程,主要完成了:

  1. ​书签管理页面​​:实现列表展示、搜索、分页、增删改查
  2. ​分类管理页面​​:实现分类的CRUD操作
  3. ​接口对接​​:封装API请求,处理跨域问题
  4. ​表单验证​​:实现表单验证逻辑

在下一篇文章中,我们将实现Ai创建书签功能。


http://www.hkcw.cn/article/UtBlVlcNKG.shtml

相关文章

“AI 编程三国杀” Google Jules, OpenAl Codex, Claude Code,人类开始沦为AI编程发展的瓶颈?

AI 编程三国杀:Google Jules, OpenAI Codex, Claude code “AI 编程三国杀”是一个形象的比喻,借指当前 AI 编程领域中几个主要参与者之间的激烈竞争与并存的局面。这其中,Google、OpenAI 以及 Anthropic (Claude 的开发者) 是重要的“国家”,而它们各自的 AI 编程工具则是…

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 文件事件处理部分)

分析客户端和服务端网络诵信交互实现 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 命令请求的执行过程案例分析介绍发送命令请求读取命令请求客户端状态的argv属性和argc属性命令执行器第…

第29次CCF计算机软件能力认证-3-LDAP

LDAP 刷新 时间限制&#xff1a; 10.0 秒 空间限制&#xff1a; 512 MiB 下载题目目录&#xff08;样例文件&#xff09; 题目背景 西西艾弗岛运营公司是一家负责维护和运营岛上基础设施的大型企业&#xff0c;拥有数千名员工。公司内有很多 IT 系统。 为了能够实现这些…

2025年- H63-Lc171--33.搜索旋转排序数组(2次二分查找,需二刷)--Java版

1.题目描述 2.思路 输入&#xff1a;旋转后的数组 nums&#xff0c;和一个整数 target 输出&#xff1a;target 在 nums 中的下标&#xff0c;如果不存在&#xff0c;返回 -1 限制&#xff1a;时间复杂度为 O(log n)&#xff0c;所以不能用遍历&#xff0c;必须使用 二分查找…

HomeKit 基本理解

概括 HomeKit 将用户的家庭自动化信息存储在数据库中&#xff0c;该数据库由苹果的内置iOS家庭应用程序、支持HomeKit的应用程序和其他开发人员的应用程序共享。所有这些应用程序都使用HomeKit框架作为对等程序访问数据库. Home 只是相当于 HomeKit 的表现层,其他应用在实现 …

秒杀系统—5.第二版升级优化的技术文档三

大纲 8.秒杀系统的秒杀库存服务实现 9.秒杀系统的秒杀抢购服务实现 10.秒杀系统的秒杀下单服务实现 11.秒杀系统的页面渲染服务实现 12.秒杀系统的页面发布服务实现 8.秒杀系统的秒杀库存服务实现 (1)秒杀商品的库存在Redis中的结构 (2)库存分片并同步到Redis的实现 (3…

尚硅谷-尚庭公寓知识点

文章目录 尚庭公寓知识点1、转换器(Converter)2、全局异常3、定时任务1. 核心步骤(1) 启用定时任务(2) 创建定时任务 2. Scheduled 参数详解3. Cron 表达式语法4. 配置线程池&#xff08;避免阻塞&#xff09;5. 动态控制任务&#xff08;高级用法&#xff09;6. 注意事项 4、M…

字符串~~~

字符串~~ KMP例题1.无线传输2.删除字符串3.二叉树中的链表 AC自动机Manacher例题 扩展KMP字符串哈希 KMP &#xff08;1&#xff09; &#xff08;2&#xff09; &#xff08;3&#xff09; 经典例题 https://leetcode.cn/problems/find-the-index-of-the-first-occurre…

WEB3——简易NFT铸造平台之nft.storage

&#x1f9e0; 1. nft.storage 是什么&#xff1f; https://nft.storage 是 一个免费的去中心化存储平台&#xff0c;由 Filecoin 背后的 Protocol Labs 推出。 它的作用是&#xff1a; ✅ 接收用户上传的文件&#xff08;图片、JSON 等&#xff09; ✅ 把它们永久存储到 IPFS…

MCP架构全解析:从核心原理到企业级实践

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…

注销微软账户

若你需要注销微软账号&#xff0c;请点击下方超链接。 点击此处

华为OD机试真题——生成哈夫曼树(2025A卷:100分)Java/python/JavaScript/C/C++/GO六种最佳实现

2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 本文收录于专栏:《2025华为OD真题目录+全流程解析/备考攻略/经验分享》 华为OD机试真题《生成…

Python实现P-PSO优化算法优化BP神经网络分类模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 随着人工智能技术的快速发展&#xff0c;神经网络在分类任务中展现了强大的性能。BP&#xff08;Back Propagation&…

学习海康VisionMaster之表面缺陷滤波

一&#xff1a;进一步学习了 今天学习下VisionMaster中的表面缺陷滤波&#xff1a;简单、无纹理背景的表面缺陷检测&#xff0c;可以检测表面的异物&#xff0c;缺陷&#xff0c;划伤等 二&#xff1a;开始学习 1&#xff1a;什么表面缺陷滤波&#xff1f; 表面缺陷滤波的核心…

34.x64汇编写法(一)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;33.第二阶段x64游戏实战-InLineHook 首先打开 Visual Studio&#xff0c;然后创…

Java网络编程实战:TCP/UDP Socket通信详解与高并发服务器设计

&#x1f50d; 开发者资源导航 &#x1f50d;&#x1f3f7;️ 博客主页&#xff1a; 个人主页&#x1f4da; 专栏订阅&#xff1a; JavaEE全栈专栏 内容&#xff1a; socket(套接字)TCP和UDP差别UDP编程方法使用简单服务器实现 TCP编程方法Socket和ServerSocket之间的关系使用简…

算法:滑动窗口

1.长度最小的子数组 209. 长度最小的子数组 - 力扣&#xff08;LeetCode&#xff09; 运用滑动窗口&#xff08;同向双指针&#xff09;来解决&#xff0c;因为这些数字全是正整数&#xff0c;在left位置确定的下&#xff0c;right这个总sum会越大&#xff0c;所以我们先让num…

AI笔记 - 网络模型 - mobileNet

网络模型 mobileNet mobileNet V1网络结构深度可分离卷积空间可分![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/aff06377feac40b787cfc882be7c6e5d.png) 参考 mobileNet V1 网络结构 MobileNetV1可以理解为VGG中的标准卷积层换成深度可分离卷积 可分离卷积主要有…

新中地三维GIS开发智慧城市效果和应用场景

近年来&#xff0c;随着科技的发展和城市化进程的加速&#xff0c;智慧城市成为了全球各大城市的一个重要发展方向。 在这一背景下&#xff0c;三维GIS技术以其独特的优势&#xff0c;成为构建智慧城市不可或缺的工具。新中地GIS开发特训营正是在这样的大环境下应运而生&#…

Linux笔记---线程

1. 线程的介绍 1.1 线程的概念 基本定义&#xff1a; 线程&#xff08;Thread&#xff09;是操作系统能够进行运算调度的最小单位。它被包含在进程&#xff08;Process&#xff09;之中&#xff08;或者说是进程的一部分、对进程的划分&#xff09;&#xff0c;是进程中的实际…