day05 Router、vuex、axios

配置

router和vuex需要在创建vue项目的时候,开始的时候选择Manually select features,于是就可以在下一个创建配置讯问中选择router和vuex。

axios则需要执行命令行:

npm install axios -S

之后再在需要发送请求的view导入即可。

router实现左边点击右边打开

首先需要安装ElementUI,方法见day4 vue2以及ElementUI-CSDN博客

在App.vue中导入框架,将<nav>、<router-view>标签移动到对应位置。其中to配置相当于servlet请求的路径。

<template>
  <div id="app">
    <el-container>
      <el-header>欢迎你</el-header>
      <el-container>
        <el-aside width="200px">
          <nav>
            <ul>
              <li><router-link to="/">Home</router-link></li>
              <li><router-link to="/about">About</router-link></li>
              <li><router-link to="/question">问题管理</router-link></li>
              <li><router-link to="/new">新页面</router-link></li>
            </ul>
          </nav>
        </el-aside>
        <el-main><router-view/></el-main>
      </el-container>
    </el-container>
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

nav {
  padding: 30px;
}

nav a {
  font-weight: bold;
  color: #2c3e50;
}

nav a.router-link-exact-active {
  color: #42b983;
}
</style>

并在router的 index.js中配置请求路径对应的view们,相当于web.xml。其中有两种方式导入view,第一种可以直接开头import,在routers中的component中就只用写出你的模块名即可,这是静态导入,其实相当于js中的include编译指令,开始就导入自然加载速度会变快,但是动态导入往往常见一些,就比如这里可以直接在component中 ()=>import ('路径'),这样是动态导入,请求该view的时候才会加载,更灵活。

import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
// 路由的配置
Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView
  },
  {
    path: '/about',
    name: 'about',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  },
  {
    path:'/question',
    name:'question',
    component:() => import(/* webpackChunkName: "about" */ '../views/QuestionView.vue')
  },
  {
    path:'/new',
    name:'new',
    component:() => import(/* webpackChunkName: "about" */ '../views/NewView.vue')
  }
]
// js文件中导出一个 router 实例
const router = new VueRouter({
  routes
})

export default router

使用vuex处理用户的动作

vuex主要往外导出五个属性,在store的index.js中,state用于储存页面共享的数据,actions用于处理发出的动作,mutations用于直接操纵state中的数据,getters中对state中的数据进行再次加工,类似vue中计算属性computed。

就如图片中一样,view中用dispatch发出动作,actions使用commit将动作处理,转给mutations对state进行直接操作,前端可以直接调用state中的数据,来实现数据更新。getters的处理则直接可以前端执行{{$store.getters.方法名}}。

 store中的index.js:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {// 共享数据
    count: 0, // 初值为零
    myNewList:[]
  },
  getters: { // 对state中的数据进行再次加工,类似vue中计算属性computed
    update(state) {
      return state.count * 10;
    }
  },
  mutations: { // 修改state数据
    change(state, value) {
      state.count += value;
    },
    sub(state, value) {
      state.count -= value;
    }
  },
  actions: { // 相应动作处理
    // 必须与dispatch中的时间名一致
    sum(context, value) {
      context.commit('change', value);
    },
    sub(context, value) {
      context.commit('sub', value);
    }
  },
  modules: { // 分模块化开发,一个模块可以有自己的state,getters,mutations,actions
  }
})

前端调用:

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <h2>当前计算的和为:{{$store.state.count}}</h2>
    <h2>当前计算的和 ✖ 10 为:{{$store.getters.update}}</h2>
    <el-button @click="add" type="primary" round>我猜你不会点击我</el-button>
    <br/><br/>
    <el-button @click="sub" type="primary" round>我可以回去哦</el-button>
  </div>
</template>
<script>
export default{
  name: "AboutView",
  methods:{
    // 求和时间处理
    add(){
      // 进行dispatch
      this.$store.dispatch("sum",5);
    },
    sub(){
      this.$store.dispatch("sub",10);
    }
  }
}
</script>

使用axios实现前后端连接

其原理是可以使用axios访问不同端口,向不同端口发送请求,有两种方式发送请求:

可以直接在view中导入axios包,直接发送请求,但是因为请求的地址往往容易变化,所以需要用第二种方式来发送请求,首先在util包中创建js页面配置baseUrl(在此导入axios包),也就是端口号,然后在api包中创建针对不同view的不同请求url,也就是请求的具体地址和请求方法以及可能的参数,将方法配置可以其他文件访问(export default),这时候就需要将配置好的js文件直接导入到view中,然后再调用方法即可。

