Java:59-前后端项目接口联调

前后端项目接口联调

一般在前后端分离的项目中,后端人员需要提供后台接口,而前端人员提供前端项目(如Vue项目的Vue脚手架项目)
进行联调,都按照接口文档来进行操作项目
对于Vue脚手架来说,版本有时决定地址,就如你设置自动打开浏览器时,地址是0.0.0.0:8080,可以如下操作进行修改
//卸载当前脚手架
npm uninstall -g vue-cli
//安装指定版本脚手架
npm install -g @vue/cli@4.5.15
这样可能就会自动打开了,所以说,有时没有错,但符合的结果不对,可能是版本问题
js进行文件上传:
当使用js时,前台没有表单以及表单提交,或者不想用这个表单,只要js传递数据时,可以使用FormData对象,简称表单数据
FormData的主要用途有两个
将form表单元素的name与value进行组合,实现表单数据的序列化,从而减少表单元素的拼接
提高工作效率
异步上传文件
创建FormData对象
//通过FormData构造函数创建一个空对象
var formdata=new FormData();
//可以通过append()方法来追加数据
formdata.append("name","laotie");
//通过get方法对值进行读取
console.log(formdata.get("name"));//laotie
//通过set方法对值进行设置
formdata.set("name","laoliu");
console.log(formdata.get("name"));//laoliu
//data中创建FormData
    //5.创建FormData对象,将图片与表单一同上传
    this.params = new FormData();
//methods添加方法
    //文件上传 
    onchange(file) { //一个方法,参数一般自动赋值的,当然,若没有自动赋值的,一般认为是undefined,因为单纯的var a这个a就是undefined,当然,在后面会使用到,这里可以了解,在后面使用到的时候,就明白了
      //判断文件不为空
      if (file != null) {
        //将文件信息 保存到params中 
        this.params.append("file", file.raw, file.name);
     }
   }
前面我说过键值对不是数据,因为键值对使用[]是根据key来的,即使用[]只能获得对应key的值
前端的断点,需要加上debugger,类似于java断点,执行到这里,那么浏览器就会到检查的源代码的地方,进行调试
具体可以自己实验,实际上是浏览器的功能,即是浏览器发现这个代码而进行调试的,所以如果浏览器不进行断点调试
那么这个代码就无作用,与java是有所不同的,前者受浏览器限制,后者只在于自己,打了就可调试
最后使用脚手架时,由于基本上是js的文件的操作,所以查看源代码,基本上没有对应dom元素,只有在检查元素里才可查看的到
对应的参数数据,如data的值可以有多种,实际上前面说过的最后变成键值对中数组不会变成键值对(一般都是这样,数组怎么变成呢)
注意:使用Vue脚手架,若发现没有错误,结果也不对,那么手写一下,可能就会对
前端做的回显,一般是在一个组件里操作第一次回显的数据的
一般后台进行操作equals时,最好忽略大小写,因为前端的传参,可能与你测试的某些参数不一样
如application/json;charset=utf-8,后面的utf-8前端传递的可能是大写的
组件之间是替换的
从这里开始,进行联调(对应的初始项目地址在最后给出了)
下面的组件还没有在对应环境中操作,所以复制是运行不了的,最后会给出对应代码地址(需要一定的功底部署)
测试的组件:
<template>
  <!-- v-model的值,为当前框的值  label选项的标签名-->
  <el-select v-model="status" placeholder="请选择">
    <el-option v-for="item in Object.keys(statusMapping)" 
    :key="item" 
    :label="statusMapping[item]" 
    :value="item"></el-option>
    <!-- Object.keys(statusMapping)获取键值对的key数组

其中value必须在,否则不会显示,而我们绑定的v-model的值
其中找到对应的value的值的对应的那条数据可以是key或者直接的数据
若是key则将他的这条数据的其中label的对应值默认的显示出来
若是直接值,则value自己显示,但label若也是直接值,则显示label的值
所以若没有对应的value参数自然就是显示label,否则使用对应的参数

而for循环的则也是给value然后他给label来显示,与上面是一样的

