news 2026/4/15 16:52:04

基于Vue和Python的羽毛球拍智能推荐系统, 从“不会选羽毛球拍”到“选对拍”的一站式小工具

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Vue和Python的羽毛球拍智能推荐系统, 从“不会选羽毛球拍”到“选对拍”的一站式小工具

如果你也经历过:

  • 预算定了,但不知道该选轻一点还是头重一点
  • 看参数看懵:重量、平衡点、硬度到底怎么影响手感
  • 想要更适合自己打法的球拍清单,还想看看球友怎么说

我把这些需求做成了一个「羽毛球拍推荐系统」,把选拍流程变得更可视化、更好用,也更适合日常自用、项目展示。


1. 项目简介

这是一个面向羽毛球爱好者的 Web 系统,核心目标是把“选拍”从主观经验变成可输入、可对比、可解释的流程:

  • 通过推荐算法根据预算/重量/平衡点/硬度/技术水平匹配球拍;
  • 通过球拍库+筛选分页快速缩小选择范围;
  • 通过评价体系形成球友口碑;
  • 通过公告支持系统更新通知;
  • 通过论坛问答支持球友提问与答疑,增强内容与互动;
  • 通过个人中心管理账号信息、修改密码、查看我的评价。

2. 功能清单

2.1 前台功能

  • 首页:入口聚合、公告预览、快速开始
  • 羽毛球拍列表:筛选 + 分页
  • 羽毛球拍详情:参数展示 + 用户评价列表 +(普通用户)发布评价
  • 推荐系统:输入偏好 → 返回 Top 推荐(含匹配度)
  • 系统公告:公告列表展示
  • 球友论坛:提问列表、问题详情、发布提问、发布回答
  • 个人中心:账号信息、修改密码、我的评价(普通用户)

2.2 管理员功能

  • 管理员登录后通过 Token 调用受保护接口
  • 羽毛球拍增删改
  • 公告发布/编辑/删除(置顶排序)

2.3 系统功能展示

首页

羽毛球球拍列表

球拍推荐

论坛

系统公告

注册

个人中心


3. 技术栈

3.1 前端

  • Vue 3 + Vite
  • Tailwind CSS(统一的绿/蓝渐变主题、卡片式布局)
  • Pinia(状态管理:用户信息、球拍数据等)
  • Vue Router(路由与登录拦截)
  • Axios(请求后端 API)

3.2 后端

  • Flask + Flask-CORS
  • Flask-SQLAlchemy(ORM)
  • MySQL(通过 PyMySQL 连接)

4. 项目结构(建议读者先有全局概念)

badminton-recommend/ ├── backend/ │ ├── app.py # Flask 后端(模型 + API) │ └── requirements.txt # Python 依赖 ├── frontend/ │ ├── vite.config.js # Vite 代理配置 │ ├── src/ │ │ ├── App.vue # 全局布局(导航 + router-view + Footer) │ │ ├── router/index.js # 路由定义 + 登录拦截 │ │ ├── store/ # Pinia store │ │ └── views/ # 页面(推荐/列表/详情/公告/论坛/个人中心) │ └── package.json

5. 前后端联调方式(Vite 代理)

前端开发时通过 Vite 代理把/api转发到后端,避免跨域 & 简化请求地址:

文件:frontend/vite.config.js

exportdefaultdefineConfig({server:{port:3999,proxy:{'/api':{target:'http://127.0.0.1:5999',changeOrigin:true,secure:false}}}})

这样前端只需要请求/api/...,即可在开发环境自动走后端服务。


6. 核心模块实现(重点:推荐 / 鉴权 / 筛选分页 / 论坛问答)

6.1 推荐算法(/api/recommend)

推荐逻辑核心是:对每个球拍计算多维得分,按权重加权后取 Top N。

权重配置(可调参):

文件:backend/app.py

weights={'price':0.2,'weight':0.25,'balance_point':0.25,'hardness':0.2,'suitable_level':0.1}

推荐接口(核心片段):