url在view中(第一种):

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
    <HelloWorld msg="Welcome to Your Vue.js App" />
    <br/>
    <el-button @click="sendRequest()" type="primary">发送axios请求,进行调用springboot项目</el-button>
    <br/><br/><br/>
    <el-table :data="this.$store.state.myNewList" border style="width: 100%">
      <el-table-column fixed prop="id" label="编号" width="150">
      </el-table-column>
      <el-table-column prop="expertName" label="专家姓名" width="120"> </el-table-column>
      <el-table-column prop="questioner" label="提问人" width="120">
      </el-table-column>
      <el-table-column prop="phone" label="电话" width="120"> </el-table-column>
      <el-table-column prop="plantName" label="农作物名称" width="300">
      </el-table-column>
      <el-table-column prop="question" label="问题" width="120"> </el-table-column>
      <el-table-column prop="answer" label="回答" width="120"> </el-table-column>
      <el-table-column prop="status" label="状态" width="120"> </el-table-column>
      <el-table-column fixed="right" label="操作" width="100">
        <template slot-scope="scope">
          <el-button @click="handleClick(scope.row)" type="text" size="small"
            >查看</el-button
          >
          <el-button type="text" size="small">编辑</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
// @ is an alias to /src
import HelloWorld from "@/components/HelloWorld.vue";
import axios from "axios"; // 引入axios库

export default {
  name: "HomeView",
  components: {
    HelloWorld,
  },
  methods: {
    sendRequest() {
      axios
        .get("http://localhost:8888/question/findAll")
        .then(response => {
          this.questonList = response.data.data;
          this.$store.state.myNewList = response.data.data;
        });
    },
  },
  data(){
    return {
      questonList: []
    }
  }
};
</script>

url在js文件中(第二种):

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
    <br />
    <el-button @click="search" type="primary">search</el-button>
    <el-button @click="add()" type="primary">add</el-button>
    <br /><br /><br />
    <el-input v-model="inputValue" placeholder="请输入id或专家姓名"></el-input>
    <br /><br />
    <el-table :data="questionList" border style="width: 100%">
      <el-table-column fixed prop="id" label="编号" width="150">
      </el-table-column>
      <el-table-column prop="expertName" label="专家姓名" width="120">
      </el-table-column>
      <el-table-column prop="questioner" label="提问人" width="120">
      </el-table-column>
      <el-table-column prop="phone" label="电话" width="120"> </el-table-column>
      <el-table-column prop="plantName" label="农作物名称" width="300">
      </el-table-column>
      <el-table-column prop="question" label="问题" width="120">
      </el-table-column>
      <el-table-column prop="answer" label="回答" width="120">
      </el-table-column>
      <el-table-column prop="status" label="状态" width="120">
      </el-table-column>
      <el-table-column fixed="right" label="操作" width="100">
        <template slot-scope="scope">
          <el-button @click="handleClick(scope.row)" type="text" size="small"
            >删除</el-button
          >
          <el-button type="text" size="small">编辑</el-button>
        </template>
      </el-table-column>
    </el-table>
   
  </div>
</template>

<script>
// @ is an alias to /src
import queApi from "@/api/question"; // 导入封装后的axios请求,具体见src/api/question.js

export default {
  name: "HomeView",
  methods: {
    sendRequest() {
      queApi.getQuestionList().then((res) => {
        this.questionList = res.data.data;
        this.$store.state.myNewList = this.questionList; // 数据共享
      });
    }
  },
  data() {
    return {
      questionList: [],
      inputValue: "",
      
    };
  },
};
</script>

api文件中配置:

import request from '../utils/request'; // 导入axios实例

/**
 * 调用boot端,进行/question/findAll查询
 * @returns {Promise}
 */
function getQuestionList() { // 获取问题列表
    return request({
        url: '/question/findAll',
        method: 'get'

    });
}

export default {
    getQuestionList, // 导出函数
} // 导出对象

除了findAll方法之外,还可以其他方法:

后端

注意axios支持get、post、put、delete等请求,所以可以直接按照swagger的请求规范做,还有一点要注意,需要配置后端可以接受并处理其他端口发出的各种请求,也就需要配置CorsConfig文件:

package com.zheng.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 添加映射路径
        registry.addMapping("/**")
               // .allowedOrigins("*") //
                .allowedOriginPatterns("*") //允许哪些域的请求,星号代表允许所有
                .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE") // 允许的方法
                .allowedHeaders("*") // 允许的头部设置
                .allowCredentials(true) // 是否发送cookie
                .maxAge(168000); // 预检间隔时间
    }


}

除此之外,如果发送的是post、put请求,也就是要将传递的参数放到请求体中的,需要在获得形参之前加@RequestBody,才能将传递的参数和需要的参数一一对应。

package com.zheng.controller;