所以value不可以不写

当没有对应的:label时,则value默认从循环后的数据里面操作key或者是直接的数据然后显示出来,而不是让label来显示了
    -->
  </el-select>
</template>
<script>
export default {
    data() {
      return {
        options: [{
          value: '选项1',
          label: '黄金糕'
        }],
        statusMapping : {
            0: "已隐藏",
            1: "待更新",
            2: "已更新"
    },
    status:"" //当成参数给label值对应,然后给出显示,若参数没有循环中存在,那么直接显示该参数
      }
    }
}
</script>

<style scoped>
</style>

<template>
  <el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick">
    <!-- 
            slot-scope:作用域插槽
            node:当前节点对象,这个可以不写,但写了就要写对应属性,否则就是空值,这是当然的
            即设置显示的值的
            data:当前数据对象,键值对存放
            每个树都有对应
    -->
    <span class="custom-tree-node" slot-scope="{ node, data }">
      <span>{{ data.section_name || data.theme}}</span><!-- 取出对应数据-->
      <!-- <span>级别:{{ node.level }}</span> -->
      <!-- node.level是级别等级,从1开始-->
    </span>
  </el-tree>
</template>
<script>
/*
data:展示数据
props:配置属性结构
 defaultProps: {
    children: 'children', 
    //指定生成子树的属性名称,即value的值决定是否有下一级,当children: 'children1'
    //加一个1,如果没有children1,那么没有下一级,其中key是固定的,不可改变,改变就不起作用了
    label: 'label'
    //指定当前树,不是子树
        }

*/
export default {
  data() {
    return {
      data: [
        {
          id: 5,
          course_id: 10,
          section_name: "麻式太极",
          description: "麻式太极拳,你动我试试",
          orderNum: 0,
          status: 2,
          create_time: "2019-07-11 10:55:10.0",
          update_time: "2019-10-09 12:43:01.0",
          isDel: 0,
          lessonList: [
            {
              id: 32,
              course_id: 10,
              section_id: 5,
              theme: "第一讲:如何给自己洗脑",
              duration: 10,
              is_free: 1,
              order_num: 1,
              status: 2,
              create_time: "2019-01-23 20:37:02.0",
              update_time: "2020-02-24 18:37:34.0",
              isDel: 0
            },
            {
              id: 33,
              course_id: 10,
              section_id: 5,
              theme: "第二讲:如何给别人洗脑",
              duration: 10,
              is_free: 1,
              order_num: 1,
              status: 2,
              create_time: "2019-01-23 20:37:02.0",
              update_time: "2020-02-24 18:37:34.0",
              isDel: 0
            }
          ]
        }
      ],
      defaultProps: {
        children:"lessonList",
        label: item => { 
            //第一个参数是节点的data键值对数值,因为data是节点的值
            //所以下面的item在不同的节点,是不同的,所以可以item.theme
            //每个树都是对应data和对应node
                return item.section_name || item.theme
        }
      }
    };
  },
  methods: {
    handleNodeClick(data) {
      console.log(data);
    }
  }
};
</script>
<style>
</style>