@app.route('/api/recommend',methods=['POST'])defrecommend_racket():data=request.get_json()user_price=float(data.get('price',0))user_weight=int(data.get('weight',0))user_balance_point=int(data.get('balance_point',0))user_hardness=int(data.get('hardness',0))user_level=str(data.get('suitable_level','初级'))rackets=Racket.query.all()scores=[]forracketinrackets:price_score=max(0,1-abs(racket.price-user_price)/max(1000,user_price))weight_score=max(0,1-abs(racket.weight-user_weight)/30)balance_score=max(0,1-abs(racket.balance_point-user_balance_point)/30)user_hardness_normalized=min(10,max(1,user_hardness))/10*3hardness_score=max(0,1-abs(racket.hardness-user_hardness_normalized)/3)user_level_value=level_mapping.get(user_level,2)racket_level_value=level_mapping.get(racket.suitable_level,2)level_score=max(0,1-abs(racket_level_value-user_level_value)/3)total_score=(weights['price']*price_score+weights['weight']*weight_score+weights['balance_point']*balance_score+weights['hardness']*hardness_score+weights['suitable_level']*level_score)scores.append({'id':racket.id,'brand':racket.brand,'model':racket.model,'score':total_score})scores.sort(key=lambdax:x['score'],reverse=True)returnjsonify(scores[:5])

前端调用(Pinia action):

文件:frontend/src/store/racket.js

asyncrecommendRackets(criteria){constresponse=awaitaxios.post('/api/recommend',criteria)this.recommendedRackets=response.datareturnresponse.data}

6.2 管理员鉴权(Token + 装饰器)

管理员登录后返回 Token,前端保存到localStorage,后续在需要管理员权限的接口中通过Authorization: Bearer <token>传递。

文件:backend/app.py

def_get_bearer_token():auth_header=request.headers.get('Authorization','')parts=auth_header.split(' ',1)iflen(parts)!=2:returnNonescheme,value=parts[0].strip().lower(),parts[1].strip()ifscheme!='bearer'ornotvalue:returnNonereturnvaluedef_get_admin_from_request():token=_get_bearer_token()orrequest.headers.get('X-Admin-Token')ifnottoken:returnNonereturnAdmin.query.filter_by(token=token).first()defrequire_admin(func):@wraps(func)defwrapper(*args,**kwargs):admin=_get_admin_from_request()ifnotadmin:returnjsonify({'success':False,'message':'需要管理员登录'}),401returnfunc(*args,**kwargs)returnwrapper

前端保存登录态(Pinia):

文件:frontend/src/store/user.js

login(userData){constid=userData.user_id??userData.admin_id??nullthis.userId=idthis.username=userData.username||nullthis.role=userData.role||(userData.admin_id?'admin':'user')this.token=userData.token||nullthis.isLoggedIn=!!id localStorage.setItem('role',this.role)if(this.token)localStorage.setItem('token',this.token)}

6.3 球拍列表筛选 + 分页(/api/rackets)

后端通过 query 参数拼装 SQLAlchemy 查询,再分页返回:

文件:backend/app.py

@app.route('/api/rackets',methods=['GET'])defget_rackets():page=request.args.get('page',1,type=int)per_page=request.args.get('per_page',10,type=int)brand=request.args.get('brand')price_min=request.args.get('price_min',type=float)price_max=request.args.get('price_max',type=float)# ... 省略其他过滤条件query=Racket.queryifbrand:query=query.filter(Racket.brand.like(f'%{brand}%'))ifprice_minisnotNone:query=query.filter(Racket.price>=price_min)ifprice_maxisnotNone:query=query.filter(Racket.price<=price_max)total_count=query.count()rackets=query.offset((page-1)*per_page).limit(per_page).all()returnjsonify({'items':[...],'total':total_count,'page':page,'per_page':per_page,'pages':(total_count+per_page-1)//per_page})

前端 Pinia 统一接收分页结构(total/pages/page/per_page),页面只消费 store:

文件:frontend/src/store/racket.js

constresponse=awaitaxios.get('/api/rackets',{params})this.racketItems=response.data.itemsthis.total=response.data.totalthis.page=response.data.pagethis.perPage=response.data.per_pagethis.totalPages=response.data.pages

6.4 系统公告(置顶排序 + 分页)

公告列表按“置顶优先 + 时间倒序”:

文件:backend/app.py

query=Announcement.query.order_by(Announcement.is_pinned.desc(),Announcement.created_at.desc(),Announcement.id.desc())

管理员发布公告加上@require_admin

@app.route('/api/announcements',methods=['POST'])@require_admindefcreate_announcement():# ...db.session.add(announcement)db.session.commit()returnjsonify({'success':True,'item':_announcement_to_dict(announcement)})

6.5 球友论坛问答(提问/答疑)

论坛接口包括:

  • GET /api/forum/questions:问题列表(分页 + 回复数)
  • POST /api/forum/questions:发布提问
  • GET /api/forum/questions/<id>:详情 + 回答列表
  • POST /api/forum/questions/<id>/answers:发布回答

列表接口中通过 SQL 聚合统计answer_count(用于列表显示“xx 回复”):

文件:backend/app.py

