前端路由跳转与调用API

06-01 1162阅读

在上一篇文章中我们已经搭建了一个基础的vue前端页面,接下来我们就完成一个网站的核心:路由跳转实现页面切换,调用API实现前端与后端的互通。在这我声明我只提供思路,由于我使用了一些额外的库和插件,直接用我的代码可能会报错,如果你有问题可以在评论区留言,我们共同探讨共同进步。

一.路由跳转

在日常使用网站时当我们点击某一个按钮或文字后网页界面就行了切换,这就是由路由跳转实现的。而要实现这种路由跳转功能我们需要进行一下几步,首先需要安装和配置路由,可以使用npm和yarn包进行安装,安装命令如下:

npm install vue-router

安装完成后,需要创建一个路由模块,在该模块中定义路由规则,包括路由路径、对应的组件以及其他相关配置。如图所示

前端路由跳转与调用API

接下来我们需要定义我们的路由组件,为每个需要进行路由跳转的页面创建相应的 Vue 组件。这些组件将在路由匹配时被渲染到页面上。也就是图中的components文件夹下,如图所示我定义了一个反馈的组件

前端路由跳转与调用API

定义好组件后我们需要将其在index.js中导入并进行路由配置,代码如下

import { createRouter, createWebHistory } from 'vue-router';
import Feedblack from '../components/FeedBlack.vue';
// 定义路由分组
const homeRoutes = [
  {
    path: '/',
    redirect: '/Home'
  },
];
// 合并所有路由
const routes = [...homeRoutes, ...otherRoutes];
const router = createRouter({
  history: createWebHistory(),
  routes
});
// 全局错误处理
router.onError((error) => {
  console.error('路由错误:', error);
  // 可以在这里进行错误提示或跳转到错误页面
});
export default router; 

之后我们需要将定义好的路由模块在 Vue 应用中进行全局注册,使路由功能能够在整个应用中生效。在main.js文件中进行注册和实例化。代码如下:

import { createApp } from "vue";
import App from './App.vue';
import router from './router';
// 导入Vuetify的样式
import 'vuetify/styles';
import * as components from 'vuetify/components';
import * as directives from 'vuetify/directives';
import { createVuetify } from 'vuetify';
// 创建Vuetify实例
const vuetify = createVuetify({
    components,
    directives
});
const app = createApp(App);
app.use(router);
// 应用Vuetify
app.use(vuetify);
app.mount('#app');

我这里使用了vuetify,这是一个很不错的ui,可以节省我们的很多工作。如果你也想使用该ui,在vue项目根目录下使用一下命令进行安装

最后我们需要在home页面中定义反馈按钮并定义好相应的方法,当我们点击反馈按钮后可以进行相应的路由跳转。代码如下

    
    
        
        
            
            
                反馈
            
        
    


// 导入 ref 创建响应式数据
import { ref } from 'vue';
// 导入 useRouter 获取路由实例
import { useRouter } from 'vue-router';
// 获取路由实例
const router = useRouter();
// 点击反馈导航,跳转到 FeedBlack 路由
const feedblack = () => {
    router.push({ name: 'FeedBlack' });
};


/* 设置文本字体大小 */
.text-size {
    font-size: 15px;
}

在代码中使用了vuetify提供的导航栏,在该导航栏上定义了一个反馈按钮

   反馈

当我们点击该按钮时,触发定义的方法跳转到我们定义的反馈组件

// 点击反馈导航,跳转到 FeedBlack 路由
const feedblack = () => {
    router.push({ name: 'FeedBlack' });
};

效果如图所示

前端路由跳转与调用API前端路由跳转与调用API

二.API调用

接下来我们聊下API调用,通过调用我们定义的api就可以实现前端与后端相通,例如上图的反馈页面,我们通过调用api可以将我们输入的问题和姓名发送给后端,后端将这个数据写入数据库或者写入某个excel表中,我们可以查看这些数据看使用者遇到的问题。

那么我们如何定义api和调用api呢?

一.后端定义api以及处理函数