主组件:
<template>
  <section class="courses">
    <!-- 表单部分 -->
    <el-form class="actions" :inline="true" :model="filter">
      <el-form-item class="input-title" label="课程名称">
        <el-input v-model="filter.course_name" type="search" placeholder="课程名称"/>
      </el-form-item>

      <el-form-item label="状态">
        <el-select v-model="filter.status" placeholder="课程状态">
          <el-option label="全部" value></el-option>
          <el-option label="已发布" value="1"></el-option>
          <el-option label="草稿" value="0"></el-option>
        </el-select>
      </el-form-item>

      <el-form-item>
        <el-button @click="filterQuery">查询</el-button>
      </el-form-item>

      <el-form-item class="btn-add">
        <el-button type="primary" icon="el-icon-plus" @click="addCourse">新建课程</el-button>
      </el-form-item>
    </el-form>

    <!-- 表格部分 -->
    <el-table :data="courses" v-loading="loading" element-loading-text="数据加载中...">
      <el-table-column prop="id" label="ID" width="100"></el-table-column>
      <el-table-column prop="course_name" label="课程名称" width="200"></el-table-column>
      <el-table-column
        prop="price"
        label="价格"
        align="center"
        width="120"
        :formatter="priceFormatter"
      ></el-table-column>
      <el-table-column prop="sort_num" label="排序" align="center" width="120"></el-table-column>

      <!-- 状态展示 -->
      <el-table-column prop="status" label="状态" align="center" width="120">
        <template slot-scope="scope">
          <i
            class="status status-success"
            title="已发布"
            v-if="scope.row.status == '1'"
            @click="updateStatus(scope.row)"
          ></i>
          <i class="status status-warning" title="草稿" v-else-if="scope.row.status == '0'"></i>
        </template>
      </el-table-column>

      <!-- 操作部分 -->
      <el-table-column label="操作" align="center">
        <template slot-scope="scope">
          <!-- 状态按钮 -->
          <el-button
            size="mini"
            :type="scope.row.status == '1' ? 'danger' : 'success'"
            @click="updateStatus(scope.row)"
          >{{ scope.row.status == "1" ? "下架" : "发布" }}</el-button>

          <!-- 营销信息按钮 -->
          <el-button size="mini" @click="handleNavigate('CourseItem', scope.row.id)">营销信息</el-
              button>

          <!-- 内容管理按钮 -->
          <el-button size="mini" @click="handleNavigate('CourseTasks', scope.row.id)">内容管理</el-
              button>
        </template>
      </el-table-column>
    </el-table>
  </section>
</template>


<script>
// JS部分代码

//引入axios
import { axios } from "../utils";

export default {
  name: "Courses",
  title: "课程管理",
  //定义数据部分
  data() {
    return {
      filter: { course_name: "", status: "" }, //查询对象
      courses: [], //课程信息集合
      loading: false //是否弹出加载
    };
  },

  //钩子函数
  created() {
    this.loadCourses();
  },

  methods: {
    //方法1: 获取课程列表
    loadCourses() {

      this.loading = true;

      //请求后台查询课程列表接口

      return axios.get("/course",{
        params:{
          methodName: "findCourseList"
        }
      }).then(resp => { //可以不写括号
      console.log(resp);
      this.courses = resp.data
      this.loading = false;

      }).catch(error => {
        this.$message.error("数据获取失败")

      })
    },

    //方法2: 条件查询课程信息
    filterQuery() {
        this.loading = true;

      //请求后台条件查询接口

      return axios.get("/course",{
        //准备参数
        params:{
          methodName: "findByCourseNameAndStatus",
          course_name:this.filter.course_name,
          status:this.filter.status
        }
      }).then(resp => { //可以不写括号
      console.log(resp);
      this.courses = resp.data
      this.loading = false;

      }).catch(error => {
        this.$message.error("数据获取失败")
        // this.loading = false;加不加无所谓,返回有提示,看着加载也行,但如果你要严谨的话,就可以加

      })
    },

    //方法3: 添加课程跳转方法
    addCourse() {
      //跳转到CourseItem.vue
      this.$router.push({name:"CourseItem",params:{courseId:"new"}})
      //有键值对的地方,一定在{}里面,而由于对应路由需要一个id
      //那么就必须是键值对,且对应id只识别params的数据
      //不传递id的话,如果没有对应判断,那么url不会显示,但还是会过去
    },

    //方法4: 修改课程状态
    updateStatus(item) {
      //item表示选中的数据 =Course对象
      axios.get("/course",{
        params:{
          methodName:"updateCourseStatus",
          id:item.id

        }
      }).then(resp => {
        // console.log(resp)
        // console.log(item)
        //将返回的状态字段数据,封装到item对象中,就是将对应的状态信息进行覆盖,并不是重新赋值
        Object.assign(item,resp.data);
        
        //item就是上面的scope.row,代表当前行,这个行中有对应字段,获得对应数据的
        //获取对应根据prop来获得内容,所以在状态信息进行覆盖时,也就是改变,但并不会进行设置
        //因为没有双向绑定,即需要重新刷新页面进行显示

        //重新加载页面,可以理解为刷新,因为不刷新的话,可能看不到最新消息,虽然可以不写
        // console.log(resp)
        // console.log(item)
        window.location.reload;  
         //一般使用window.location.reload();,虽然可能是一样的,但最好使用后面的有()的,因为可能不会起作用

      }).catch(error => {
        this.$message.error("状态修改失败")
      })
    },

    //方法5: 根据路由名称, 导航到对应组件
    handleNavigate(name, id) {
      //根据参数 使用对应的路由 导航到指定的组件
      this.$router.push({name,params:{ //前面也说过,变量在键值对的,会默认为有对应值,即也变成键值对
        courseId:id
      }}) 
    },

    //价格前面添加¥ 方法
    priceFormatter(row, column, value, index) {
      return `¥${value}`;
    }
  }
};
</script>