import com.zheng.entity.Question;
import com.zheng.model.Result;
import com.zheng.service.QuestionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * Controller整合Swagger
 */
@RestController
@RequestMapping("/question")
@Tag(name="question",description = "tb_question控制层") // swagger标签
public class QuestionController {
    @Autowired
    private QuestionService questionService;

    @Operation(description = "question查询全部") // swagger 标签
    @GetMapping("/findAll")
//    public List<Question> findAll() {
//        return questionService.findAll();
//    }
    public Result<List<Question>> findAll() {
        return Result.ok(questionService.findAll());
    }
    /**
     * findById?id=10
     * @param id
     * @return
     */
    @Operation(description = "question根据主键查询") // swagger 标签
    @GetMapping("findById")
//    public Question findById(@RequestParam("id") int id) {
//        return questionService.findById(id);
//    }
    public Result<Question> findById(@RequestParam("id") int id) {
        return Result.ok(questionService.findById(id));
    }

    @Operation(description = "根据专家名字查询")
    @GetMapping("/findByExpertName/{expertName}")
    public Result<List<Question>> findByExpertName(@PathVariable("expertName") String expertName) {
        return Result.ok(questionService.findByExpertName(expertName));
    }
    @PostMapping("/save")
    @Operation(description = "添加question")
    public Result<Integer> save(@RequestBody Question question) {
        return Result.ok(questionService.save(question));
    }
    @PutMapping("/update") // 修改只能用put请求,删除只能用delete请求
    @Operation(description = "修改question")
    public Result<Integer> update(Question question) {
        return Result.ok(questionService.update(question));
    }

    /**
     * /delete/10
     * @param id
     * @return
     */
    @DeleteMapping("/delete/{qid}")
    @Operation(description = "按照编号删除question")
    public Result<Integer> delete(@PathVariable("qid") int id) {
        return Result.ok(questionService.delete(id));
    }
}

一个好习惯是将返回的数据封装成一个Result<T>类(这也相当于一个特殊的实体类)来处理,这样当面对不同的数据类型的时候才能正常处理:

package com.zheng.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Result<T> {
    private Integer code; // 编码 ;200 404 500
    private String msg; // 消息内容
    private T data; // 真正的数据

    public Result(Integer code, T data) {
        this.code = code;
        this.data = data;
    }
    public Result(String msg, T data) {
        this.msg = msg;
        this.data = data;
    }
    public Result(T data){
        this.data = data;
    }

    public static<T> Result<T> ok(T data){
        return new Result(200,"success",data);
    }

    public static<T> Result<T> fail(Integer code){
        return new Result(500,"fail",null);
    }
}

前端

对于前端如何链接这么多方法呢:

如果是get、delete请求并将传递的参数直接拼接在路径中,而非键值对的形式,那么在前端也可以直接拼接在url中,不能使用参数params,否则404:

function findByExpertName(expertName) {
    return request({
        url:'/question/findByExpertName/' + expertName,
        method:'get'
    })
}

 对于post请求可以直接这样传递参数(在控制层是直接接受一个对象的类型,所以传入的也是一个对象类型,直接使用data列出要传递的参数即可),对于get、delete请求参数必须params,而post、put请求必须data传递数据:

function addQuestion(data) { // 新增问题
    return request({
        url: '/question/save',
        method: 'post', // post 请求执行添加操作
        data: data // 发送数据
    })
}

最后,做的小项目:实现findById以及findByExpertName以及add方法

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
    <br />
    <el-button @click="search" type="primary">search</el-button>
    <el-button @click="add()" type="primary">add</el-button>
    <br /><br /><br />
    <el-input v-model="inputValue" placeholder="请输入id或专家姓名"></el-input>
    <br /><br />
    <el-table :data="questionList" border style="width: 100%">
      <el-table-column fixed prop="id" label="编号" width="150">
      </el-table-column>
      <el-table-column prop="expertName" label="专家姓名" width="120">
      </el-table-column>
      <el-table-column prop="questioner" label="提问人" width="120">
      </el-table-column>
      <el-table-column prop="phone" label="电话" width="120"> </el-table-column>
      <el-table-column prop="plantName" label="农作物名称" width="300">
      </el-table-column>
      <el-table-column prop="question" label="问题" width="120">
      </el-table-column>
      <el-table-column prop="answer" label="回答" width="120">
      </el-table-column>
      <el-table-column prop="status" label="状态" width="120">
      </el-table-column>
      <el-table-column fixed="right" label="操作" width="100">
        <template slot-scope="scope">
          <el-button @click="handleClick(scope.row)" type="text" size="small"
            >删除</el-button
          >
          <el-button type="text" size="small">编辑</el-button>
        </template>
      </el-table-column>
    </el-table>
    <!--  添加的对话框-->
    <el-dialog title="问题信息" :visible.sync="dialogFormVisible">
      <el-form :model="form" :rules="rules" ref="questionForm">
        <el-form-item
          label="expertName"
          :label-width="formLabelWidth"
          prop="expertName"
        >
          <el-input v-model="form.expertName" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item
          label="questioner"
          :label-width="formLabelWidth"
          prop="questioner"
        >
          <el-input v-model="form.questioner" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="phone" :label-width="formLabelWidth" prop="phone">
          <el-input v-model="form.phone" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item
          label="plantName"
          :label-width="formLabelWidth"
          prop="plantName"
        >
          <el-input v-model="form.plantName" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item label="title" :label-width="formLabelWidth" prop="title">
          <el-input v-model="form.title" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item
          label="question"
          :label-width="formLabelWidth"
          prop="question"
        >
          <el-input v-model="form.question" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item
          label="answer"
          :label-width="formLabelWidth"
          prop="answer"
        >
          <el-input v-model="form.answer" autocomplete="off"></el-input>
        </el-form-item>
        <el-form-item
          label="status"
          :label-width="formLabelWidth"
          prop="status"
        >
          <el-input v-model="form.status" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="cancel">取 消</el-button>
        <el-button type="primary" @click="save">保 存</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