api的定义在后端完成,可以使用flask或Django框架,我这里使用的是flask框架,代码如下

import logging
import traceback
from flask import Flask, send_file, jsonify, request
from flask_cors import CORS
from datetime import datetime
import mysql.connector
import pandas as pd
# 配置日志记录,增加日志输出到文件
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s',
                    handlers=[logging.StreamHandler(sys.stdout), logging.FileHandler('app.log')])
def register_feedblack_routes(app):
    @app.route('/api/feedback', methods=['POST'])
    def feedback_run():
        try:
            # 获取当前时间
            date = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
            # 获取请求表单中的问题和姓名
            problem = request.form.get('problem')
            name = request.form.get('name')
            if not problem or not name:
                return "问题和姓名不能为空", 400
            path = r'D:\project\问题反馈\问题反馈.xlsx'
            df = pd.read_excel(path)
            new_row = {'姓名': name, '日期': date, '问题': problem}
            df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
            df.to_excel(path, index=False)
            # 连接到 MySQL 数据库
            mydb = mysql.connector.connect(
                host="localhost",
                user="root",
                password="1234xhj",
                database="shopdict"
            )
            # 创建游标
            mycursor = mydb.cursor()
            sheetname = '反馈问题'
            # 检查表是否存在
            check_table_query = f"SHOW TABLES LIKE '{sheetname}'"
            mycursor.execute(check_table_query)
            table_exists = mycursor.fetchone()
            if not table_exists:
                return f"表 {sheetname} 不存在,无法插入数据。", 404
            # 插入数据
            insert_query = f"INSERT INTO `{sheetname}` (姓名, 日期, 问题) VALUES (%s, %s, %s)"
            values = (name, date, problem)
            mycursor.execute(insert_query, values)
            # 提交更改
            mydb.commit()
            return "反馈提交成功", 200
        except mysql.connector.Error as err:
            logging.error(f"/api/feedback 插入数据时出错: {err}")
            return "数据库操作出错,请稍后再试", 500
        except Exception as e:
            logging.error(f"/api/feedback 发生未知错误: {e}")
            return "发生未知错误,请稍后再试", 500
        finally:
            if 'mycursor' in locals():
                mycursor.close()
            if 'mydb' in locals():
                mydb.close()
    return app
def create_app():
    app = Flask(__name__)
    CORS(app, origins="*", methods=["GET", "POST", "PUT", "DELETE"], allow_headers=["Content-Type"])
    #注册反馈模块的路由
    app = register_feedblack_routes(app)
    return app
if __name__ == '__main__':
    app = create_app()
    app.run(host='127.0.0.1', debug=True)

这个系统主要由 Flask 框架驱动,用户通过 POST 请求将反馈信息(姓名和问题描述)提交到 /api/feedback 接口。系统会将这些信息同时保存到本地的 Excel 文件和 MySQL 数据库中,并且在处理过程中会对各种可能出现的错误进行捕获和处理。

from flask import Flask, send_file, jsonify, request
from flask_cors import CORS
from datetime import datetime
import mysql.connector
import pandas as pd

这里导入了多个必要的模块:

Flask 相关的模块用于创建 Web 应用、处理请求和响应。

Flask - CORS 用于解决跨域请求的问题,允许不同域名的页面向我们的接口发送请求。

datetime 模块用于获取当前的时间,方便记录反馈提交的时间。

mysql.connector 用于连接和操作 MySQL 数据库。

pandas 用于读取和写入 Excel 文件。

def register_feedblack_routes(app):
    @app.route('/api/feedback', methods=['POST'])
    def feedback_run():
        # 具体处理逻辑
        pass
    return app

register_feedblack_routes 函数接收一个 Flask 应用实例作为参数,使用 @app.route 装饰器注册了一个 /api/feedback 的 POST 请求路由。当有 POST 请求发送到这个接口时,会调用 feedback_run 函数进行处理。

date = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')
problem = request.form.get('problem')
name = request.form.get('name')
if not problem or not name:
    return "问题和姓名不能为空", 400

