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

从0开始搭建vue + flask 旅游景点数据分析系统(九):旅游景点管理之增删改查

这一期来做旅游景点数据的增删改查

先看下我们做好的效果是这样的:
在这里插入图片描述## 1 后台接口

这里的接口已经考虑到了分页的情况,因为前端的表格是带有分页的,接受的前端传过来的get参数为 title 、page、 limit ,titie是查询的关键词,会根据景点的title去模糊查询。

@main.route('/tours', methods=['GET'])
def get_tours():try:title = request.args.get('title', '')  # 获取查询参数中的 titlepage = int(request.args.get('page', 1))  # 获取当前页码,默认为 1limit = int(request.args.get('limit', 10))  # 获取每页显示的记录数,默认为 10# 根据 title 进行模糊搜索query = Tour.query.filter(Tour.title.like(f'%{title}%'))# 计算总数和获取当前页数据total = query.count()  # 总记录数tours = query.offset((page - 1) * limit).limit(limit).all()  # 当前页的数据result = tours_schema.dump(tours)  # 使用你的序列化方案处理数据return make_response(data={'total': total, 'records': result})except Exception as e:return make_response(code=1, message=str(e))

2 前端表格

前端表格分为表头、表格数据部分、分页,放在一个el-card中,注意到表格栏中有景点图片的处理,可以在表格中直接展示图片。

<el-card class="box-card"><div slot="header" class="header"><span class="header-title">旅游景点管理</span><div class="header-controls"><el-input v-model="searchTitle" placeholder="输入标题进行搜索" class="search-input"></el-input><el-button type="primary" @click="fetchData">搜索</el-button><el-button type="success" @click="handleAddTour">添加景点</el-button></div></div><el-table :data="tours" style="width: 100%"><el-table-column label="图片" width="120"><template slot-scope="scope"><el-image:src="scope.row.img"class="tour-image":alt="scope.row.title"fit="cover"lazy/></template></el-table-column><el-table-column prop="title" label="景点名称" min-width="180"></el-table-column><el-table-column prop="title_en" label="别名" min-width="180"></el-table-column><el-table-column prop="comments" label="评论数" min-width="100"></el-table-column><el-table-column prop="score" label="评分" min-width="100"></el-table-column><el-table-column prop="nation" label="国家" min-width="120"></el-table-column><el-table-column prop="city" label="城市" min-width="120"></el-table-column><el-table-column label="操作" min-width="180"><template slot-scope="scope"><el-button @click="handleEditTour(scope.row)" type="text" size="small">编辑</el-button><el-button @click="handleDeleteTour(scope.row)" type="text" size="small">删除</el-button></template></el-table-column></el-table><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="currentPage":page-size="pageSize":total="totalItems"layout="total, sizes, prev, pager, next, jumper"/></el-card>

数据部分是这样的,原先的静态数据改成[]就行了,通过后台接口来获取数据:

data() {return {searchTitle: '',tours: [],// tours: [//   { id: 1, name: '东京迪士尼度假区', alias: 'Tokyo Disney Resort', reviewCount: 1500, rating: 4.8, featuredReview: '非常美丽的景点', country: '日本', city: '东京' },//   { id: 2, name: '东京塔', alias: 'Tokyo Tower', reviewCount: 2500, rating: 4.9, featuredReview: '历史悠久,气势恢宏', country: '日本', city: '东京' },//   { id: 3, name: '三鹰之森吉卜力美术馆', alias: 'Ghibli Museum', reviewCount: 1800, rating: 4.7, featuredReview: '象征自由的地标', country: '日本', city: '东京' }// ],dialogVisible: false,form: {},formLabelWidth: '100px',totalItems: 0,currentPage: 1,pageSize: 10,};},

在页面加载的时候默认让它显示第一页,进行搜索的时候,会根据关键词去查询后台接口:

mounted() {this.currentPage = 1this.loadData()},methods: {fetchData() {this.loadData()},//加载数据loadData() {tours(this.searchTitle, this.currentPage, this.pageSize).then(res => {// console.log(res.data.data.records);this.tours = res.data.data.recordsthis.totalItems = res.data.data.total})},handleCurrentChange(page) {this.currentPage = page;this.loadData();},handleSizeChange(size) {this.pageSize = size;this.loadData();},
}

还有两个分页相关的方法也可以注意下,因为分页控件可以调整页数、每页显示的数量,点击之后也需要加载数据,而且假如说这时候是有搜索关键词的,关键词也需要保持。

样式部分

.tours-container {padding: 20px;
}.header {display: flex;justify-content: space-between;align-items: center;padding: 10px 20px;
}.dialog-footer {text-align: right;
}.header-title {font-size: 18px;font-weight: bold;
}.header-controls {display: flex;align-items: center;
}.search-input {width: 300px;margin-right: 10px; /* Adjust spacing between input and buttons */
}

在tour.js中需要添加新的接口:

export function  tours(title,page=1,limit=10){return request({url:  '/tours',method: 'get',params: {title, page, limit}})
}

3 搜索效果测试

在这里插入图片描述

4 新增景点

先增加后端方法:

@main.route('/tour', methods=['POST'])
def add_tour():data = request.json  # 获取JSON数据# 这里可以进行数据验证,例如检查必填字段是否存在required_fields = ['img', 'title', 'title_en', 'comments', 'score', 'select_comment', 'nation', 'city']for field in required_fields:if field not in data:return make_response(code=1, message= f'错误,缺少字段: {field}')# 创建新的景点对象new_tour = Tour(img=data['img'],title=data['title'],title_en=data['title_en'],comments=data['comments'],score=data['score'],select_comment=data['select_comment'],nation=data['nation'],city=data['city'] )

前端部分我们先增加一个接口,使用的是POST请求:

export function  addTour(data){return request({url:  '/tour',method: 'post',data})
}

页面部分是先点击添加景点,触发handleAddTour,打开对话框:

handleAddTour() {this.dialogTitle = '新增景点'this.dialogVisible = true;this.form = {img: '',title: '',title_en: '',comments: 0,score: 0,select_comment: '',nation: '',city: ''};},<el-dialog :title="dialogTitle" :visible.sync="dialogVisible"><el-form :model="form"><el-form-item label="景点图片地址" :label-width="formLabelWidth"><el-input v-model="form.img"></el-input></el-form-item><el-form-item label="景点名称" :label-width="formLabelWidth"><el-input v-model="form.title"></el-input></el-form-item><el-form-item label="别名" :label-width="formLabelWidth"><el-input v-model="form.title_en"></el-input></el-form-item><el-form-item label="评论数" :label-width="formLabelWidth"><el-input v-model="form.comments" type="number"></el-input></el-form-item><el-form-item label="评分" :label-width="formLabelWidth"><el-input v-model="form.score" type="number"></el-input></el-form-item><el-form-item label="精选评论" :label-width="formLabelWidth"><el-input v-model="form.select_comment"></el-input></el-form-item><el-form-item label="国家" :label-width="formLabelWidth"><el-input v-model="form.nation"></el-input></el-form-item><el-form-item label="城市" :label-width="formLabelWidth"><el-input v-model="form.city"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="handleSaveTour">保存</el-button></div></el-dialog>

对话框填写完毕后,点击handleSaveTour,有后台交互,这里为了偷懒,这个对话框我们打算既支持新增也支持编辑,所以对话框的标题也是动态的数据:

 handleSaveTour() {if (this.form.id) {//更新景点信息的逻辑} else {addTour(this.form).then(res=>{console.log(res.data.message)const messageType = res.data.code === 0 ? 'success' : 'error';Message({message: res.data.message,type: messageType, // 'success' 或者 'error' 根据需要duration: 2000 // 消息框显示的时长(毫秒)});})}this.dialogVisible = false;},

根据返回code,弹出不同的消息框(elmentui的Message)。

我们添加了一个测试景点:

在这里插入图片描述

返回消息框:
在这里插入图片描述
在这里插入图片描述

5 修改景点

先新增前后端的接口:

@main.route('/tour/<int:id>', methods=['PUT'])
def update_tour(id):data = request.json  # 获取JSON数据tour = Tour.query.get(id)  # 根据ID查找景点if not tour:return make_response(code=1, message='景点不存在')# 更新景点的字段for field in ['img', 'title', 'title_en', 'comments', 'score', 'select_comment', 'nation', 'city']:if field in data:setattr(tour, field, data[field])db.session.commit()return make_response(code=0, message='修改景点成功')

tour.js

// 修改现有景点
export function updateTour(id, data) {return request({url: `/tour/${id}`,method: 'put',data});
}

然后就很容易,修改补全刚才未完成的*handleSaveTour 就行了,基本上和新增的都一样。逻辑就是如果form.id 存在,说明是一个修改操作,如果不存在,说明是一个新增操作。*

 handleSaveTour() {if (this.form.id) {updateTour(this.form.id, this.form).then(res=>{console.log(res.data.message)const messageType = res.data.code === 0 ? 'success' : 'error';Message({message: res.data.message,type: messageType, // 'success' 或者 'error' 根据需要duration: 2000 // 消息框显示的时长(毫秒)});})} else {addTour(this.form).then(res=>{console.log(res.data.message)const messageType = res.data.code === 0 ? 'success' : 'error';Message({message: res.data.message,type: messageType, // 'success' 或者 'error' 根据需要duration: 2000 // 消息框显示的时长(毫秒)});})}this.dialogVisible = false;},

在这里插入图片描述
在这里插入图片描述

6 删除景点

先增加后端接口

@main.route('/tour/<int:id>', methods=['DELETE'])
def delete_tour(id):tour = Tour.query.get(id)  # 根据ID查找景点if not tour:return make_response(code=1, message='景点不存在')db.session.delete(tour)db.session.commit()return make_response(code=0, message='删除景点成功')

然后修改前端tour.js

// 删除景点
export function deleteTour(id) {return request({url: `/tour/${id}`,method: 'delete'});
}

最后就是完善一下 *handleDeleteTour*这个方法就是点击每行上的删除按钮触发的:

handleDeleteTour(tour) {deleteTour(tour.id).then(res=>{const messageType = res.data.code === 0 ? 'success' : 'error';Message({message: res.data.message,type: messageType, // 'success' 或者 'error' 根据需要duration: 2000 // 消息框显示的时长(毫秒)});})},

在这里插入图片描述

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • @Async 异步注解使用
  • 基于YOLOv10深度学习的草莓成熟度检测与识别系统【python源码+Pyqt5界面+数据集+训练代码】目标检测、人工智能
  • C# VideoCapture 多路视频播放
  • uniapp粘贴板地址识别 address-parse插件的使用
  • 20240810在荣品RK3588S-AHD开发板的预置Android13下挂载exFAT的256GB的TF卡
  • 十二、OpenCVSharp 中的图像匹配与模板匹配
  • buildroot是啥,用来干什么?怎么用
  • ImportError: DLL load failed: 找不到指定的程序的解决方案
  • GLM4 API 调用方法
  • CH07_数据绑定
  • 电子电气架构 --- 座舱域控制器:一芯多屏快速渗透
  • 【定稿】英飞凌Aurix2G TC3XX CAN模块详解
  • extern在头文件中添加是否必要?(C/C++)
  • 怎么衡量数据仓库模型的优与劣
  • 电池常用,但电芯热电耦合难?科学仿真技术轻松解决
  • 【162天】黑马程序员27天视频学习笔记【Day02-上】
  • HTTP--网络协议分层,http历史(二)
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • Python中eval与exec的使用及区别
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • SegmentFault 2015 Top Rank
  • Terraform入门 - 3. 变更基础设施
  • thinkphp5.1 easywechat4 微信第三方开放平台
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • Xmanager 远程桌面 CentOS 7
  • 动态魔术使用DBMS_SQL
  • 对象引论
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 来,膜拜下android roadmap,强大的执行力
  • 两列自适应布局方案整理
  • 每天一个设计模式之命令模式
  • 前端知识点整理(待续)
  • 如何合理的规划jvm性能调优
  • 设计模式(12)迭代器模式(讲解+应用)
  • 试着探索高并发下的系统架构面貌
  • ​​​​​​​开发面试“八股文”:助力还是阻力?
  • ​Python 3 新特性:类型注解
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • #职场发展#其他
  • (2)(2.4) TerraRanger Tower/Tower EVO(360度)
  • (27)4.8 习题课
  • (附源码)ssm捐赠救助系统 毕业设计 060945
  • (小白学Java)Java简介和基本配置
  • (循环依赖问题)学习spring的第九天
  • (一)Kafka 安全之使用 SASL 进行身份验证 —— JAAS 配置、SASL 配置
  • (转)甲方乙方——赵民谈找工作
  • .form文件_SSM框架文件上传篇
  • .net core MVC 通过 Filters 过滤器拦截请求及响应内容
  • .NET Core 中的路径问题
  • .NET Remoting学习笔记(三)信道
  • .netcore 6.0/7.0项目迁移至.netcore 8.0 注意事项
  • .NET编程C#线程之旅:十种开启线程的方式以及各自使用场景和优缺点
  • .net利用SQLBulkCopy进行数据库之间的大批量数据传递
  • .sh