<style lang="scss">
.courses {
  .actions {
    display: flex;
    align-items: flex-end;
  }

  .input-title {
    .el-form-item__content {
      min-width: 22vw;
    }
  }

  .btn-add {
    margin-left: auto;
  }
}
</style>

<template>
  <section class="course-item">
    <!-- 头部 -->
    <div class="header">
      <!-- 返回上一页 back()方法就是返回上一个路由的操作,简称为返回上一页 -->
      <el-page-header
        @back="() => this.$router.back()"
        :content="course.title"
      />
      <el-button type="primary" @click="handleSave">保存</el-button>
    </div>

    <!-- 表单开始 -->
    <el-form ref="form" :model="course" :rules="rules" label-width="120px">
      <el-card
        shadow="never"
        v-loading="loading"
        element-loading-text="数据加载中..."
      >
        <header slot="header">基本信息</header>
        <el-form-item label="名称" prop="course_name">
          <el-input
            v-model="course.course_name"
            type="text"
            maxlength="50"
            show-word-limit
          />
        </el-form-item>
        <el-form-item label="简介" prop="brief">
          <el-input
            v-model="course.brief"
            type="text"
            maxlength="100"
            show-word-limit
          />
        </el-form-item>
        <el-form-item label="讲师姓名" prop="teacher_name">
          <el-input
            v-model="course.teacher_name"
            type="text"
            maxlength="50"
            show-word-limit
          />
        </el-form-item>
        <el-form-item label="讲师简介" prop="teacher_title">
          <el-input
            v-model="course.teacher_info"
            type="text"
            maxlength="100"
            show-word-limit
          />
        </el-form-item>
        <el-form-item
          label="课程概述"
          prop="preview_first_field"
          class="form-control-summary"
        >
          <el-input
            v-model="course.preview_first_field"
            type="text"
            maxlength="20"
            show-word-limit
          />
          <el-input
            v-model="course.preview_second_field"
            type="text"
            maxlength="20"
            show-word-limit
          />
        </el-form-item>
      </el-card>

      <el-card
        shadow="never"
        v-loading="loading"
        element-loading-text="数据加载中..."
      >
        <header slot="header">销售信息</header>
        <el-form-item label="售卖价格" prop="discounts">
          <el-input v-model="course.discounts" type="number">
            <template slot="append">元</template>
          </el-input>
        </el-form-item>
        <el-form-item label="商品原价">
          <el-input v-model="course.price" type="number">
            <template slot="append">元</template>
          </el-input>
        </el-form-item>
        <el-form-item label="活动标签">
          <el-input
            v-model="course.price_tag"
            type="text"
            maxlength="4"
            show-word-limit
          />
        </el-form-item>
      </el-card>

      <el-card
        shadow="never"
        v-loading="loading"
        element-loading-text="数据加载中..."
      >
        <header slot="header">分享信息</header>
        <!-- 上传图片部分 -->
        <el-form-item label="分享小图" prop="share_image_title">
          <el-input v-model="course.share_image_title" type="text">
            <!-- :auto-upload="false",取消自动上传, :on-change="onchange" 调用onchange进行处理 -->
            <el-upload
              slot="prepend"
              :auto-upload="false"
              :on-change="onchange"
              action
              :limit="1"
            >
            <!-- false,不立即上传,也就是不发送到后台-->
              <el-button size="small" type="primary">点击上传</el-button>
            </el-upload>
          </el-input>
        </el-form-item>
        <el-form-item label="分享标题" prop="share_title">
          <el-input
            v-model="course.share_title"
            type="text"
            maxlength="40"
            show-word-limit
          />
        </el-form-item>
        <el-form-item label="分享简介" prop="share_description">
          <el-input
            v-model="course.share_description"
            type="text"
            maxlength="60"
            show-word-limit
          />
        </el-form-item>
      </el-card>

      <el-card
        shadow="never"
        v-loading="loading"
        element-loading-text="数据加载中..."
      >
        <header slot="course_description">课程详情</header>
        <editor v-model="course.course_description" />
      </el-card>

      <div class="actions">
        <el-button type="primary" @click="handleSave">保存</el-button>
      </div>
    </el-form>

    <!-- 表单结束 -->
  </section>
