精华内容
下载资源
问答
  • 项目部署一般是两种方式:一种是打包成 jar 包直接执行,另一种是打包成 war 包放到 tomcat 服务器下。springboot一般情况下默认为jar包方式,这里我把jar包和war包两种方式都记录一下 *默认maven管理项目 !!! ...

    项目部署一般是两种方式:一种是打包成 jar 包直接执行,另一种是打包成 war 包放到 tomcat 服务器下。springboot一般情况下默认为jar包方式,这里我把jar包和war包两种方式都记录一下

    *默认maven管理项目 !!!

    1、jar包方式:

    a、先用mvn clean清理一下;

    b、忽略测试类 打成jar包命令:mvn clean package -Dmaven.test.skip=true;

    c、将jar包上传到服务器上之后 ,使用命令:nohup java -jar target/spring-boot-scheduler-1.0.0.jar & 启动服务能正常访问即可。

    2、war包方式:

    a、将pom底下 <packaging>jar</packaging>  改为  <packaging>war</packaging>

    b、在pom底下修改 ---打包时排除自带的tomcat 

    在这里将 scope 属性设置为 provided,这样在最终形成的 WAR 中不会包含这个 JAR 包,因为 Tomcat 或 Jetty 等服务器在运行时将会提供相关的 API 类。

    c、注册启动类

    创建 ServletInitializer.java,继承 SpringBootServletInitializer ,覆盖 configure(),把启动类 Application 注册进去。外部 Web 应用服务器构建 Web Application Context 的时候,会把启动类添加进去。

    package cn.tycoding;
    
    import javafx.application.Application;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
    
    /**
     * @Author: liLong
     * @Data: Created on 2019/6/5
     * @Desc: 生成war包注册启动类
     */
    public class ServletInitializer extends SpringBootServletInitializer {
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application){
            return application.sources(Application.class);
        }
    }
    

    d、执行 mvn clean package -Dmaven.test.skip=true 忽略测试类 打包上传到服务器启动tomcat 可以访问即可!

    展开全文
  • 项目前后端接口联调联调准备运行后台项目clean 清空项目的编译文件compile 重新编译项目项目部署Tomcat项目名为 lagou_edu_home,端口号: 8080,使用 war 方式部署部署图片上传路径为 Tomcat 的 webapps 目录...

    d62c52af722d038cb22f0d5b367d089c.png

    项目前后端接口联调

    联调准备

    运行后台项目

    clean 清空项目的编译文件

    compile 重新编译项目

    将项目部署到 Tomcat:项目名为 lagou_edu_home,端口号: 8080,使用 war 方式部署

    部署图片上传路径为 Tomcat 的 webapps 目录下的 upload 目录

    最后运行前端项目

    首先导入前端项目到 VS Code

    运行项目

    课程管理首页

    Courses.vue 的视图部分代码

    <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>

    获取课程列表

    Courses.vue JS 部分代码

    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.loading = false; // 关闭加载
                    this.courses = resp.data; // 取出数据
                })
                    .catch(error => {
                    this.$message.error("数据获取失败! ! !");
                });
            },
        }
    };
    

    条件查询课程信息

    Courses.vue JS 代码的 methods

    //方法 2: 条件查询课程信息
    filterQuery() {
        this.loading = true;
        // 定义查询条件对象
        const search = {};
        // 保存用户输入的条件
        if (this.filter.course_name) search.course_name = this.filter.course_name;
        if (this.filter.status) search.status = this.filter.status;
        // 请求后台条件查询接口
        return axios
            .get("/course", {
            // 准备参数
            params: {
                methodName: "findByCourseNameAndStatus",
                course_name: search.course_name,
                status: search.status
            }
        })
            .then(resp => {
            console.log(resp);
            this.loading = false;
            // 将响应数据保存到 courses
            this.courses = resp.data;
        })
            .catch(error => {
            this.$message.error("数据获取失败! ! !");
        });
    },
    

    跳转到新建课程页面

    Courses.vue JS 代码的 methods

    // 方法 3: 添加课程跳转方法 
    addCourse() {
        // 路由跳转到 CourseItem.vue 组件
        this.$router.push({ name: "CourseItem", params: { courseId: "new" } });
    },
    

    修改课程状态

    Courses.vue JS 代码的 methods

    // 方法 4: 修改课程状态
    updateStatus(item) {
        // item 表示选中的数据 = Course 对象
        axios
            .get("/course", {
            params: {
                methodName: "updateCourseStatus",
                id: item.id
            }
        })
            .then(res => {
            console.log(res);
            // 将返回的状态字段,封装到对象中
            Object.assign(item, res.data);
            // 重新加载页面
            window.location.reload;
        });
    },
    

    跳转课程营销或内容管理

    Courses.vue JS 代码的 methods

    // 方法 5: 根据路由名称,导航到对应组件  
    handleNavigate(name, id) {
        this.$router.push({ name, params: { courseId: id } });
    },
    

    新建 & 修改课程

    Course 组件中的跳转方法

    <!-- 营销信息按钮 -->
    <el-button
               size="mini"
               @click="handleNavigate('CourseItem', scope.row.id)"
               >营销信息</el-button>
    // 方法 3: 添加课程跳转方法
    addCourse() {
        // 路由跳转到 CourseItem.vue 组件
        this.$router.push({ name: "CourseItem", params: { courseId: "new" } });
    },

    router.js 路由

    找到 name 为: CourseItem 的路由

    // 添加课程的路由
    {
        path: "/courses/:courseId", // 路径,携带参数: 课程 ID
        name: "CourseItem",
        // 路由导航到的组件
        component: () =>
        import(/* webpackChunkName: 'courses' */ "../views/CourseItem.vue")
    },
    

    CourseItem 组件

    CourseItem.vue 的视图部分代码

    <template>
      <section class="course-item">
        <!-- 头部 -->
        <div class="header">
          <!-- 返回上一页 -->
          <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"
                >
                  <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>

    CourseItem.vue 的 JS 部分代码

    data() {
        // 数据
        return {
            rules, //规则
            course: {}, //课程
            loading: false,
            params: {} //参数对象
        };
    },
    // 钩子函数
    created() {
        // 1.显示当前页面在网站中的位置
        this.$breadcrumbs = [
          { name: "Courses", text: "课程管理" },
          { text: "营销信息" }
        ];
    
        // 2.从路由中获取传递的参数, 课程 id
        const id = this.$route.params.courseId;
    
        // 3.判断 id 是否有值
        if (!id) return this.redirectToError();
    
        // 4.判断是 new 还是具体的id
        if (id === "new") {
          // new 代表新增
          this.course.title = "新增课程";
        } else {
          // 否则就是修改
          this.loadCourse(id);
        }
    },
    

    图片上传分析

    页面部分

    <!-- 上传图片部分 -->
    <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"
                       >
                <el-button size="small" type="primary">点击上传</el-button>
            </el-upload>
        </el-input>
    </el-form-item>

    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
    
    1. 将 form 表单元素的 name 与 value 进行组合,实现表单数据的序列化,从而减少表单元素的拼接,提高工作效率。
    2. 异步上传文件
    3. 创建 FormData 对象

    CourseItem.vue 的 JS 的 methodscreated 部分的代码

    //5.创建FormData对象,将图片与表单一同上传
    this.params = new FormData();
    

    methods 添加方法

    // 文件上传
    onchange(file) {
        // 判断文件不为空
        if (file != null) {
            // 将文件信息保存到 params 中
            this.params.append("file", file.raw, file.name);
        }
    },
    

    新建课程信息

    CourseItem.vue 的 JS 的 methods 的代码

    // 方法 1: 保存和修改课程信息
    handleSave() {
        // 检查是否拿到了正确的需要验证的 form
        this.$refs.form.validate(valid => {
            if (!valid) return false;
    
            // 1.设置 Content-Type 为多部件上传
            let config = {
                headers: {
                    "Content-Type": "multipart/form-data"
                }
            };
    
            // 2.获取表单中的数据,保存到 params (params 就是 FromData对象)
            for (let key in this.course) {
                //debugger
                console.log(key + "---" + this.course[key]);
                this.params.append(key, this.course[key]);
            }
    
            // 3.保存课程信息
            axios
                .post("/courseSalesInfo", this.params, config)
                .then(res => {
                //debugger
                if (res.data.status == 0) {
                    // 保存成功,跳转到首页
                    this.$router.back();
                } else if (res.data.status == 1) {
                    this.$message({
                        type: "error",
                        message: res.data.msg
                    });
                }
            })
                .catch(err => {
                this.$message.error("保存失败! ! !");
            });
        });
    },
    

    修改课程信息

    CourseItem.vue 的 JS 的 methods 的代码

    // 方法 2: 根据 ID 回显课程信
    loadCourse(id) {
        this.loading = true;
        axios
            .get("/course", {
            params: {
                methodName: "findCourseById",
                id: id
            }
        })
            .then(res => {
            this.loading = false;
            this.course = res.data;
        })
            .catch(() => {
            this.$message.error("回显数据失败! ! !");
        });
    },
    

    内容管理

    Course 组件中的跳转方法

    <!-- 内容管理按钮 -->
    <el-button
               size="mini"
               @click="handleNavigate('CourseTasks', scope.row.id)">内容管理</el-button>
    // 方法 5: 根据路由名称, 导航到对应组件
    handleNavigate(name, id) {
        this.$router.push({ name, params: { courseId: id } });
    },

    router.js 路由

    // 内容管理的路由
    {
        path: "/courses/:courseId/tasks",
        name: "CourseTasks",
        meta: { requireAuth: true },
        component: () =>
        import(/* webpackChunkName: 'courses' */ "../views/CourseTasks.vue")
    }
    

    CourseTasks 组件

    树形控件测试

    1. 打开之前编写 Element UI 的项目
    2. 在 component 目录下添加一个组件 TestTree.vue
    <template>
    </template>
    <script>
        export default {
    ​
        }
    </script>
    <style scoped>
    </style>
    1. Index.vue 组件中的导航菜单位置添加一个树形控件导航;注意要设置 index 的路径为 “/tree”
    <el-submenu index="1">
      <template slot="title">
        <i class="el-icon-location"></i>
        <span>导航菜单</span>
      </template>
      <el-menu-item-group>
        <el-menu-item index="/course">
          <i class="el-icon-menu"></i>
          <span>课程管理</span>
        </el-menu-item>
        <el-menu-item index="/tree">
          <i class="el-icon-menu"></i>
          <span>树形控件</span>
        </el-menu-item>
      </el-menu-item-group>
    </el-submenu>
    1. router/index.js 路由文件中进行配置,在布vue局路由中再添加一个子路由
    // 导入树形控件组件
    import Tree from "@/components/TestTree.vue"
    
    ...
      {
        path: "/index",
        name: "index",
        component: Index,
        children: [
          {
            path: "/course",
            name: "course",
            component: Course
          },
           // Tree控件测试路由
          {
            path: "/tree",
            name: "tree",
            component: Tree
          }
        ]
      }
    ...
    
    1. ElementUI 官网中查找树形控件
    2. 先来查看基础用法,赋值代码到 TestTree.vue
    <template>
        <el-tree :data="data" :props="defaultProps"></el-tree>
    </template>
    
    <script>
    export default {
        data() {
          return {
            data: [{
              label: '一级 1',
              children: [{
                label: '二级 1-1',
                children: [{
                  label: '三级 1-1-1'
                }]
              }]
            }, {
              label: '一级 2',
              children: [{
                label: '二级 2-1',
                children: [{
                  label: '三级 2-1-1'
                }]
              }, {
                label: '二级 2-2',
                children: [{
                  label: '三级 2-2-1'
                }]
              }]
            }, {
              label: '一级 3',
              children: [{
                label: '二级 3-1',
                children: [{
                  label: '三级 3-1-1'
                }]
              }, {
                label: '二级 3-2',
                children: [{
                  label: '三级 3-2-1'
                }]
              }]
            }],
            defaultProps: {
              children: 'children',
              label: 'label'
            }
          };
        },
        methods: {
        }
      };
    </script>
    
    <style scoped>
    </style>
    1. Tree 组件属性分析

    data:展示数据

    props:配置树形结构;label - 设置节点名称,children - 指定生成子树的属性名称

    1. 自定义树节点内容:data - 数据对象,node - 节点对象
    <el-tree :data="data" :props="defaultProps">
        <span class="custom-tree-node" slot-scope="{ node, data }">
            <span>{{ data.label }}</span>
            <span> 级别:{{node.level}}</span>
        </span>
    </el-tree>
    1. 展示树形结构章节与课时
    <template>
      <el-tree :data="data" :props="defaultProps">
        <span class="custom-tree-node" slot-scope="{ node, data }">
          <span>{{ data.section_name || data.theme }}</span>
          <span> 级别:{{node.level}}</span>
        </span>
      </el-tree>
    </template>
    
    <script>
    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 => {
                return item.section_name || item.theme;
            },
          },
        };
      }
    };
    </script>

    显示当前课程的名称

    <el-page-header
            @back="() => this.$router.back()"
            :content="addSectionForm.course_name"
          />

    data 数据

    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: "lessonList"
        };
    
        // 定义章节状态信息
        const statusMapping = {
            0: "已隐藏",
            1: "待更新",
            2: "已更新"
        };
    
        const statusForm = {
            status: 0
        };
    
        return {
            addSectionForm,
            treeProps,
            sections: [],
            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(res => {
                // 将数据保存到表单对象中
                this.addSectionForm.course_id = res.data.id;
                this.addSectionForm.course_name = res.data.course_name;
            })
                .catch(error => {
                this.loading = false;
                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;
        })
            .catch(error => {
            this.loading = false;
            this.$message.error("数据获取失败! ! !");
        });
    },
    

    Element UI 树形控件 el-tree:data 列表数据;:props 配置选项。

    <!-- 树形控件, 展示课程对应的章节信息 -->
    <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>
            ...
        </div>
        ...
    </el-tree>

    :props 配置选项:label - 指定节点标签为节点对象的某个属性值;children - 指定子树为节点对象的某个属性值。

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

    操作按钮显示:node.level 获取当前节点的级别;@click.stop 事件冒泡,点击哪个元素就执行哪个元素绑定的事件

    <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="handleShowToggleStatus(data)"
                   >{{ statusMapping[data.status] }}</el-button>
    </span>

    回显信息

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

    JS 代码

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

    添加章节

    <el-button type="primary" @click="handleAddSection">确 定</el-button>

    JS 代码

    // 方法 4: 添加&修改章节操作
    handleAddSection() {
        axios
            .post("/courseContent", {
            methodName: "saveOrUpdateSection",
            section: this.addSectionForm
        })
            .then(resp => {
            this.showAddSection = false;
            // 重新加载列表
            return this.loadChildren(this.addSectionForm.course_id);
        })
            .catch(error => {
            this.loading = false;
            this.$message.error("操作执行失败! ! !");
        });
    },
    

    后台接口问题解决

    BaseServlet 中代码修改

    if("application/json;charset=utf-8".equals(contentType)) {
        ...
    }
        
    // 修改为:
    
    if("application/json;charset=utf-8".equalsIgnoreCase(contentType)) {
        ...
    }

    CourseContentServlet 中的 saveOrUpdateSection 方法修改

    // 使用 BeanUtils 的 copyProperties 方法将 map 中的数据封装到 section 对象里
    BeanUtils.copyProperties(section,map.get("section"));

    修改章节

    <el-button v-if="node.level == 1" size="small" 
               @click.stop="handleEditSection(data)">编辑</el-button>

    JS 回显示方法

    // 方法 5: 修改章节回显方法
    handleEditSection(section) {
        // 对象拷贝
        Object.assign(this.addSectionForm, section);
        this.showAddSection = true;
    },
    

    事件冒泡:当点击子元素的事件。如果父元素也有同样的事件的话。他就会一并的触发。

    解决冒泡事件的方法:@click.stop

    <body>
        <!-- 事件冒泡: 解决方案 @click.stop -->
        <div id="app" @click="log('div点击事件')">
            <button @click.stop="log('button点击事件')">事件冒泡</button>
        </div>
    </body>
    <script src="./js/vue.min.js"></script>
    <script>
        var VM = new Vue({
            el: "#app",
            methods: {
                log(t) {
                    alert(t);
                },
            },
        });
    </script>

    章节状态回显

    <!-- 章节状态按钮 -->
    <el-button
               v-if="node.level == 1"
               size="small"
               @click.stop="showStatus(data)"
               >{{ statusMapping[data.status] }}</el-button>

    JS 代码

    // data 函数中定义的章节状态
    const statusMapping = {
        0: "已隐藏",
        1: "待更新",
        2: "已更新"
    };
    // 方法 6: 显示章节状态
    showStatus(data) {
        this.statusForm.id = data.id;
        this.statusForm.status = data.status.toString();
        this.statusForm.data = data;
        this.showStatusForm = true;// 显示状态表单
    },
    

    Select 选择器

    1. 打开之前编写 Element UI 的项目
    2. 在 component 目录下添加一个组件 TestSelect.vue
    <template></template>
    <script>
        export default {};
    </script>
    <style scoped>
    </style>
    1. Index.vue 组件中的导航菜单位置添加一个 Select 选择器导航;注意要设置 index 的路径为 /select
    <el-menu-item index="/select">
        <i class="el-icon-menu"></i>Select选择器
    </el-menu-item>
    1. index.js 路由文件中进行配置,在布局路由中再添加一个子路由
    import Select from "@/components/TestSelect.vue"
    {
        path: "/select",
        name: "select",
        component: Select,
    },
    
    1. 在 Element UI 官网中查找 Select 选择器
    2. 查看基础用法,将代码复制到 TestSelect.vue
    <template>
      <el-select v-model="value" placeholder="请选择">
        <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
      </el-select>
    </template>
    ​
    <script>
      export default {
        data() {
          return {
            options: [{
              value: '选项1',
              label: '黄金糕'
            }, {
              value: '选项2',
              label: '双皮奶'
            }, {
              value: '选项3',
              label: '蚵仔煎'
            }, {
              value: '选项4',
              label: '龙须面'
            }, {
              value: '选项5',
              label: '北京烤鸭'
            }],
            value: ''
          }
        }
      }
    </script>
    ​
    <style scoped>
    </style>
    1. select 选择器属性分析

    v-model 的值为当前被选中的 el-optionvalue 属性值

    el-option 选项:label 选项的标签名;value 选择的值

    1. 使用 Select 选择器展示状态信息
    <template>
      <el-select v-model="status" placeholder="请选择">
        <el-option
          v-for="index in Object.keys(statusMappings)"
          :key="index"
          :label="statusMappings[index]"
          :value="index"
        ></el-option>
      </el-select>
    </template>
    
    <script>
    export default {
      data() {
        return {
          statusMappings: {
            0: "已隐藏",
            1: "待更新",
            2: "已更新",
          },
          status: "",
        };
      },
    };
    </script>
    
    <style scoped>
    </style>

    Object.keys()

    var obj = { 0: 'a', 1: 'b', 2: 'c' };
    console.log(Object.keys(obj)); // console: ['0', '1', '2']
    

    章节状态修改

    <el-button type="primary" @click="updateStatus">确 定</el-button>

    JS 部分

    //方法7: 修改章节状态
    updateStatus(statusForm) {
        axios
            .get("/courseContent", {
            params: {
                methodName: "updateSectionStatus",
                status: this.statusForm.status,
                id: this.statusForm.id
            }
        })
            .then(resp => {
            this.statusForm.data.status = this.statusForm.status;
            this.statusForm = {};
            this.showStatusForm = false;
        })
            .catch(error => {
            this.showStatusForm = false;
            this.$message.error("修改状态失败! ! !");
        });
    },
    

    v-for 里面数据层次太多, 修改过数据变了,页面没有重新渲染,需手动强制刷新。

    <!-- 强制刷新 @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>

    项目上线部署发布

    简介

    服务器与操作系统

    服务器是计算机的一种,它比普通计算机运行更快、负载更高、价格更贵。

    服务器从硬件上等同于电脑 PC。而服务器跟 PC 都是由 CPU、内存、主板、硬盘、电源等组成;但服务器的性能要远远超过 PC,因为它要保证全年无休。

    操作系统是作为应用程序与计算机硬件之间的一个接口。

    没有安装操作系统的计算机,被称为裸机, 如果想在裸机上运行自己的程序,就需要使用机器语言。

    安装操作系统之后,就可以配置一些高级语言的环境,进行高级语言的开发。

    Linux 系统是最具稳定性的系统。

    Linux 比 Windows 更具安全性。

    Linux 服务器在应用开发上更能节约成本。

    项目的发布部署

    项目的开发流程大致要经过一下几个步骤:

    • 项目立项
    • 需求分析阶段
    • 原型图设计阶段
    • 开发阶段
    • 测试阶段
    • 系统上线

    后台项目部署

    安装虚拟机

    在 Linux 阶段已经安装过了虚拟机,使用的是 Linux 操作系统 CentOS 7 版本。

    安装软件环境

    以下软件在 Linux 阶段都已安装完成:

    • JDK 11
    • Tomcat 8.5
    • MySQL 5.7

    查看 Java 版本

    java -version

    查看 tomcat 是否能够正常启动

    # 进入到 tomcat 目录
    cd /usr/tomcat/
    ​
    # 启动 tomcat
    ./bin/startup.sh 
    ​
    # 关闭 tomcat
    ./bin/shutdown.sh 

    关闭防火墙

    #查看已经开放的端口:
    firewall-cmd --list-ports
    ​
    #开启端口
        firewall-cmd --zone=public --add-port=8080/tcp --permanent
    ​
    #命令含义:
        –zone #作用域
        –add-port=8080/tcp #添加端口,格式为:端口/通讯协议
        –permanent #永久生效,没有此参数重启后失效#重启防火墙
    firewall-cmd --reload
    ​
    # 关闭防火墙
    # 停止 firewall
    systemctl stop firewalld.service
    # 禁止 firewall 开机启动
    systemctl disable firewalld.service
    # 查看默认防火墙状态(关闭后显示 notrunning,开启后显示 running)
    firewall-cmd --state

    登录 MySQL 检查数库连接是否正常

    -- 创建用户
    CREATE USER 'JiuYuan'@'%' IDENTIFIED BY 'JiuYuan@123';
    
    -- 授予所有权限
    GRANT ALL PRIVILEGES ON *.* TO 'JiuYuan'@'%' IDENTIFIED BY 'JiuYuan@123';
    
    -- 刷新
    FLUSH PRIVILEGES;

    使用 SQLYog 连接 Linux 上的 MySQL,导入 SQL 脚本创建项目所需的数据库

    项目打包发布

    1. 修改项目的数据库配置文件:数据库的 IP、用户名、密码。
    2. 修改 Constants 常量类中的项目 URL
    // 生产环境地址
    public static final String LOCAL_URL = "http://192.168.91.128:8080";
    1. 修改后启动项目,测试一下 保证数据库连接没有问题
    2. 检查 POM 文件打包方式必须是 war 编译版本为 JDK 11
    <packaging>war</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    1. 执行打包命令
    // 清除 target 文件夹
    clean 
    // 打包跳过测试
    package
    1. 复制出 target 目录下的 war 包
    2. 修改一下 war 包名称为 lagou_edu_home.war
    3. 上传到 tomcat 的 webapps 目录中启动测试,访问接口:
    http://192.168.91.128:8080/lagou_edu_home/course?methodName=findCourseList

    前端项目部署

    修改配置文件

    前端项目的配置文件有两个:一个是开发环境的配置文件,一个是生产环境的配置文件。

    先修改一下开发环境文件 .env.development 的后端服务器访问地址,然后进行一下测试:

    VUE_APP_API_BASE =  http://192.168.91.128:8080/lagou_edu_home

    修改生产环境的配置文件 .env.production

    VUE_APP_API_BASE = http://192.168.91.128:8080/lagou_edu_home

    前端项目打包

    修改 vue.config.js 配置文件

    module.exports = {
      // relative path for dev
      publicPath: process.env.NODE_ENV === "production" ? "/edu-boss/" : "./",
      // for gh-pages
      indexPath: "index.html",
      assetsDir: "static",
      lintOnSave: process.env.NODE_ENV !== "production",
      productionSourceMap: false,
      css: {
        // sourceMap: process.env.NODE_ENV !== 'production'
      },
      devServer: {
        open: true,
        port: 8081
      }
    };
    

    执行下面的打包命令:

    npm run build

    在项目下会生成一个 dist 目录

    在本地 Tomcat 的 webapps 目录下创建一个 edu-boss 文件夹将 dist 目录中的文件拷贝到里面

    启动本地 Tomcat 访问前端项目路径为

    http://localhost:8081/edu-boss/

    前端项目发布

    1. 验证没有问题后将 edu-boss 项目压缩上传到 Tomcat 服务器
    # 复制一份 Tomcat
    cp -r /usr/tomcat/ /usr/tomcat2
    # 上传 edu-boss.zip 并解压
    unzip edu-boss.zip 
    # 删除 edu-boss.zip
    rm -rf edu-boss.zip
    1. 修改 tomcat2 的 server.xml 配置文件的 3 个端口避免与 tomcat 冲突,修改结果如下:
    ...
    <Server port="8006" shutdown="SHUTDOWN">
      <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
    ...
      <Service name="Catalina">
    ...
        <Connector port="8081" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8444" />
    ...
      </Service>
    </Server>
    1. 在部署后端项目的 tomcat1 的 webapps 目录下创建一个 upload 文件夹保存图片
    2. 运行前端项目:
    # 进入 tomcat2,启动项目
    ./bin/startup.sh
    # 动态查看日志
    tail -f logs/catalina.out
    1. 运行后端项目
    # 进入 tomcat1,启动项目
    ./bin/startup.sh 
    # 动态查看日志
    tail -f logs/catalina.out 
    1. 前后端都启动后进行测试
    想了解更多,欢迎关注我的微信公众号:Renda_Zhang
    展开全文
  • 全量发布和增量发布概念和区别二、springboot部署tomcat2.1. 创建Web初始化类2.2. 修改打包方式2.3. 项目发布目录2.4. 启动tomcat2.5. 浏览器验证 一、 企业发布场景 1. 首次发布 项目上线第一次会采用全量发布 ...

    一、 企业发布场景
    1. 首次发布

    项目上线第一次会采用全量发布
    【编译】-【打包】-【全量发布】-【测试】-【上线】

    2. 非首次发布

    从第2次开始包括第2次:采用增量发布
    【编译】-【打包】-【增量发布】-【测试】-【上线】

    3. 全量发布和增量发布概念和区别

    全量发布:
    全部文件一次性发布

    增量发布:
    1.只发布新增、更新、删除部分

    区别:
    全量发布仅限第一次发布项目
    增量发布自第2次开始,发布前,需要提前备份,做好回滚准备

    前言:
    开始以为打包springboot项目为war包丢到tomcat上的webapps下面就可以访问了,可是调用接口却报404的错误,而打开8080的主页,不加路径却可以看到index.jsp正常显示,后来查网上的资料才知道是需要添加一个servlet的初始化类

    二、springboot部署tomcat
    2.1. 创建Web初始化类
    package com.gblfy.xe;
    
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
    
    /**
     * know Web程序启动类
     *
     * @author gblfy
     * @Date 2019/11/21 9:43
     */
    public class ServletInitializer extends SpringBootServletInitializer {
    
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
            return builder.sources(XeApplication.class);
        }
        //这个类继承至SpringBoorServletInitializer,并覆盖了其configuer方法
    }
    
    
    2.2. 修改打包方式
     <packaging>war</packaging>
    
    2.3. 项目发布目录

    将项目的war包,放到tomcat的webapps下面
    在这里插入图片描述

    2.4. 启动tomcat

    在这里插入图片描述

    2.5. 浏览器验证

    http://ip:端口/项目名/url
    例如:
    http://localhost:8080/xe-0.0.1-SNAPSHOT/aa
    注:这个项目名为war包发布后的名称
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    以后增量发布的时候,就往这个war的文件夹里面,把需要增加、修改文件复制到里面重新发布即可!

    一般会有java文件(编译后的)、配置文件、jar这3类文件
    数据补丁:单独运行
    在这里插入图片描述

    展开全文
  • 项目上线的时候如何部署到服务器上,一般可能通过自己写脚本跑,如果使用maven的话也是可以的。 使用maven自动部署项目tomcat

    项目上线的时候如何部署到服务器上,一般可能通过自己写脚本跑,如果使用maven的话也是可以的。

    关于Ant和Jenkins自动部署时需要开启tomcat的管理功能,默认不开启。tomcat7之后总共有6种角色,有两种角色很少人讲到,就是admin-gui,admin-script(注意,不是manager-gui,manager-script),这个是针对Host Manager的。
    Tomcat Manager用户配置详解
    使用maven自动部署项目到tomcat

    maven启动访问不了主页

    展开全文
  • vue-cli3如何部署在服务器的tomcat,以及vue.config.js如何配置才能上线(亲测可用) 首先需要保证tomcat服务器在8090端口(我的Vue项目运行在8090端口上)正常运行,并在tomcat的webapps文件夹下创建dist文件夹 1....
  • 一种是打包成jar包直接执行,另一种是打包成war包放到tomcat服务器下。 先来介绍第一种: 打成jar包 如果你使用的是maven来管理项目,执行以下命令既可以打包: cd 项目跟目录(和pom.xml同级) mvn clean package #...
  • 有很多网友会时不时的问我, Spring Boot 项目如何测试,如何部署,在生产中有什么好的部署方案吗?这篇文章就来介绍一下 Spring Boot 如何开发、调试、打包到最后的投产上线。开发阶段单元测试在开发阶段的时候最...
  • 什么是项目部署简单说就是: 1.当项目代码编码完工后。 2.经过专业测试后确定项目可以上线给客户使用了的时候。 3.这个时候,找一台机器当做服务器,里面安装软件所需要的环境(tomcat/weblogic,Apache),把...
  • 项目组采用的是boot2.0+vue前后端分离的方式开发,但是因为没有专业的运维人员,所以决定部署到一起达成war的方式进行上线。 目前经过摸索和踩坑,已经可以正常合在一起并达成war部署tomcat,但是遇到一个问题...
  • 为了让代码开发之后,提升测试、部署上线的效率,需要将代码编译、部署过程自动化。这里先介绍怎样使用javac自动化编译java代码。因为项目目前暂未使用ant、maven等管理构建,所以需要使用比较原始的javac来实现,很...
  • 如何在CentOS 7上部署Java Web项目

    千次阅读 2018-03-22 19:11:48
    近日,因有需求,须上线一七年之项目; 然,工程已久,不知其还在否,查之; 幸而项目未删; 遂,对其实体类以建库及其表也,查其教程以配之环境也,将其部署使之跑也; 善,终成矣,故做此文以录之; ...
  • vue项目打包部署生产环境

    千次阅读 2020-10-09 16:24:07
    当我们将 vue 项目完成后,面临的就是如何项目进行打包上线,放到服务器中。我使用的是 vue-cli(simple) 脚手架,所以就讲一下如何项目进行打包,并放到 tomcat 上。 build > utils.js (修改publicPath:...
  • SpringBoot 上线部署 SSL 安全证书

    千次阅读 2018-05-04 10:50:15
    SpringBoot 上线部署 SSL 安全证书文章 未结 0  197admin 4 白金2018-04-03 15:16:56使用微服务 SpringBoot 开发的项目,由于它使用的是内嵌 Tomcat 容器,要上线了,那么要如何部署 SSL 安全...
  • 在携程程序中,我们经常会遇到要求能够在项目部署上线后能够定时执行,自动执行的要求。现在我就带大家写一个简单的定时器,改定时器可以实现项目部署在tomcat后便可以自动运行,无需执行其他任何操作。首先我们来...
  • 由于项目中使用的文件较多,每次上线需要将包中的properties文件删除比较麻烦,所以需要将这些配置文件放在服务器一个指定的目录中,这样项目中就不包含这些配置文件方便部署。 那么如何加载这些配置文件? 1.通过...
  • 项目部署上线1、项目文件导入服务器项目文件夹导入服务器项目文件夹workspace文件夹是我们默认的项目文件存放的文件夹,位置和名称需与配置文件中定义保持一致导入方式: · 复制开发平台的项目文件夹内容到服务器...
  • 现在公司一个服务器上需要部署两个项目,其中一个项目已经正式上线,并且已经占用了80端口,另外一个项目 部署上去后,访问必须要加端口号,这样的用户体验非常不好,那么如何解决解决这个问题了? 使用nginx去共用...
  • 刚来到新公司,第一次做需求发现上线是手动打版,在 apache-tomcat-7.0.40/webapps 目录里 搜索 改过的文件 ,然后复制到 项目的空 文件夹里上传到服务器,一个一个找好麻烦,最主要的是目录太深,害怕搞错。...
  • 项目部署上线后,由于某些原因需要重新更新内容(即新的war包部署到服务器),但是上线了用户上传的图片会丢失,新的war包是不可能的有就项目的上传图片的。 问题 如何做到重新部署项目到服务器后,之前上传的图片...
  • 前言 刚从事java开发的同学很多时间会直接接触一些简单的ssm框架,使用spring mvc外加mybatis实现一个基本的web项目,打个war包部署上线。跟上时代潮流的会使用spring boot直接拉上内置tomcat jar包部署方式上手实现...
  • 项目总结

    2011-04-02 13:53:35
    项目今天上线,没想到这能准时上线,开发过程中遇到大大小小的问题也不少,昨天部署到服务器上也出现了很多问题。 1.服务器环境oracle9i、jdk1.5、Tomcat5x(很多版本的jdk、Tomcat) 本地环境oracle10g、jdk1.6、...
  • manager(pom)聚合起来的,这些module层层依赖关系,在eclipse用着是真舒服,可是部署上线完全没有打包的思路。        问题:如何把这些打成一个完整war用于部署阿里云?求懂的老师细解!...
  • 本文会详细介绍如何在centos7上安装matlab2018a,介绍安装jdk8,安装mysql,安装tomcat等一系列问题 在项目开发中由于需要用到matlab中的函数,但是项目需要上线,所以在经过几天的不断尝试后找到了一种比较耗时间...
  • 终于,经过一次次需求和设计的更改,项目终于快要上线了。可是最近几天,我发现了一个特别古怪的问题: 一般来说,我们更新网站,就是把最近的class文件,替换掉原先的class文件即可。服务器如Tomcat会自动为我们...
  • Spring Boot应用部署于外置Tomcat容器 Spring Boot热部署加持 数据库密码配置项都不加密?心也太大了! 谁要是再敢用Map传参,我过去就是一JIO 前后端分离 这次要讲不清前后端分离,我都怎么地! 前后端分离的...
  • 【JavaWeb基础】图书管理系统【部署开发环境、解决分类、图书、前台页面模块】 【JavaWeb基础】图书管理系统【用户、购买、订单模块、添加权限】 【JavaWeb基础】图书管理系统总结 :hamburger:Hibernate ...
  • 一文带你搞懂API网关

    2020-12-28 06:45:32
    项目会变的非常复杂且很难维护。</li><li>后期如果需要对微服务进行重构的话,也会变的非常麻烦,需要客户端配合你一起进行改造,比如商品服务,随着业务变的越来越复杂,后期需要...

空空如也

空空如也

1 2
收藏数 29
精华内容 11
关键字:

tomcat项目如何部署上线