首先,使用 datetime.now() 获取当前时间,并将其格式化为 YYYY - MM - DD HH:MM:SS.ffffff 的字符串。然后,从请求表单中获取用户提交的问题和姓名。如果问题或姓名为空,会返回一个错误信息和 400 状态码。

path = r'D:\project\问题反馈\问题反馈.xlsx'
df = pd.read_excel(path)
new_row = {'姓名': name, '日期': date, '问题': problem}
df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
df.to_excel(path, index=False)

读取指定路径下的 Excel 文件,将其存储为一个 Pandas 的 DataFrame 对象。创建一个包含新反馈信息的字典,将其转换为 DataFrame 并与原 DataFrame 进行拼接。最后,将更新后的 DataFrame 保存回原 Excel 文件,不保存行索引。

mydb = mysql.connector.connect(
    host="localhost",
    user="root",
    password="1234xhj",
    database="shopdict"
)
mycursor = mydb.cursor()
sheetname = '反馈问题'
check_table_query = f"SHOW TABLES LIKE '{sheetname}'"
mycursor.execute(check_table_query)
table_exists = mycursor.fetchone()
if not table_exists:
    return f"表 {sheetname} 不存在,无法插入数据。", 404
insert_query = f"INSERT INTO `{sheetname}` (姓名, 日期, 问题) VALUES (%s, %s, %s)"
values = (name, date, problem)
mycursor.execute(insert_query, values)
mydb.commit()

使用 mysql.connector 连接到本地的 MySQL 数据库,创建一个游标对象。检查指定的表是否存在,如果不存在,返回一个错误信息和 404 状态码。如果表存在,使用 SQL 插入语句将反馈信息插入到表中,并提交更改。

except mysql.connector.Error as err:
    logging.error(f"/api/feedback 插入数据时出错: {err}")
    return "数据库操作出错,请稍后再试", 500
except Exception as e:
    logging.error(f"/api/feedback 发生未知错误: {e}")
    return "发生未知错误,请稍后再试", 500
finally:
    if 'mycursor' in locals():
        mycursor.close()
    if 'mydb' in locals():
        mydb.close()

使用 try - except 块捕获可能出现的数据库错误和其他未知错误,并记录错误日志。无论是否发生错误,最后都会关闭数据库游标和连接,释放资源。

def create_app():
    app = Flask(__name__)
    CORS(app, origins="*", methods=["GET", "POST", "PUT", "DELETE"], allow_headers=["Content-Type"])
    app = register_feedblack_routes(app)
    return app

create_app 函数创建一个 Flask 应用实例,使用 CORS 处理跨域请求,允许所有来源的请求,支持多种 HTTP 方法和 Content - Type 请求头。调用 register_feedblack_routes 函数注册反馈处理路由,最后返回应用实例。

if __name__ == '__main__':
    app = create_app()
    app.run(host='127.0.0.1', debug=True)

当脚本作为主程序运行时,调用 create_app 函数创建应用实例,并启动 Flask 应用,监听本地回环地址 127.0.0.1,开启调试模式。

二.前端页面搭建以及调用api

    
        
            
        首页
      
      
            个人程序
        
        
            数据
        
        
            反馈
        
        
        
            
                
                    
                        
                            意见反馈
                        
                        
                    
                    
                        
                            请留下您在使用过程中遇到的问题或提出宝贵的建议
                        
                    
                    
                        
                            
                                
                            
                            
                                
                            
                            
                                确定
                                
                            
                        
                    
                
            
        
    


import {ref} from 'vue'
import {useRouter} from 'vue-router'
import axios from 'axios'
const router = useRouter()
const problem = ref('')
const name = ref('')
const showmakesure = ref(false)
const Home = () => {
    router.push({
        name: 'Home'
    })
}
const personalProgram = () => {
    router.push({
        name: 'DepartmentList'
    })
}
const feedblack = () => {
    router.push({ name: 'FeedBlack' })
}
const data = () => {
    router.push({ name: 'DataHome' })
}
const makesure = async() =>{
    if(!problem){
        console.error('未输入问题')
        return
    }
    if(!name){
        console.error('未输入名字')
        return
    }
    showmakesure.value = true
    const formData = new FormData()
    formData.append('problem',problem.value)
    formData.append('name',name.value)
    try{
        const response = await axios.post('http://129.28.127.103:80/api/feedback',formData,{
        responseType:'blob'
        })
    }catch(error){
        console.error('Error',error)
    }finally{
        showmakesure.value = false
    }
    
}