rows=(db.session.query(ForumAnswer.question_id,func.count(ForumAnswer.id)).filter(ForumAnswer.question_id.in_(ids)).group_by(ForumAnswer.question_id).all())counts={qid:int(cnt)forqid,cntinrows}

前端发起提问(带上author_id/author_role):

文件:frontend/src/views/Forum.vue

constresponse=awaitaxios.post('/api/forum/questions',{title,content,author_id:userStore.userId,author_role:userStore.role})

前端发布回答:

文件:frontend/src/views/ForumDetail.vue

constresponse=awaitaxios.post(`/api/forum/questions/${questionId}/answers`,{content,author_id:userStore.userId,author_role:userStore.role})

7. 路由与登录拦截(个人中心示例)

个人中心需要登录后访问,使用meta.requiresAuth+beforeEach做拦截:

文件:frontend/src/router/index.js

{path:'/profile',name:'Profile',component:Profile,meta:{requiresAuth:true}}router.beforeEach((to,from,next)=>{if(to.meta?.requiresAuth){constisLoggedIn=!!localStorage.getItem('userId')if(!isLoggedIn){next({path:'/login',query:{redirect:to.fullPath}})return}}next()})

8. 可优化点

  • 论坛增强:点赞、采纳最佳答案、搜索、标签、分页加载
  • 推荐升级:引入“历史评价/行为”,做协同过滤或内容推荐
  • 管理后台:用户管理、数据统计、敏感词过滤等

如果你也想要一个“选拍更直观 + 有球友讨论氛围”的小系统,欢迎交流,我也可以继续把功能打磨得更完整的选购辅助小工具。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/14 9:03:45

CFR Java反编译工具完整教程:从字节码到可读源码的终极指南

CFR Java反编译工具完整教程&#xff1a;从字节码到可读源码的终极指南 【免费下载链接】cfr This is the public repository for the CFR Java decompiler 项目地址: https://gitcode.com/gh_mirrors/cf/cfr 在现代Java开发领域&#xff0c;字节码解析技术已成为开发者…

作者头像 李华
网站建设 2026/4/13 22:13:18

MouseClick:5分钟上手智能鼠标连点器,彻底告别重复点击疲劳

还在为游戏刷怪、办公点击、数据录入等重复性鼠标操作感到疲惫吗&#xff1f;MouseClick鼠标连点器正是你需要的效率助手&#xff01;这款基于Qt6开发的跨平台工具&#xff0c;通过智能模拟鼠标点击行为&#xff0c;让繁琐操作变得轻松简单。无论是游戏升级、批量处理还是软件测…

作者头像 李华
网站建设 2026/4/15 4:37:27

3分钟搞定PT助手Plus:浏览器一键下载种子的终极指南

3分钟搞定PT助手Plus&#xff1a;浏览器一键下载种子的终极指南 【免费下载链接】PT-Plugin-Plus PT 助手 Plus&#xff0c;为 Microsoft Edge、Google Chrome、Firefox 浏览器插件&#xff08;Web Extensions&#xff09;&#xff0c;主要用于辅助下载 PT 站的种子。 项目地…

作者头像 李华
网站建设 2026/4/15 3:54:07

3分钟搞定B站缓存合并:告别碎片化视频的终极解决方案

3分钟搞定B站缓存合并&#xff1a;告别碎片化视频的终极解决方案 【免费下载链接】BilibiliCacheVideoMerge 项目地址: https://gitcode.com/gh_mirrors/bi/BilibiliCacheVideoMerge 你是否曾经遇到过这样的情况&#xff1a;在B站缓存了喜欢的视频准备离线观看&#xf…

作者头像 李华
网站建设 2026/4/11 9:14:43

5步构建高效Aria2下载系统:从零配置到云存储一体化

5步构建高效Aria2下载系统&#xff1a;从零配置到云存储一体化 【免费下载链接】aria2.conf Aria2 配置文件 | OneDrive & Google Drvive 离线下载 | 百度网盘转存 项目地址: https://gitcode.com/gh_mirrors/ar/aria2.conf 在当今数字资源爆炸的时代&#xff0c;传…

作者头像 李华
网站建设 2026/4/15 10:30:17

三步突破百度网盘下载限制:Python解析工具实战指南

三步突破百度网盘下载限制&#xff1a;Python解析工具实战指南 【免费下载链接】baidu-wangpan-parse 获取百度网盘分享文件的下载地址 项目地址: https://gitcode.com/gh_mirrors/ba/baidu-wangpan-parse 还在为百度网盘龟速下载而烦恼吗&#xff1f;&#x1f629; 当你…

作者头像 李华