// @ is an alias to /src
import queApi from "@/api/question"; // 导入封装后的axios请求,具体见src/api/question.js

export default {
  name: "HomeView",
  methods: {
    sendRequest() {
      queApi.getQuestionList().then((res) => {
        this.questionList = res.data.data;
        this.$store.state.myNewList = this.questionList;
      });
    },
    isNumber(value) {
      return /^\d+$/.test(value) && value !== '';
    },
    search() {
      if (this.inputValue == "") {
        this.sendRequest();
      } else {
        if (this.isNumber(this.inputValue)) {
          this.findById();
        } else {
            this.findByExpertName();
        }
      }
    //   this.sendRequest();
    },
    findById() {
      queApi.findById(this.inputValue).then((res) => {
        this.question = res.data.data;
        if (this.question != null) {
          this.$set(this, "questionList", []); // 清空原有数据
          this.questionList.push(this.question);
        } else {
          alert("未查询到数据");
        }
      });
    },
    add() {
      this.dialogFormVisible = true;
    },
    cancel() {
      this.dialogFormVisible = false;
      this.form = {
        expertName: "",
        questioner: "",
        phone: "",
        plantName: "",
        title: "",
        question: "",
        answer: "",
        status: "",
      };
    },
    save() {
      // 验证表单
      this.$refs.questionForm.validate((validate) => {
        if (validate) {
          // 验证通过,可以提交数据
          //   alert(this.form.expertName);
          queApi.addQuestion(this.form).then((res) => {
            if (res.data.code == 200) {
              alert("添加了" + res.data.data + "条数据");
              this.sendRequest();
            } else {
              alert(res.data.msg);
            }
          });
          this.dialogFormVisible = false;
          this.form = {
            expertName: "",
            questioner: "",
            phone: "",
            plantName: "",
            title: "",
            question: "",
            answer: "",
            status: "",
          };
        }
      });
    },
    findByExpertName() {
      queApi.findByExpertName(this.inputValue).then((res) => {
        if (res.data.data == null) {
          alert("未查询到数据");
        } else {
          this.questionList = res.data.data;
        }
      });
    },
  },
  data() {
    return {
      questionList: [],
      question: {},
      inputValue: "",
      dialogFormVisible: false, // 控制添加的对话框是否可见
      form: {
        expertName: "",
        questioner: "",
        phone: "",
        plantName: "",
        title: "",
        question: "",
        answer: "",
        status: "",
      },
      formLabelWidth: "100px",
      rules: {
        expertName: [
          { required: true, message: "请输入专家姓名", trigger: "blur" },
        ],
        questioner: [
          { required: true, message: "请输入提问人", trigger: "blur" },
        ],
        phone: [
          { required: true, message: "请输入电话", trigger: "blur" },
          //   {
          //     pattern: /^1[34578]\d{9}$/,
          //     message: "请输入正确的手机号",
          //     trigger: "blur",
          //   },
        ],
        plantName: [
          { required: true, message: "请输入农作物名称", trigger: "blur" },
        ],
        title: [{ required: true, message: "请输入标题", trigger: "blur" }],
        question: [{ required: true, message: "请输入问题", trigger: "blur" }],
        answer: [{ required: true, message: "请输入回答", trigger: "blur" }],
        status: [{ required: true, message: "请输入状态", trigger: "blur" }],
      },
    };
  },
};
</script>

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值