</template>

<script>
//引入富文本编辑器 和 axios
import Editor from "@/components/Editor.vue";
import { axios } from "../utils";

export default {
  name: "CourseItem",
  title: "营销信息",
  components: { Editor },
  data() {
    //表单校验规则
    const rules = {
      course_name: [
        { required: true, message: "请输入课程名称", trigger: "blur" },
        { min: 2, max: 50, message: "长度在 2 到 50 个字符", trigger: "blur" }
      ],
      brief: [
        { required: true, message: "请输入课程简介", trigger: "change" },
        { min: 5, max: 100, message: "长度在 5 到 100 个字符", trigger: "blur" }
      ],
      teacher_name: [
        { required: true, message: "请输入讲师姓名", trigger: "change" },
        { min: 2, max: 50, message: "长度在 2 到 50 个字符", trigger: "blur" }
      ],
      teacher_info: [
        { required: true, message: "请输入讲师简介", trigger: "change" },
        { min: 5, max: 50, message: "长度在 5 到 50 个字符", trigger: "blur" }
      ],
      preview_first_field: [
        { required: true, message: "请输入课程概述", trigger: "change" },
        { min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" }
      ],
      preview_second_field: [
        { required: true, message: "请输入课程概述", trigger: "change" },
        { min: 2, max: 20, message: "长度在 2 到 20 个字符", trigger: "blur" }
      ],
      price: [{ required: true, message: "请输入课程售价", trigger: "change" }],
      share_img: [
        { required: true, message: "请上传分享图片", trigger: "change" }
      ],
      share_title: [
        { required: true, message: "请输入分享标题", trigger: "change" }
      ],
      share_description: [
        { required: true, message: "请输入分享简介", trigger: "change" }
      ]
    };

    //数据部分
    return {
      rules, //规则
      course: {}, //课程
      loading: false,
      params: {}
    };
  },
  //钩子函数
  created() {
    //1.显示当前页面在网站中的位置
    this.$breadcrumbs = [
      { name: "Courses", text: "课程管理" },
      { text: "营销信息" }
    ];
      //获取路由传递的参数
      //获取当前路由,所以可以通过push来改变位置,改变浏览器自动就会访问,这是浏览器的作用,与这无关
      const id = this.$route.params.courseId; 
      //注意:这里是route,不是router,前面是获得值,后面是使用方法,不要搞混,他们有不同的对应值存放
      //判断id是否有值,没有值 就跳转到错误页面
      if(!id) return this.redirectToError() 
      //数字取否,为false,小于等于0的判断也为false,其余基本为true
      //只有一条语句,也可以与java一样省略括号

      //若没有值,那么就要判断是去新建还是修改,即是new还是有对应id值
      if(id === "new"){
        //new 代表新增操作
        this.course.title="新增课程"
      }else{
        //否则就是修改操作
        //alert("修改操作的ID是:" + id)

        //根据ID查询课程信息,进行回显
        this.loadCourse(id)
      }

      //创建FormData对象,将图片和表单其他内容 一同上传
      this.params = new FormData();

  },
  methods: {
    //方法1: 保存和修改课程信息
    handleSave() {
      //校验表单是否正确,$refs表示当前的dom,即获得对应表单,然后执行对应校验方法
      this.$refs.form.validate(valid => { //valid是校验后的boolean值
          //判断校验是否通过
          if(!valid) return false

          //设置多部件上传,Content-type属性
          let config = {
            headers:{
              "Content-type":"multipart/form-data"
            }
          }

          //获取到表单数据,保存到params中(params就是FormData对象)
          for(let key in this.course){ 
            //若in右边是数组,则一一获得,若是键值对,则一一获得key,不获得value
            //debugger //前端的debug调试方式,与java断点是类似的
            console.log(key + " = " + this.course[key]) 
            this.params.append(key,this.course[key])  //记得文件的那个去掉,防止被后端继续操作,使得数据出现问题
          }
            //请求后台接口 保存课程信息
            //可以是多个{}
            axios.post("/courseSalesInfo",this.params,config).then(resp =>{
              if(resp.data.status == 0){
                this.$router.back()
              } else if(resp.data.status == 1){
                this.$message.error(this.data.msg)
              }
            }).catch(error => {
              this.$message.error("保存课程失败!")
          

            }) 
            //这些$message是element-ui的js

          
      })
      
    },

    //文件上传
    onchange(file) {
      if(file != null){
        //将文件信息,保存到FormData对象中
        //参数1:表示文件上传项,对应名称最好要正确 参数2:文件上传的二进制数据,参数3:文件名
        //之所以可以得到二进制数据,就与java得到字节数据一样,实际都是字节
        this.params.append("file",file.raw,file.name) 
      }
    },

    //方法2: 根据ID 回显课程信
    loadCourse(id) {
      this.loading = true;
       axios.get("/course",{
        params:{
          methodName:"findCourseById",
          id:id

        }
      }).then(resp => {
        this.course = resp.data;
        this.loading = false;

      }).catch(error => {
        this.$message.error("回显数据失败")
      })
      
    },

    //跳转到错误
    redirectToError() {
      // TODO: Error components
      this.$router.replace({ path: "/not-found" });
    }
  }
};
</script>

<style lang="scss">
.course-item {
  .header {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  .el-card {
    margin-top: 20px;
  }

  .form-control-summary {
    .el-form-item__content {
      display: flex;
      justify-content: space-between;

      .el-input {
        width: 49%;
      }

      &:before,
      &:after {
        display: none;
      }
    }
  }

  .actions {
    display: flex;
    justify-content: center;

    .el-button {
      margin: 40px 20px;
    }
  }
}
</style>

<template>
  <section class="course-tasks">
    <!-- 头部 -->
    <div class="header">
      <!-- 显示当前课程的名称 -->
      <el-page-header @back="() => this.$router.back()" :content="addSectionForm.course_name"/>

      <!-- 添加章节按钮 -->
      <el-button type="primary" icon="el-icon-plus" @click="handleShowAddSection">添加章节</el-
          button>
    </div>

    <!-- 树形控件,展示课程对应的章节信息 -->
    <el-tree
      :data="sections"
      :props="treeProps"
      v-loading="loading"
      element-loading-text="数据加载中..."
    >
      <!-- slot-scope:代表当前树节点内容,有两个参数 data表示当前树节点, node表示当前节点状态 -->
      <div class="inner" slot-scope="{ data, node }">
        <span>{{ data.section_name || data.theme }}</span>

        <!-- 操作按钮 -->
        <span class="actions">
          <!-- 编辑章节  @click.stop 阻止事件冒泡 -->
          <el-button v-if="node.level == 1" size="small" @click.stop="handleEditSection(data)">编辑
    </el-button>

          <!-- 修改章节状态 -->
          <el-button
            v-if="node.level == 1"
            size="small"
            @click.stop="showStatus(data)"
          >{{ statusMapping[data.status] }}</el-button>
        </span>
      </div>
    </el-tree>
    <!-- 树形控件,展示课程对应的章节信息 -->

    <!-- 修改章节状态-对话框 -->
    <el-dialog class="toggle-dialog" title="提示" :visible.sync="showStatusForm" width="30%">
      <header class="toggle-header">
        <i class="el-icon-info"></i>
        <span>
          当前状态:{{
          statusForm.data && statusMapping[statusForm.data.status]
          }}
        </span>
        <!-- 
          在js里面&&和||作为结果时,根据决定性来进行返回
        如&&,如果左边为true,那么右边决定结果,返回右边
        否则左边决定结果,返回左边
        若是||,左边是false,那么右边决定结果,返回右边
        否则左边决定结果,返回左边
        -->
      </header>

      <el-form label-position="right" label-width="110px" :model="statusForm">
        <el-form-item label="状态变更为:">
          <!-- v-model的值是字符串类型,就获得类似于:label对应的值(其中参数是该字符串类型)
          否则就是v-model的值
          这里只获得一次传递的值,那么后面使用时,一直是默认的,所以需要刷新页面,重置次数
          @change="$forceUpdate()" 强制刷新
          -->
          <el-select @change="$forceUpdate()" v-model="statusForm.status" style="width: 100%">
            <el-option
              v-for="(item,index) in Object.keys(statusMapping)"
              :key="index"
              :label="statusMapping[item]"
              :value="item"
            ></el-option>
          </el-select>
        </el-form-item>
      </el-form>

      <span slot="footer" class="dialog-footer">
        <el-button @click="showStatusForm = false">取 消</el-button>
        <el-button type="primary" @click="updateStatus">确 定</el-button>
      </span>
    </el-dialog>
    <!-- 修改章节状态 -->

    <!-- 添加或修改章节 -->
    <el-dialog class="add-dialog" title="章节信息" :visible.sync="showAddSection">
      <el-form
        label-position="right"
        label-width="80px"
        ref="addSectionForm"
        :model="addSectionForm"
      >
        <el-form-item label="课程名称" prop="course_name">
          <el-input v-model="addSectionForm.course_name" disabled></el-input>
        </el-form-item>
        <el-form-item label="章节名称" prop="section_name">
          <el-input v-model="addSectionForm.section_name"></el-input>
        </el-form-item>
        <el-form-item label="章节描述" prop="description">
          <el-input type="textarea" v-model="addSectionForm.description"></el-input>
        </el-form-item>
        <el-form-item label="章节排序" prop="order_num">
          <el-input v-model="addSectionForm.order_num" type="number">
            <template slot="append">数字控制排序,数字越大越靠后</template>
          </el-input>
        </el-form-item>
      </el-form>

      <span slot="footer" class="dialog-footer">
        <el-button @click="showAddSection = false">取 消</el-button>
        <el-button type="primary" @click="handleAddSection">确 定</el-button>
      </span>
    </el-dialog>
    <!-- 添加或修改章节 -->
  </section>
</template>

<script>
//引入axios
import { axios } from "../utils";

export default {
  name: "CourseTasks",
  title: "课程结构",
  data() {
    //定义章节信息
    const addSectionForm = {
      course_id: undefined,
      course_name: "",
      section_name: "",
      description: "",
      order_num: 0
    };

    //章节与课时信息,树形结构
    const treeProps = {
      label: item => {
        return item.section_name || item.theme;
      },
      children: "courseLessonList"
    };

    //定义章节状态信息
    const statusMapping = {
      0: "已隐藏",
      1: "待更新",
      2: "已更新"
    };

    const statusForm = {
      id:undefined,
      status: 0,
      data:{}
    };

    return {
      addSectionForm,
      treeProps,
      sections: [], //总data,即总数据
      statusForm, //状态表单
      statusMapping,

      loading: false, //树形控件
      showAddSection: false, //添加或修改章节
      showStatusForm: false //状态修改
    };
  },
  created() {
    //1.显示当前页面在网站中的位置
    this.$breadcrumbs = [
      { name: "Courses", text: "课程管理" },
      { text: "课程结构" }
    ];

    //2. 从路由中获取传递的参数, 课程id
    const id = this.$route.params.courseId;
    if (!id) return this.redirectToError();

    //3.加载课程信息
    this.loadCourse(id);

    //4.加载课程对应的章节与课时
    this.loadChildren(id);
  },
  methods: {
    //方法1: 加载课程信息
    loadCourse(id) {
      axios
        .get("/courseContent", {
          params: {
            methodName: "findCourseById",
            course_id: id
          }
        })
        .then(resp => {
          //将数据保存到表单对象中
          this.addSectionForm.course_id = resp.data.id;
          this.addSectionForm.course_name = resp.data.course_name;
        })
        .catch(error => {
          this.$message.error("数据获取失败");
        });
    },

    //方法2: 加载树(章节与课程)
    loadChildren(id) {
      this.loading = true;
      axios
        .get("/courseContent", {
          params: {
            methodName: "findSectionAndLessonByCourseId",
            course_id: id
          }
        })
        .then(resp => {
          //将数据保存到sections里
          this.sections = resp.data;
          this.loading = false;
          console.log(resp.data);
        })
        .catch(error => {
          this.$message.error("数据获取失败");
          this.loading = false;
        });
    },

    //方法3: 显示添加章节表单,回显课程信息
    handleShowAddSection() {
      this.showAddSection = true;
    },

    //方法4: 添加&修改章节操作
    handleAddSection() {
      axios
        .post("/courseContent", {
          //这里传递过去的json,修改了后台的忽略大小写
        
          methodName: "saveOrUpdateSection",
          section: this.addSectionForm
        })
        .then(resp => {
          //debugger;
          //重新加载列表
          return this.loadChildren(this.addSectionForm.course_id);
        })
        .then(() => {
          //reset 重置表单
          this.addSectionForm.section_name = "";
          this.addSectionForm.description = "";
          this.addSectionForm.order_num = 0;
          this.showAddSection = false;
        })
        .catch(error => {
          this.$message.error("操作执行失败");
          this.showAddSection = false;
        });
    },

    //方法5: 修改章节回显方法
    handleEditSection(section) {
      //将对应信息拷贝到addSectionForm,进行回显
      //多余的键值对,则会添加到addSectionForm里面去
      Object.assign(this.addSectionForm,section);
    
      //this的当前表单开启
      this.showAddSection = true;

    },

    //方法6: 显示章节状态
    showStatus(data) {
      //回显状态
      
      this.statusForm.id = data.id;
      //之所以要加上toString(),是因为状态变更那里,只识别字符串类型
      //若你是number类型,那么就不会显示对应状态,而是直接显示对应数字
      this.statusForm.status = data.status.toString();
      this.statusForm.data = data;
      //弹出框
      this.showStatusForm = true;
    },

    //方法7: 修改章节状态
    updateStatus(statusForm) {

        axios
        .get("/courseContent", {
          params: {
            methodName: "updateSectionStatus",
            id:this.statusForm.id,
            status:this.statusForm.status
          }
        })
        .then(resp => {
          //之所以进行改变,是因为需要set原来状态,否则修改后,不会边
          this.statusForm.data.status = this.statusForm.status
          this.statusForm = {};
          this.showStatusForm = false;
        })
        .catch(error => {
          this.$message.error("修改状态失败");
          this.showStatusForm = false;
        });
    },

    //跳转到错误页面
    redirectToError() {
      this.$router.replace({ path: "/not-found" });
    }
  }
};
</script>

<style lang="scss">
.course-tasks {
  .header {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  .el-tree {
    margin-top: 20px;
  }

  .el-tree,
  .el-tree__empty-block {
    min-height: 200px;
  }

  .el-tree-node__content {
    height: auto;
  }

  .inner {
    display: flex;
    flex: 1 0 0;
    align-items: center;
    padding: 10px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
  }

  .actions {
    margin-left: auto;
  }
}
</style>

代码地址:
链接:https://pan.baidu.com/s/1Hku35l0bJOMDI7OwQtjCAQ
提取码:alsk
  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值