.sidebar {
  position: fixed;
  top: 0;
  left: 0;
  height: 100vh;
  z-index: 1000;
  margin: 0;
  padding: 0;
  border-radius: 10px 0 0 10px;
  /* 调整侧边栏顶部间距,避免与 app-bar 重叠 */
  padding-top: 64px; 
}
.problem-description {
  display: block;
  margin-bottom: 100px; /* 调整此值以改变间距 */
}
.main-content {
    margin-left: 0px;
}
.grey-bg {
    background-color: #f0f0f0; /* 灰色背景 */
    min-height: 100vh;
    padding: 20px;
}
.white-box {
    background-color: #fff; /* 白色背景 */
    border: 1px solid #e0e0e0; /* 浅灰色边框 */
    border-radius: 4px; /* 圆角 */
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); /* 轻微阴影 */
    padding: 20px;
}
span {
  cursor: pointer; /* 添加这行代码使鼠标悬停时变为手型 */
}
.bold-text {
  font-size: 22px;
  font-weight: bold;
}
.loading-spinner {
    display: inline-block;
    width: 20px;
    height: 20px;
    border: 2px solid rgba(0, 0, 0, 0.1);
    border-top-color: #007bff;
    border-radius: 50%;
    animation: spin 1s ease-in-out infinite;
    margin-left: 5px;
    vertical-align: middle;
}
.text-size {
    font-size: 15px;
}

这个页面基于 Vuetify 框架,使用了作为应用的根容器,包含一个顶部导航栏和主要内容区域。顶部导航栏中有多个,每个标题内有一个元素,用于显示不同的导航链接,如 “首页”、“个人程序”、“数据” 和 “反馈”。主要内容区域则是一个包含意见反馈表单的布局。

    
        
            
                首页
            
            
                个人程序
            
            
                数据
            
            
                反馈
            
        
        
            
                
                    
                        
                            意见反馈
                        
                    
                    
                        
                            请留下您在使用过程中遇到的问题或提出宝贵的建议
                        
                    
                    
                        
                            
                                
                            
                            
                                
                            
                            
                                确定
                                
                            
                        
                    
                
            
        
    

顶部导航栏:每个元素都绑定了一个点击事件,分别对应Home、personalProgram、data和feedblack方法,用于实现页面的路由跳转。

主要内容区域:

  • 使用v-row和v-col组件进行布局,v-row表示一行,v-col表示一列。
  • 包含一个标题 “意见反馈” 和一段提示文字 “请留下您在使用过程中遇到的问题或提出宝贵的建议”。
  • 有两个文本输入框,分别使用v-model指令绑定到problem和name响应式数据,用于用户输入问题描述和名字。
  • 有一个按钮,点击时触发makesure方法,并且通过:disabled属性绑定showmakesure响应式数据,用于控制按钮的禁用状态。当showmakesure为true时,会显示一个加载指示器(元素,带有loading-spinner类)。
    import {ref} from 'vue'
    import {useRouter} from 'vue-router'
    import axios from 'axios'
    const router = useRouter()
    const problem = ref('')
    const name = ref('')
    const showmakesure = ref(false)
    const Home = () => {
        router.push({
            name: 'Home'
        })
    }
    const personalProgram = () => {
        router.push({
            name: 'DepartmentList'
        })
    }
    const feedblack = () => {
        router.push({ name: 'FeedBlack' })
    }
    const data = () => {
        router.push({ name: 'DataHome' })
    }
    const makesure = async() =>{
        if(!problem.value){
            console.error('未输入问题')
            return
        }
        if(!name.value){
            console.error('未输入名字')
            return
        }
        showmakesure.value = true
        const formData = new FormData()
        formData.append('problem',problem.value)
        formData.append('name',name.value)
        try{
            const response = await axios.post('http://129.28.127.103:80/api/feedback',formData,{
            responseType:'blob'
            })
        }catch(error){
            console.error('Error',error)
        }finally{
            showmakesure.value = false
        }
        
    }
    
    • 导入必要的模块:从vue导入ref用于创建响应式数据,从vue-router导入useRouter用于获取路由实例,从axios导入用于发送 HTTP 请求。
    • 定义响应式数据:problem和name分别用于存储用户输入的问题描述和名字,showmakesure用于控制 “确定” 按钮的禁用状态和加载指示器的显示。
    • 定义路由跳转方法:Home、personalProgram、feedblack和data方法分别用于跳转到不同的路由页面,通过router.push方法实现。
    • makesure方法:
      • 首先检查problem和name是否有值,如果没有则在控制台输出错误信息并返回。
      • 将showmakesure设置为true,禁用按钮并显示加载指示器。
      • 创建一个FormData对象,将用户输入的问题和名字添加到其中。
      • 使用axios.post方法发送 POST 请求到指定的 API 地址http://129.28.127.103:80/api/feedback,并设置responseType为blob。
      • 捕获请求过程中可能出现的错误并在控制台输出。
      • 无论请求是否成功,最后将showmakesure设置为false,启用按钮并隐藏加载指示器。
        .sidebar {
          position: fixed;
          top: 0;
          left: 0;
          height: 100vh;
          z-index: 1000;
          margin: 0;
          padding: 0;
          border-radius: 10px 0 0 10px;
          /* 调整侧边栏顶部间距,避免与 app-bar 重叠 */
          padding-top: 64px; 
        }
        .problem-description {
          display: block;
          margin-bottom: 100px; /* 调整此值以改变间距 */
        }
        .main-content {
            margin-left: 0px;
        }
        .grey-bg {
            background-color: #f0f0f0; /* 灰色背景 */
            min-height: 100vh;
            padding: 20px;
        }
        .white-box {
            background-color: #fff; /* 白色背景 */
            border: 1px solid #e0e0e0; /* 浅灰色边框 */
            border-radius: 4px; /* 圆角 */
            box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); /* 轻微阴影 */
            padding: 20px;
        }
        span {
          cursor: pointer; /* 添加这行代码使鼠标悬停时变为手型 */
        }
        .bold-text {
          font-size: 22px;
          font-weight: bold;
        }
        .loading-spinner {
            display: inline-block;
            width: 20px;
            height: 20px;
            border: 2px solid rgba(0, 0, 0, 0.1);
            border-top-color: #007bff;
            border-radius: 50%;
            animation: spin 1s ease-in-out infinite;
            margin-left: 5px;
            vertical-align: middle;
        }
        .text-size {
            font-size: 15px;
        }
        

        这里定义了一些样式类,用于设置页面的布局和外观:

        • .sidebar类用于设置侧边栏的样式,包括固定位置、高度、边框半径等。
        • .problem-description类用于设置问题描述的样式,这里设置了下外边距。
        • .main-content类用于设置主要内容区域的样式,这里设置了左边距。
        • .grey-bg类用于设置灰色背景区域的样式。
        • .white-box类用于设置白色盒子的样式,包括背景颜色、边框、圆角和阴影。
        • .span类设置了鼠标悬停时的样式,使其变为手型。
        • .bold-text类用于设置粗体文本的样式。
        • .loading-spinner类用于设置加载指示器的样式,通过动画实现旋转效果。
        • .text-size类用于设置文本的字体大小。

          最后效果如图所示

          前端路由跳转与调用API

          三.结尾

          通过以上的代码和思路。我们实现了用户输入反馈信息并提交到服务器的功能,同时提供了友好的界面和交互体验。希望这篇博客能帮助你更好地理解 Vue3+Vuetify+flask框架在实际项目中的应用。如果你有任何疑问或建议,欢迎留言讨论。

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码