SSM - down (pull hook education background management system - front end)

Posted by rahnel on Tue, 14 Dec 2021 22:31:23 +0100

Phase VI module V

Task 1 front end development of curriculum and advertising module

1.Vue review

1.1 project structure description

We use scaffolding to quickly build the Vue project

|--- edu-boss entry name
    |--- node_modules Directory where dependent packages are stored
    |--- public Static resource management directory
    |--- src Component source directory(The code we wrote)
        |--- assets Store static picture resources(CSS You can also put it here)
        |--- components Storage of foundation components,Reusable
        |--- router The project routing file is stored
        |--- services Storage request background JS file,
        |--- store Save shared data between components
        |--- utils Manage public JS file
        |--- views Placed components are public(Each main page)
        |--- App.vue app.vue It can be regarded as the home page of the website vue The main component of the project, page entry file
        |--- main.js Package the entry file for operation, and introduce vue Module and app.vue Components and routing route
    |--- babel.config.js babel configuration file, Transcoding source code(hold es6=>es5)
    |--- package.json Dependency profiles for projects and tools
    |--- paxkage-lock.json Dependency profile
    |--- README.md Project description
    |--- vue.config.js Custom profile

1.2 Views directory description

Page section of front-end project

CourseManage: course management
AdvertiseManage: Advertising management
PermissionManage: Authority management
CommentManage:public
Users.vue: user management 
Login.vue: Sign in

1.3 vue component development

Every * * * vue files can be regarded as a component**

Components of components:

template: the HTML part of the component

Script: JS script of the component (written using ES6 syntax)

Style: CSS style of the component

<!-- 1.template representative html structure, template The content in must have and only have one root element. The static part of the page is view part -->
<template>
    <div>
        Test page...
    </div>
</template>

<!-- 2.to write vue.js code -->
<script>
    //You can import its components
    // import Header from '../components/header.vue'
    //The default writing method is to output the component
    export default {
        name:"Home", // Component name, used for route jump in the future
        data() {// Data to be used in the current component
            return {}
        },
        methods: {}
    }
</script>

<!-- Write style code for the current component -->
<style scoped>
    /* Page style plus scoped presentation style is only valid in the current component*/
</style>

2. Course module review

2.1 course data display

Before you start writing front-end code, import the latest database script file

2.1. 1 functional analysis

Course.vue component to display course data and query conditions

Use ElementUI table for data display https://element.eleme.cn/#/zh-CN/component/table

2.1. 2. JS code writing

1. Define data section

// Data part
data() {
    // query criteria
    const filter = {
        courseName: "",
        status: ""
    };

    return {
        filter,
        courses: [], // Course data
        loading: false
    };
},

// Hook function
created() {
    this.loadCourses();
},

2. Write the method of querying course data according to the interface document

// Get course data
loadCourses() {
    this.loading = true;
    const data = {};

    if (this.filter.courseName) data.courseName = this.filter.courseName;
    if (this.filter.status) data.status = this.filter.status;

    // Send request
    return axios
        .post("/course/findAllCourse", data)
        .then(resp => {
        this.courses = resp.data.content;
        this.loading = false;
    })
        .catch(error => {
        this.$message.error("Data acquisition failed! ! !");
    });
},

3. Condition query (the same interface is accessed)

<el-button @click="handleFilter()">query</el-button>
// Condition query
handleFilter() {
    this.loadCourses();
},

2.2 new courses

2.2. 1 functional analysis

Click new to navigate the route to courseitem vue

<el-button type="primary" icon="el-icon-plus" @click="handleAdd">New course</elbutton>
//New course route jump
handleAdd() {
    //The parameter new of the route means new
    this.$router.push({ name: "CourseItem", params: { courseId: "new" } });
},

router.js

{
    path: "/courses/:courseId",
    name: "CourseItem",
    meta: { requireAuth: true, title: "Course Details " },
    component: () =>
        import(/* webpackChunkName: 'courses' */ "../views/CourseManage/CourseItem.vue")
},

The CourseItem component uses the form in the ElementUI to submit course data https://element.eleme.cn/#/zh-CN/component/form

2.2. 2. JS code writing
<el-button type="primary" @click="handleSave">preservation</el-button>
//Save and modify course information
handleSave() {

    //Check whether the rules are met
    this.$refs.form.validate(valid => {
        if (!valid) return false;
        axios
            .post("/course/saveOrUpdateCourse", this.course)
            .then(res => {
            //Return to previous page
            this.$router.back();
        })
            .catch(error => {
            this.$message.error("Failed to save course information! ! !");
        });
    });
},

2.3 course picture upload

2.3. 1 functional analysis

In the SSM front-end project, the image upload function uses the common component uploadimage vue

In courseitem Vue, this component is introduced

import UploadImage from "@/components/UploadImage.vue";
<el-form-item label="Course cover" prop="courseImgUrl">
    <!-- Using the picture upload component,Complete image upload -->
    <upload-image
                  :content="course.courseImgUrl && [course.courseImgUrl]"
                  :get-urls="getCourseImgUrl"
                  uploadUrl="/course/courseUpload"
                  ref="courseCoverRef"
                  max="10M"
                  tipInfo="Recommended size: 230*300px,JPG,PNG Format, picture less than 10 M"
                  ></upload-image>
</el-form-item>
2.3. 2 case demonstration

In order to better understand the use of the image upload component, we create a Vue project to demonstrate the use of the image upload component

  1. Import the prepared Vue foundation project my ssmweb

  2. Create an uploadimage in the components directory Vue component

  3. View the ElementUI documentation and copy the code to uploadimage vue https://element.eleme.cn/#/zh -CN/component/upload

<template>
    <div>
        <el-upload
                   action="https://jsonplaceholder.typicode.com/posts/"
                   list-type="picture-card"
                   :on-preview="handlePictureCardPreview"
                   :on-remove="handleRemove"
                   >
            <i class="el-icon-plus"></i>
        </el-upload>
        <el-dialog :visible.sync="dialogVisible">
            <img width="100%" :src="dialogImageUrl" alt />
        </el-dialog>
    </div>
</template>
<script>
    export default {
        data() {
            return {
                dialogImageUrl: "",
                dialogVisible: false
            };
        },
        methods: {
            handleRemove(file, fileList) {
                console.log(file, fileList);
            },
            handlePictureCardPreview(file) {
                this.dialogImageUrl = file.url;
                this.dialogVisible = true;
            }
        }
    };
</script>
<style scoped>
</style>
  1. Configure routing
//Layout routing
{
    path: "/index",
    name: "index",
    component: Index,
    //Add a sub route and use the children attribute to represent the sub route
    children: [
        //Picture upload sub route
        {
            path: "/upload",
            name: "upload",
            component: UploadImage,
        },
    ],
},
  1. In index Add a picture upload option to the Vue navigation menu location
<el-menu-item-group>
    <!-- modify index Routing address for -->
    <el-menu-item index="/upload">
        <i class="el-icon-menu"></i>Picture upload
    </el-menu-item>
</el-menu-item-group>
  1. Visit the page to test
2.3. 3 attribute description
parameterexplaintype
actionRequired parameter, upload addressstring
multipeSupport multiple filesboolean
limitMaximum number of uploads allowednumber
before-uploadThe hook before uploading a file. The parameter is the uploaded filefunction(file)
on-successHook when the file is uploaded successfullyfunction(respone,file,fileList)
on-removeHook when removing files from file listfunction(file,fileList)
on-exceedHook when the number of files exceeds the limitfunction(files,fileList)
file-listList of uploaded filesarray
2.3. 4 introduction of components

How do I introduce one component into another? Next, let's demonstrate the introduction of the picture component

  1. Create a testuplopad Vue component
<template>
    <div>
        <!-- Use components,Pay attention to the use of short horizontal lines -->
        <upload-image></upload-image>
    </div>
</template>
<script>
    //1. Import components
    import UploadImage from "@/components/UploadImage";
    export default {
        //2. Register components
        components: {
            UploadImage
        }
    };
</script>
<style scoped>
</style>
2.3. 5 transmission parameters of components

UploadImage.vue

/*
Component transfer parameters
uploadUrl:Image upload path,
getUrl: function
*/
props: ["uploadUrl", "getUrl"],
data() {
    return {
        uploadAction: this.uploadUrl
    };
},
//Callback function after successful upload
uploadSuccess(res, file) {
    this.getUrl(file);
}
<el-upload
    :action="uploadAction"
    list-type="picture-card"
    :on-preview="handlePictureCardPreview"
    :on-remove="handleRemove"
    :on-success="uploadSuccess"
    >

TestUpload.vue

<template>
    <div>
        <!-- Use components,Pay attention to the use of short horizontal lines ,Two parameters were passed to the parent component
			uploadUrl: Picture upload address
			:get-url:Passed a function
		-->
        <upload-image
		uploadUrl="https://jsonplaceholder.typicode.com/posts/"
        :get-url="show">
        </upload-image>
    </div>
</template>
methods: {
    show(file) {
        console.log(file.name);
    }
}
2.3. 6 course module picture upload

CourseItem.vue

The picture upload component is introduced and used

<el-form-item label="Course cover" prop="courseImgUrl">
    <!-- Using the picture upload component,Complete image upload -->
    <upload-image
	:content="course.courseImgUrl && [course.courseImgUrl]"
	:get-urls="getCourseImgUrl"
	uploadUrl="/course/courseUpload"
	ref="courseCoverRef"
	max="10M"
	tipInfo="Recommended size: 230*300px,JPG,PNG Format, picture less than 10 M"
	></upload-image>
</el-form-item>
import UploadImage from "@/components/UploadImage.vue";
export default {
  name: "CourseItem",
  title: "Marketing information",
  components: { Editor, UploadImage },
}

2.4 modifying courses

  1. Click Edit to carry the id of the current data and navigate to courseitem vue
<el-button size="mini" @click="handleNavigate('CourseItem', scope.row.id)">edit</el-button>
//Course editing & Content Management
handleNavigate(name, id) {
    this.$router.push({ name, params: { courseId: id } });
},
  1. In the hook function of the CourseItem component, judgment will be made. If it is modified, the corresponding course data will be obtained first and echoed
//Hook function
created() {
    //Get course id
    const id = this.$route.params.courseId;
    if (!id) return this.redirectToError();
    //Judge whether to create or modify
    if (id === "new") {
        this.pathTitle = "New courses";
        this.$breadcrumbs = [
            { name: "Courses", text: "course management" },
            { text: "New courses" }
        ];
    } else {
        this.$breadcrumbs = [
            { name: "Courses", text: "course management" },
            { text: "Marketing information" }
        ];
        this.loadCourse(id);
    }
},
//Echo course information
loadCourse(id) {
    this.loading = true;
    return axios
        .get("/course/findCourseById?id=" + id)
        .then(resp => {
        console.log(resp);
        this.pathTitle = resp.data.content.courseName;
        this.course = Object.assign(this.course, resp.data.content);
        this.course.id = id;
        this.loading = false;
    })
        .catch(error => {
        this.$message.error("Echo data failed! !");
    });
},
  1. Modifying a course and adding a course use the same background interface. The difference is that the modification operation must carry an ID

2.5 course status management

Click on or off the shelf to complete the switching of course status

<el-button size="mini" type="danger" v-if="scope.row.status === 1" @click="handleToggleStatus(scope.row)">Off the shelf</el-button>
<el-button size="mini" type="success" v-else-if="scope.row.status === 0"
@click="handleToggleStatus(scope.row)">Put on the shelf</el-button>
//Switch course status
handleToggleStatus(item) {
    //Set latest status
    const toggledStatus = 1 - item.status;
    //Request background interface
    axios
        .get("/course/updateCourseStatus", {
        params: {
            status: toggledStatus,
            id: item.id
        }
    })
        .then(res => {
        debugger;
        //Set the latest value
        item.status = toggledStatus;
        console.log(item);
        //Reload page
        window.location.reload;
    })
        .catch(error => {
        this.$message.error("Status modification failed! ! !");
    });
},

2.6 course content management

2.6. 1 get course content data

The course content data includes chapter and class information. Query the chapter and class information contained in the course according to the course ID

<el-button size="mini" @click="handleNavigate('CourseSections', scope.row.id)">Content management
</el-button>
created() {
    //1. Display the position of the current page in the website
    this.$breadcrumbs = [
        { name: "Courses", text: "course management" },
        { text: "Course structure" }
    ];
    //2. Get the passed parameter id from the route
    const id = this.$route.params.courseId;
    if (!id) return this.redirectToError();
    this.loading = true;
    //3. Load course information
    this.loadCourse(id);
    //4. Load course content
    this.loadSections(id);
},
//Load course information
loadCourse(id) {
    axios
        .get("/courseContent/findCourseByCourseId?courseId=" + id)
        .then(res => {
        const course = res.data.content;
        //Save data to chapter form object
        this.addSectionForm.courseId = course.id;
        this.addSectionForm.courseName = course.courseName;
        //Save data to session form object
        this.addLessonForm.courseId = course.id;
        this.addLessonForm.courseName = course.courseName;
    })
        .catch(error => {
        this.$message.error("Data acquisition failed! ! !");
    });
},
//Load course content (tree structure)
loadSections(courseId) {
    this.loading = true;
    axios
        .get("/courseContent/findSectionAndLesson?courseId=" + courseId)
        .then(res => {
        this.sections = res.data.content;
        console.log(res.data.content);
        this.loading = false;
    })
        .catch(error => {
        this.$message.error("Data acquisition failed! ! !");
    });
},
2.6. Chapter 2 management

New chapter

<el-button type="primary" icon="el-icon-plus" @click="handleShowAddSection">Add chapter</el-button>

When a new chapter is added, the course name corresponding to the chapter needs to be echoed

//Show new chapter form
handleShowAddSection() {
    this.addSectionForm = {
        courseId: this.addSectionForm.courseId,
        courseName: this.addSectionForm.courseName
    };
    this.showAddSection = true;
},

Modify chapter

<el-button size="small" @click.stop="handleEditSection(data)">edit</el-button>
//Edit chapter (echo)
handleEditSection(section) {
    this.addSectionForm = Object.assign(this.addSectionForm, section);
    this.showAddSection = true;
},

Adding and modifying chapters access the same interface

//Add & modify chapters
handleAddSection() {
    axios
        .post("/courseContent/saveOrUpdateSection", this.addSectionForm)
        .then(res => {
        this.showAddSection = false;
        //Reload list
        return this.loadSections(this.addSectionForm.courseId);
    })
        .then(() => {
        //Reset form content
        this.addSectionForm.sectionName = "";
        this.addSectionForm.description = "";
        this.addSectionForm.orderNum = 0;
        this.reload();
    })
        .catch(error => {
        this.showAddSection = false;
        this.$message.error("Operation execution failed! ! !");
    });
},

Chapter status

There are three chapter states

//status information 
const statusMapping = {
    0: "Hidden",
    1: "To be updated",
    2: "Updated"
};

Select the status and click OK to modify the status

<el-button type="primary" @click="handleToggleStatus">determine</el-button>
//Modify chapter status
handleToggleStatus() {
    //Judge the status to be modified
    if (this.toggleStatusForm.data.sectionName) {
        //Modify chapter status
        axios
            .get("/courseContent/updateSectionStatus", {
            params: {
                id: this.toggleStatusForm.id,
                status: this.toggleStatusForm.status
            }
        })
            .then(resp => {
            this.toggleStatusForm.data.status = this.toggleStatusForm.status;
            this.toggleStatusForm = {};
            this.showStatusForm = false;
            this.reload();
        })
            .catch(error => {
            this.showStatusForm = false;
            this.$message.error("Failed to modify status! ! !");
        });
    } else {
        //Modify class hour status
    }
},
2.6. 3 class hour management

Class hour management includes: class hour addition, class hour modification and class hour status management. It is basically the same as chapter management

3. Advertising module

3.1 advertising space management

3.1 display of advertising space

AdvertiseSpaces.vue component, which is the advertising space page

JS part

data() {
    return {
        list: null,
        listLoading: false
    };
},
created() {
    //Load ad space data
    this.loadPromotionSpace();
},
//Method 1: load advertising space information
loadPromotionSpace() {
    this.listLoading = true;

    // Request the background interface to obtain advertising space list data
    axios.get("/PromotionSpace/findAllPromotionSpace").then(res => {
        this.list = res.data.content;
        this.listLoading = false;
    }).catch(error => {
        this.$message("Failed to load data!!!");
    });

},
3.2 add advertising space

1) Click the Add button to navigate to the specified component through the route

<el-button size="mini" class="btn-add" @click="handleAdd()">Add ad space</el-button>
//Add ad space jump
handleAdd() {
    this.$router.push({ path: "/addAdvertiseSpace" });
},

2) View routing router JS, jump to addadvertisespace vue

{
    path: "addAdvertiseSpace",
    name: "AddAdvertiseSpace",
    component: () => import("../views/AdvertiseManage/AddAdvertiseSpace"),
    meta: { requireAuth: true, title: "Add ad space" }
},

3) View addadvertisespace vue

<template>
    <home-advertise-detail :isEdit="false"></home-advertise-detail>
</template>

<script>
    import HomeAdvertiseDetail from "./AdvertiseSpaceDetail";

    export default {
        name: "addHomeAdvertise",
        title: "Add ad space",
        components: { HomeAdvertiseDetail }
    };
</script>
<style></style>

4) The component that is actually displayed is advertisespacedetail vue

First, judge whether to add or modify. According to isEdit, true means modify and false means add

//Hook function
created() {

    // Add or modify during judgment
    if(this.isEdit){
        // modify
        const id = this.$route.query.id;
        this.loadPromotionSpace(id);
    }else{
        // newly added
        this.homeAdvertise = {};
    }
},

newly added

//Method 1: save advertising space information
handleSave() {
    this.$refs.form.validate(valid => {
        if(!valid) return false;

        // Request background
        axios.post(
            "/PromotionSpace/saveOrUpdatePromotionSpace",
            this.homeAdvertise
        )
            .then(res => {
            // Return to previous page
            this.$router.back();
        }).catch(error =>{
            this.$message("Data processing failed!!");
        });

    });
},
3.3 modification of advertising space

It is necessary to request the background interface to echo the advertising space information

//Method 2: echo advertising space information
loadPromotionSpace(id) {
    return axios.get("/PromotionSpace/findPromotionSpaceById?id=" + id)
        .then(res => {
        Object.assign(this.homeAdvertise,res.data.content);
        this.homeAdvertise.id = id;
    })
	.catch(error =>{
        this.$message("Data processing failed!!");
    });
}

3.2 advertising management

3.2.1 ElementUI paging component

Advertises.vue component, which is the advertisement list page

The display of the advertisement list uses the paging component. Next, a case is used to demonstrate the use of the paging plug-in

https://element.eleme.cn/#/zh-CN/component/pagination

1. Quick use
  1. In the test project, create a pagelist Vue, copy the code as follows
<template>
    <div>
        <div class="block">
            <span class="demonstration">Complete function</span>
            <!-- 
                current-change Current page
                page-sizes="[100, 200, 300, 400]" Show bars per page option
                page-size Number of displays per page
                total Total number
             -->
            <el-pagination
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
            :current-page="currentPage4"
            :page-sizes="[10, 20, 30, 40]"
            :page-size="100"
            layout="total, sizes, prev, pager, next, jumper"
            :total="40">
            </el-pagination>
        </div>
    </div>
</template>
<script>
  export default {
    methods: {
      
      // Triggered when the number of displays changes
      handleSizeChange(val) {
        console.log(`each page ${val} strip`);
      },

      // Triggered when the current page changes
      handleCurrentChange(val) {
        console.log(`Current page: ${val}`);
      }
    },
    data() {
      return {
        currentPage4: 4
      };
    }
  }
</script>
2. Attribute introduction
parameterexplaintype
page-sizeNumber of displays per pageint
current-pageCurrent pageint
totalTotal numberint
page-sizesNumber of options displayed per page[10,20,30]
layoutComponent layout

analysis:

Page size and current page are data that needs to be transmitted from the front end to the back end

total and list data need to be returned from the back end to the front end

3. Event introduction
eventexplainCallback function
size-changeTriggered when the number of displays per page changesNumber of entries per page
current-changeTriggered when the current page changesCurrent page
4. Case demonstration
  1. Copy the following code to PageList
<template>
  <div class="app-container">
    <div class="table-container">
      <el-table
        ref="homeAdvertiseTable"
        :data="list"
        style="width: 100%;"
        v-loading="listLoading"
        border
      >
        <el-table-column label="id" width="120" align="center">
          <template slot-scope="scope">{{scope.row.id}}</template>
        </el-table-column>
        <el-table-column label="Advertisement name" align="center">
          <template slot-scope="scope">{{scope.row.name}}</template>
        </el-table-column>

        <el-table-column label="Advertising pictures" width="120" align="center">
          <template slot-scope="scope">
            <img style="height: 80px" :src="scope.row.img" />
          </template>
        </el-table-column>
      </el-table>
    </div>
    <!-- paging -->
    <div class="pagination-container">
      <el-pagination
        background
        @size-change="handlePageSizeChange"
        @current-change="handleCurrentPageChange"
        layout="total, sizes,prev, pager, next,jumper"
        :current-page="page"
        :page-sizes="[5,10, 20]"
        :page-size="size"
        :total="total"
      ></el-pagination>
    </div>
  </div>
</template>
  1. Write JS code
<script>
  export default {
    
    //Data part
    data() {
      return {
          list:[], //Guang'ao data background
          page: 1, //Front of current page
          size: 5, //Number of bars per page
          total: 0,  //Total number background
          listLoading:true
      };
    },
    // Hook function
    created() {
        this.loadList();
    },

    methods: {  
        // Get advertising data
        loadList(){
            return this.axios.get("http://192.168.3.5:8080/ssm_web/PromotionAd/findAllPromotionAdByPage",{ params: {
                currentPage:this.page,
                pageSize:this.size
            }})
            .then(res =>{
                this.list = res.data.content.list;
                this.total = res.data.content.total;
                this.listLoading = false;
            })
            .catch(error =>{
                this.$message("Data acquisition failed!");
            });
        },
        // Triggered when the number of displays changes
        handlePageSizeChange(size) {
            this.size = size;
            this.loadList();
        },

        // Triggered when the current page changes
        handleCurrentPageChange(page) {
            this.page = page;
            this.loadList();
        }
    },
  }
</script>
3.2. 2. Advertisement list display
1. Demand analysis

We have solved the paging problem. Next, let's look at what the advertising page should display:

The display data of advertising list comes from two tables:

​ promotion_ad advertising form

​ promotion_space advertising space table

2. Function realization
  1. Data part
//Data part
data() {
    return {
        typeOptions:[], //Drop down list display
        typeMap: {}, //Save advertising space object information
        total: 0, //Total number
        size: 5, //Number of displays per page
        page: 1, //Current page
        list: [], //Advertising data
        listLoading: false
    };
},
  1. Hook function
//Hook function
created() {
    //Get ad list data
    this.loadPromotionAd();

    //Get advertising space data
    this.loadPromotionSpace();
},
  1. Function part
//Method 1; Get ad list data
loadPromotionAd() {

    this.listLoading = true;

    return axios
        .get("http://192.168.3.5:8080/ssm_web/PromotionAd/findAllPromotionAdByPage",{
            params:{
                currentPage:this.page,
                pageSize:this.size
            }
        })
        .then(res =>{
            this.list = res.data.content.list;
            this.total = res.data.content.total;
            this.listLoading = false;
        })
        .catch(error =>{});
},

//Method 2: obtain advertising location data
loadPromotionSpace() {
    this.listLoading = true;

    return axios
        .get("/PromotionSpace/findAllPromotionSpace")
        .then(res =>{
            // Traversal using map
            res.data.content.map(item => {
                // Save the data to typemap. The key is the ID and the value is the advertising space object
                this.typeMap[item.id] = item;
            });
            this.listLoading = false;
        });
},

//Method 3: get the advertisement location name
getSpaceName(spaceId) {
    if(!spaceId){
        return "";
    }

    return this.typeMap[spaceId] && this.typeMap[spaceId].name;
},
3.2. 3. Modification of advertising status

Demand analysis: click the button to modify the status, 0 offline and 1 online

Function realization

The page part uses the El switch component

Active value: the value when the switch is turned on

Inactive value: the value when the switch is closed

<!-- Online and offline -->
<el-table-column label="go online/Offline" width="120" align="center">
    <template slot-scope="scope">
		<el-switch
           @change="handleUpdateStatus(scope.row)"
           :active-value="1"
           :inactive-value="0"
           v-model="scope.row.status"
        ></el-switch>
    </template>
</el-table-column>

JS part

 //Method 4: modify status
    handleUpdateStatus(row) {
      
      this.$confirm('Do you want to modify the upper limit line/Offline status?', 'Tips', {
          confirmButtonText: 'determine',
          cancelButtonText: 'cancel',
          type: 'warning'
        }).then(() =>{
          axios.get("/PromotionAd/updatePromotionAdStatus",{
            params:{
              id:row.id,
              status:row.status
            }
          })
          .then(res =>{
            this.loadPromotionAd();
          })
          .catch(error =>{
            this.$message("Failed to modify status!!!");
          });
        })
    },
3.3. 4 advertising Addition & modification
1. Demand analysis
  1. Click Add advertisement to trigger the event
<el-button size="mini" class="btn-add" @click="handleAdd()">Add ad</el-button>
  1. Route navigate to the specified component
//Jump to new
handleAdd() {
    this.$router.push({ path: "/addAdvertise" });
},
  1. View the routing information and jump to addadvertisement Vue component
{
    path: "addAdvertise",
    name: "AddAdvertise",
    component: () => import("../views/AdvertiseManage/AddAdvertise"),
    meta: { requireAuth: true, title: "Add ad" }
},
  1. AddAdvertise.vue component

In the addadvertisement component, the AdvertiseDetail component is introduced, and the real operation is completed in this component

: isEdit = "false": false indicates a new operation

<template>
  <home-advertise-detail :isEdit="false"></home-advertise-detail>
</template>
<script>
import HomeAdvertiseDetail from './AdvertiseDetail'
export default {
  name: 'addHomeAdvertise',
  title: 'Add ad',
  components: { HomeAdvertiseDetail }
}
</script>
<style></style>
  1. AdvertiseDetail.vue component

This component is a page for adding and modifying advertisements

2. Function realization

Data part

//Data part
data() {
    return {
        homeAdvertise, //Ad form object
        typeOptions: [], //Ad space drop-down list
        rules
    };
},

Hook function

//Hook function
created() {
    //Judge whether to add or modify
    if (this.isEdit) {
        //modify
        const id = this.$route.query.id;
        this.loadPromotion(id);
    } else {
        //newly added
        this.homeAdvertise = {};
    }

    //Get advertising space data
    this.loadPromotionSpace();
},

method

//Method 1: obtain advertising location data
loadPromotionSpace() {
    return axios.get("/PromotionSpace/findAllPromotionSpace").then(res =>{
        //Use the map function to traverse, obtain the AD Slot id and name, and save them to typeOptions
        this.typeOptions = res.data.content.map(item =>{

            return {label:item.name,value:item.id};
        })
    })
},

//Method 2: save advertising information
handleSave() {

    this.$refs.form.validate(valid =>{

        if(!valid) return false;

        axios
            .post("/PromotionAd/saveOrUpdatePromotionAd",this.homeAdvertise)
            .then(res =>{
                // Return to the previous page and refresh
                this.$router.back();
            })
            .catch(error =>{});
    });

},

//Method 3: modify echo advertising information
loadPromotion(id) {
    return axios.get("/PromotionAd/findPromotionAdById?id=" +id).then(res =>{
        Object.assign(this.homeAdvertise,res.data.content);
        this.homeAdvertise.id = id;
    }).catch(error =>{});
},

Task 2 front end development of authority management module

1. User management

1.1 paging & query user data by criteria

Query criteria:

	1. User mobile number

 		2. Registration time,Include start and end dates
1.1. 1 date selector assembly

The date selector in the element UI is used in the query criteria. Let's briefly learn about the use of date selector

https://element.eleme.cn/#/zh-CN/component/date-picker#mo-ren-xian-shi-ri-qi

  1. In the test project, create a testdate Vue component, copy code to page
<template>
    <div class="block">
        <span class="demonstration">With shortcut options</span>
        <el-date-picker
            v-model="dateTime"
            type="daterange"
            align="right"
            unlink-panels
            range-separator="to"
            start-placeholder="Start date"
            end-placeholder="End date"
            :picker-options="pickerOptions">
        </el-date-picker>

        <el-button type="primary" @click="getDate">query</el-button>
    </div>
</template>

<script>
  export default {
    data() {
      return {
        pickerOptions: {
          shortcuts: [{
            text: 'Last week',
            onClick(picker) {
              const end = new Date();
              const start = new Date();
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
              picker.$emit('pick', [start, end]);
            }
          }, {
            text: 'Last month',
            onClick(picker) {
              const end = new Date();
              const start = new Date();
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
              picker.$emit('pick', [start, end]);
            }
          }, {
            text: 'Last three months',
            onClick(picker) {
              const end = new Date();
              const start = new Date();
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
              picker.$emit('pick', [start, end]);
            }
          }]
        },
        dateTime: ''
      };
    },

    methods: {
        getDate(){
            const params = {};

            params.startCreateTime = this.dateTime[0];
            params.startCreateTime.setHours(0);
            params.startCreateTime.setMinutes(0);
            params.startCreateTime.setSeconds(0);

            params.endCreateTime = this.dateTime[1];
            params.endCreateTime.setHours(23);
            params.endCreateTime.setMinutes(59);
            params.endCreateTime.setSeconds(59);

            console.log(params);
        }
    },
  };
</script>
1.1. 2 function realization

Data part

//Data part
return {
    pickerOptions, //Date selector option settings
    total: 0, //Total number
    size: 10, //Number of displays per page
    page: 1, //Current page
    filter,
    users: [], //user data
    loading: false,
    allocAdminId: "",
    allocDialogVisible: false,
    allocRoleIds: [],
    allRoleList: []
};

JS part

created() {
    //Initialize user data
    this.loadUsers();
},

//Method 1: load user data
loadUsers() {
    this.loading = true;

    // Set parameters
    const params = {currentPage:this.page,pageSize:this.size};

    // Filter condition
    if(this.filter.username) params.username = this.filter.username;

    //Set date parameters
    if (this.filter.resTime) {
        params.startCreateTime = this.filter.resTime[0];
        params.startCreateTime.setHours(0);
        params.startCreateTime.setMinutes(0);
        params.startCreateTime.setSeconds(0);

        params.endCreateTime = this.filter.resTime[1];
        params.endCreateTime.setHours(23);
        params.endCreateTime.setMinutes(59);
        params.endCreateTime.setSeconds(59);
    }

    // Request background interface
    return axios
        .post("/user/findAllUserByPage",params)
        .then(res =>{
            this.users = res.data.content.list; // user data
            this.total = res.data.content.total;
            this.loading = false;
        }).catch(error =>{
        this.$message("Failed to get data!!!");
    })

},

1.2 user status setting

Status button

<el-button size="mini" type="text" @click="handleToggleStatus(scope.row)">
{{ scope.row.status == "ENABLE" ? "Disable" : "Enable" }}</el-button>

JS part

//Method 2: modify user status
handleToggleStatus(item) {

    return axios
        .get("/user/updateUserStatus",{
            params:{
                id:item.id,
                status:item.status
            }
        })
        .then(res =>{
            item.status = res.data.content;
        })
        .catch(error =>{
            this.$message("Status modification failed!!!");
        });

},

2. Authority management

2.1 role management

2.1. 1 display & Query role list

The role component is roles Vue, which manages role information in this component

Function realization

  1. Data part
// Data part
data() {
    return {
        listQuery: { name: "" },
        list: null,
        listLoading: false,
        dialogVisible: false,
        role: Object.assign({}, defaultRole),
        isEdit: false
    };
},
  1. Hook function, call loadRoles to obtain role data
created() {
    //Get role list
    this.loadRoles();
},
//Get role data
loadRoles() {
    return axios
        .post("/role/findAllRole",this.listQuery)
        .then(res =>{
            this.list = res.data.content;
            this.listLoading = false;
        })
        .catch(error =>{
            this.$message("Failed to get data!!!");
        });
},
  1. The parameter carried by the request is: listQuery
<el-input v-model="listQuery.name" class="input-width" placeholder="Role name" clearable></el-input>
//Condition query
handleSearchList() {
    this.loadRoles();
},
2.1. 2 add & modify roles
  1. Page section
<el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left:20px">Add role</el-button>
  1. How to open the add role window
//Add character pop-up
handleAdd() {
    this.dialogVisible = true; //open a dialog box
    this.isEdit = false; //false add operation
    this.role = Object.assign({}, defaultRole); // Create a role object
},
  1. Add role dialogue and use v-model for two-way data binding
<!-- add to&Modify role dialog box -->
<el-dialog :title="isEdit?'Edit role':'Add role'" :visible.sync="dialogVisible" width="40%">
    <el-form :model="role" label-width="150px" size="small">
        <el-form-item label="Role name:">
            <el-input v-model="role.name" style="width: 250px"></el-input>
        </el-form-item>
        <el-form-item label="Role code:">
            <el-input v-model="role.code" style="width: 250px"></el-input>
        </el-form-item>
        <el-form-item label="Description:">
            <el-input v-model="role.description" type="textarea" :rows="5" style="width: 250px"></el-input>
        </el-form-item>
    </el-form>
    <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false" size="small">Cancel</el-button>
        <el-button type="primary" @click="handleSave()" size="small">determine</el-button>
    </span>
</el-dialog>
  1. Add role method
//Add & modify roles
handleSave() {
    axios.post("/role/saveOrUpdateRole",this.role)
        .then(res =>{
            this.dialogVisible = false;
            this.loadRoles();
        })
        .catch(error =>{
            this.$message("Failed to save role!!!");
        });
},
  1. How to modify roles

Click Modify to transfer the data object of the current line

<el-button size="mini" type="text" @click="handleUpdate( scope.row)">edit</elbutton>

Display dialog box, echo data

//Modify character pop-up
handleUpdate(row) {
    this.dialogVisible = true;
    this.isEdit = true;
    //Echo data
    this.role = Object.assign({}, row);
},

Modify the role or call the handleSave method

2.1. 3 delete role
<el-button size="mini" type="text" @click="handleDelete(scope.row)">delete</elbutton>

The MessageBox pop-up box in ElementIUI is used here

https://element.eleme.cn/#/zh-CN/component/message-box#options

//Delete role
handleDelete(row) {
    this.$confirm('Do you want to delete this role', 'Tips', {
        confirmButtonText: 'determine',
        cancelButtonText: 'cancel',
        type: 'warning'
    }).then(() => {
        // Request background
        axios.get("/role/deleteRole?id=" + row.id)
            .then(res =>{
                this.loadRoles();
            })
            .catch(error =>{
                this.$message("Operation failed!!!");
            });
    });
},
2.1. 4 assign a menu to a role
1. Demand analysis

Assign menus to roles. A role can have multiple menu permissions

A menu permission can also be owned by multiple roles

The relationship between roles and menus is many to many

Click the allocation menu to display the effect on the page

Effect to be achieved by the front end

Step 1: get all the menu data and display it in the tree control

Step 2: check the menu permissions owned by the current role

2. Realization of menu display function
  1. Click the assign menu button to transfer the data of the current line
<el-button size="mini" type="text" @click="handleSelectMenu(scope.row)">Assign menu</el-button>
  1. Route navigation to allocMenu
//Assign a menu to a role
handleSelectMenu(row) {
    this.$router.push({ path: "/allocMenu", query: { roleId: row.id } });
},
  1. routes.js
{
    path: "allocMenu",
    name: "AllocMenu",
    component: () =>
        import(
        /* webpackChunkName: 'allocMenu' */ "../views/PermissionManage/AllocMenu"
    ),
    meta: { requireAuth: true, title: "Role menu management" }
},
  1. In allocmenu Complete the menu operation of assigning roles in the Vue component

  2. Data part

data() {
    return {
        menuTreeList: [], //Menu data
        checkedMenuId: [], //Selected menu

        //Tree structure child node settings
        defaultProps: {
            children: "subMenuList",
            label: "name"
        },
        roleId: null
    };
},
  1. Hook function
//Hook function
created() {
    //Get the id carried by the route
    this.roleId = this.$route.query.roleId;

    //Get menu list
    this.treeList();

    //Get the menu information owned by the role
    this.getRoleMenu(this.roleId);
},
//Method 1: get the menu list and use the tree control to display it
treeList() {
    axios.get("/role/findAllMenu").then(res =>{
        console.log(res.data.content);
        // Gets the data required by the tree control
        this.menuTreeList = res.data.content.parentMenuList;
    });

},

//Method 2: get the menu list id owned by the current role
getRoleMenu(roleId) {
    axios.get("/role/findMenuByRoleId?roleId=" + roleId)
        .then(res =>{
            console.log(res.data.content);
            // Check the existing menu permissions of the role
            this.$refs.tree.setCheckedKeys(res.data.content);
        });
},
3. Realization of distribution menu function

Assign menu button

<div style="margin-top: 20px" align="center">
    <el-button type="primary" @click="handleSave()">preservation</el-button>
    <el-button @click="handleClear()">empty</el-button>
</div>

method

//Method 3: modify the menu list owned by the role
handleSave() {

    // 1. Get all selected nodes
    const checkedNodes = this.$refs.tree.getCheckedNodes();

    // 2. Define the array save menu id
    const checkedMenuIds = [];

    // 3. Traverse to get the menu id
    if(checkedNodes != null && checkedNodes.length > 0){
        //Traversal to get node object
        for(let i = 0; i < checkedNodes.length; i++){
            const checkedNode = checkedNodes[i];

            // Save menu id
            checkedMenuIds.push(checkedNode.id);

            // Judge if the current node is a child node & & the id of the parent node of the child node has not been saved
            if(checkedNode.parentId !== -1 && checkedMenuIds.filter(item => checkedNode.parentId).length === 0){
                checkedMenuIds.push(checkedNode.parentId);
            }
        }
    }

    console.log(checkedMenuIds);

    // 4. Send a request to the background to complete the operation of assigning a menu to the role
    this.$confirm('Assign menu', 'Tips', {
        confirmButtonText: 'determine',
        cancelButtonText: 'cancel',
        type: 'warning'
    }).then(() => {

        // Prepare parameters
        const params = {
            roleId:this.roleId, //Role id
            menuIdList:checkedMenuIds  // Menu id array
        };

        // Request background
        axios.post("/role/RoleContextMenu",params)
            .then(res =>{
                this.$router.back();
            }).catch(error =>{
            this.$message("Menu permission assignment failed!!!");
        });

    });

},

2.2 menu management

The menu component is menus Vue, which manages menu information

2.2. 1 display menu list

Requirement analysis: the menu list is displayed with pagination

Function realization

  1. Data part
data() {
    return {
        total: 0, //Total number
        size: 10, //Number of displays per page
        page: 1, //Current page
        list: [], //Advertising data
        listLoading: false
    };
},
  1. Hook function
created() {
    //Get menu list
    this.loadMenuList();
},
//Method 1: Load menu list data
loadMenuList() {
    this.listLoading = true;

    return axios.get("/menu/findAllMenu",
                     {
        params:{
            currentPage:this.page,
            pageSize:this.size
        }
    })
        .then(res =>{
        this.list = res.data.content.list;
        this.total = res.data.content.total;
        this.listLoading = false;
    })
        .catch(error => {
        this.$message.error("Data acquisition failed! ! !");
    });
},
2.2. 2 add & modify menu
1. Route jump process
  1. Add button, click jump
<el-button class="btn-add" @click="handleAddMenu()" size="mini">add menu</elbutton>
//Add menu jump
handleAddMenu() {
    this.$router.push("/addMenu");
},

2)AddMenu. MenuDetail is introduced into the Vue component

<template>
  <menu-detail :is-edit='false'></menu-detail>
</template>
<script>
import MenuDetail from './MenuDetail'
export default {
  name: 'addMenu',
  title: 'add menu',
  components: { MenuDetail }
}
</script>
<style>
</style>
  1. MenuDetail. Add and modify the menu in Vue
2. Demand analysis

After opening the new menu page, you need to display a drop-down box. The data in the drop-down box is all the top-level parent menus

3. Function realization
  1. Data part
data() {
    return {
        menu, //Menu object
        selectMenuList: [], //Drop down list data
        rules
    };
},
  1. Hook function

Judge in the hook function. If it is a modification operation, query the current menu information and parent menu information according to the ID

If it is a new operation, you can only query the parent menu information

// Hook function
created() {
    if(this.isEdit){
        // Modify echo menu information
        const id = this.$route.query.id;

        //Get current menu and parent menu information
        this.findMenuInfoById(id);
    }else{
        // add to
        this.menu = Object.assign({},menu);

        // Get parent menu information
        this.findMenuInfoById(-1);
    }
},
//Method 1: add or modify drop-down parent menu echo
findMenuInfoById(id) {
    axios.get("/menu/findMenuInfoById?id=" + id)
        .then(res =>{
        console.log(res.data);

        // Judge whether menuInfo is empty
        if(res.data.content.menuInfo != null){
            // If it is not empty, modify it and echo it
            this.menu = res.data.content.menuInfo;
        }

        // Get the required parent menu information in the drop-down list
        this.selectMenuList = res.data.content.parentMenuList.map(item => {
            return {id: item.id , title: item.name};
        });

        // -1 display no parent menu
        this.selectMenuList.unshift({id: -1,title: "No parent menu"});
    })
        .catch(err => {
        this.$message.error("Data acquisition failed! ! !");
    });
},
  1. Click save
<el-button type="primary" @click="handleSave()">Submit</el-button>
//Save menu
handleSave() {
    this.$refs.form.validate(valid => {
        if (!valid) return false;

        axios.post("/menu/saveOrUpdateMenu",this.menu)
            .then(res =>{
            this.$router.back();
        })
            .catch(error =>{
            this.$message("Failed to save menu information! ! !");
        });
    });
}

2.3 resource management

The resource component is resources Vue, which manages resource information in this component

2.3. 1 display & Query resource list
  1. Display resource data with paging

  2. There are three query criteria for querying resource data

Resource name

Resource path

Resource classification information: drop-down list

  1. Data part
//query criteria
const listQuery = {
    currentPage: 1,
    pageSize: 5,
    name: null,
    url: null,
    categoryId: null
};

//Resource object
const defaultResource = {
    id: null,
    name: null,
    url: null,
    categoryId: null,
    description: ""
};
data() {
    return {
        listQuery, //query criteria
        total: 0,
        list: [], //Resource data
        cateList: [], //Resource classification data
        listLoading: false,
        dialogVisible: false,
        resource: Object.assign({}, defaultResource),
        isEdit: false,
        categoryOptions: [],
        defaultCategoryId: null
    };
},
  1. Hook function

In the hook function, you need to obtain the data of resources and resource classification

//Hook function
created() {
    //Get resource data
    this.getResourceList();

    //Get resource classification data
    this.getResourceCateList();
},

The getResourceList() method gets the resource information

//Method 1: get resource data
getResourceList() {
    this.listLoading = true;
    axios.post("/resource/findAllResource",this.listQuery)
        .then(res =>{
        this.list = res.data.content.list;
        this.total = res.data.content.total;
        this.listLoading = false;
    })
        .catch(error =>{
        this.$message.error("Data acquisition failed! ! !");
    });
},

The getResourceCateList() method obtains the resource classification information, which is displayed in the drop-down box

//Method 2: obtain resource classification data
getResourceCateList() {
    axios.get("/ResourceCategory/findAllResourceCategory")
        .then(res =>{
        // Save resource classification
        this.cateList = res.data.content;

        // Traverse to obtain resource classification information display drop-down list
        for(let i = 0; i < this.cateList.length; i++){
            // Fetch classification object
            const cate = this.cateList[i];

            // Save the name and ID of the resource classification categoryoptions
            this.categoryOptions.push({label:cate.name, value:cate.id});

        }
    }).catch(error =>{
        this.$message.error("Data acquisition failed! ! !");
    });
},

query

<el-button style="float:right" type="primary" @click="handleSearchList()" size="small">Query search</el-button>
//Query button
handleSearchList() {
    this.getResourceList();
},
2.3. 2. Add & modify resources
  1. add button
<el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">add to</el-button>
  1. Displays the dialog box for adding a resource form
//Add resource dialog box echo
handleAdd() {
    this.dialogVisible = true;
    this.isEdit = false;
    this.resource = Object.assign({},defaultResource);
},
  1. Resource classification information is displayed using the drop-down menu:

The value of v-model is the value attribute value of the currently selected El option

attributeexplain
valueThe value of the option
labelLabel name of the option
keyThe key name that uniquely identifies value
<el-form-item label="Resource classification:">
	<el-select v-model="resource.categoryId" placeholder="whole" clearable style="width: 250px">
		<el-option
            v-for="item in categoryOptions"
            :key="item.value"
            :label="item.label"
            :value="item.value"
        ></el-option>
    </el-select>
</el-form-item>
  1. Click save
<el-button type="primary" @click="handleSave()" size="small">determine</el-button>
//Add & modify resources
handleSave() {
    axios.post("/resource/saveOrUpdateResource",this.resource)
        .then(res =>{
        this.dialogVisible =false;
        this.getResourceList();
    }).catch(error =>{
        this.$$message("operation failed! ! !");
    })
},
  1. Modify operation. The parameter is the current row data
<el-button size="mini" type="text" @click="handleUpdate(scope.row)">edit</elbutton>
  1. Echo operation
//Edit resource echo
handleUpdate(row) {
    this.dialogVisible = true;
    this.isEdit = false;
    this.resource = Object.assign({},row);
},

Task 3: Project packaging and publishing

1. User permission control

1.1 user login

  1. In the user login interface, you need to enter the mobile phone number and password

  2. Login component login vue

Login button

<el-button type="primary" :loading="loading" @click="submit('login-form')">{{ loading ? 'Loading...' : 'Sign in' }}</el-button>

Method of submitting forms

//Submit login form
submit (ref) {
    // check
    this.$refs[ref].validate(valid => {
        if (!valid) return false

        // validated
        this.error = null
        this.loading = true

        // create token from remote
        //Send login request
        this.$store
            .dispatch('createToken', this.model)
            .then(res => {
            if (res.state !== 1) {
                this.error = {
                    title: 'Error occurred',
                    message: 'Abnormal, please try again later!'
                };
                switch (res.state) {
                    case 201:
                        this.error.message = 'Please enter the correct mobile phone number'
                        break;
                    case 202:
                        this.error.message = 'Please input a password'
                        break;
                    case 203:
                        this.error.message = 'Password error'
                        break;
                    case 204:
                        this.error.message = 'Verification code expired'
                        break;
                    case 205:
                        this.error.message = 'Wrong account or password'
                        break;
                    case 206:
                        this.error.message = 'Wrong account or password'
                        break;
                    case 207:
                        this.error.message = 'Verification code error'
                        break
                    case 500:
                        this.error.message =
                            'Internal server error, please try again later!'
                        break;
                }
                this.loading = false
                return;
            }

            // Login succeeded
            this.$router.replace({ path: this.$route.query.redirect || '/' })
            this.loading = false
        })
            .catch(err => {
            console.error(err)
            this.error = {
                title: 'Error occurred',
                message: 'Abnormal, please try again later!'
            };
            switch (err.response && err.response.status) {
                case 401:
                    this.error.message = 'Incorrect username or password!'
                    break;
                case 500:
                    this.error.message =
                        'Internal server error, please try again later!'
                    break;
            }
            this.loading = false;
        })
    })
}

this.$store.dispatch("createToken", this.model)

This code means to call the actions of the store warehouse createToken method in JS

Send login request and login code

/**
 * User login
 */
createToken: async ({ commit }, { username, password }) => {
    //Request background login interface
    const res = await TokenService.userLogin({
        phone: username.trim(),
        password: password.trim()
    });

    console.log(res);
    //debugger;

    //If the judgment result is not equal to 1, login failed
    if (res.state !== 1) {
        return Promise.resolve(res);
    }

    //Get content
    const result = res.content;

    //Save the token in the session
    commit(CHANGE_SESSION, {
        accessToken: result.access_token
    });

    //Save user information
    commit(CHANGE_SESSION, { user: result.user });
    return res;
},

TokenService

import { TokenService, UserService } from "../services";
//Login request async ES6 syntax, function: send asynchronous request
export const userLogin =  async (data) => {
    //await indicates waiting to receive the returned data
    return await PostRequest(`${process.env.VUE_APP_API_FAKE}/user/login${Serialize(data)}`)
}

1.2 get user menu dynamically

  1. After we log in successfully, we will immediately send a second request to obtain the user's menu permission list

  2. In actions JS to complete the operation of requesting the background interface to obtain data

/**
 * Get the permissions of the currently logged in user
 */
getUserPermissions: async ({ commit }) => {
    //1. Request the background to obtain the permission of the current user
    const res = await UserService.getUserPermissions();

    //2. Judgment
    if (!res.success) {
        //If the acquisition fails, false is returned directly
        return res.success;
    }

    //debugger;
    //3. The data is obtained successfully, and the menu and resource list are taken out
    const { menuList, resourceList } = res.content;

    //4. The following code is generating the menu of tree structure
    let menus = [];
    const formatMenu = treeData => {
        if (treeData.length > 0) {
            return treeData.map(item => formatMenu(item));
        }
        const result = {};

        //If show equals, it means that it can be displayed and the content can be saved
        if (treeData.shown == 1) {
            result.id = treeData.id;
            result.text = treeData.name;
            result.label = treeData.name;
            result.name = treeData.href;
            result.icon = treeData.icon;
            result.shown = treeData.shown;
        } else {
            return "";
        }

        //Get child nodes
        if (treeData.subMenuList) {
            result.children = [];
            treeData.subMenuList.forEach(item => {
                formatMenu(item) && result.children.push(formatMenu(item));
            });

            if (result.children.length === 0) {
                delete result.children;
            }
        }
        return result;
    };

    const memusMap = {};

    const splapMenu = treeData => {
        if (treeData.length > 0) {
            return treeData.map(item => splapMenu(item));
        }
        const result = {};
        result.id = treeData.id;
        result.text = treeData.name;
        result.label = treeData.name;
        result.name = treeData.href;
        result.icon = treeData.icon;
        result.shown = treeData.shown;
        result.name && (memusMap[result.name] = result);

        if (treeData.subMenuList) {
            result.children = [];
            treeData.subMenuList.forEach(item => {
                result.children.push(splapMenu(item));
            });
        }
        return result;
    };

    splapMenu(menuList);

    menus = formatMenu(menuList);
    commit(CHANGE_SIDERBAR_MENU, menus);

    return { menus, resourceList, menuList, memusMap };
},

1.3 verify Token

1.3. 1 navigation guard

Some hook functions executed before routing, such as verifying whether the user has permission, need to be used

  1. authorize.js is configured with navigation guard to restrict user login
//Navigate to the URL you want to access, the path from which you want to jump, and then release it with next()
// Authorize (Make sure that is the first hook.)
router.beforeHooks.unshift((to, from, next) => {
    // don't need authorize
    if (!to.meta.requireAuth) return next();
    // check login state
    store.dispatch("checkToken").then(valid => {
        // authorized
        if (valid) {
            store.dispatch('getUserPermissions').then(res => {
                const { memusMap } = res
                if (memusMap.Courses && to.name === 'Home') {
                    return next()
                } else if (memusMap[to.name]) {
                    return next()
                } else if (Object.keys(memusMap).length > 0) {
                    return next({ name: memusMap[Object.keys(memusMap)[0]].name })
                } else {
                    next({ name: 'PermissionDenied' })
                }
            })
            return next();
        }
        // unauthorized
        console.log("Unauthorized");

        next({ name: "Login", query: { redirect: to.fullPath } });
    });
});
  1. In actions JS to check whether the token is available
/**
 * Check if the client token is available
 */
checkToken: async ({ commit, getters }) => {
    //Take out the token
    const token = getters.session.accessToken;

    if (!token) {
        //Not available
        return Promise.resolve(false);
    }

    return Promise.resolve(true);
},

1.4 user role assignment

1.4. 1 process analysis

Click the assign role button

Assign role dialog box

1.4. 2 code part

display a dialog box

//Assign roles
handleSelectRole(index, row) {
    //Save user ID
    this.allocAdminId = row.id;

    //Get role list
    this.getRoleList();

    //Gets the role owned by the current user
    this.getUserRoleById(row.id);

    //open a dialog box
    this.allocDialogVisible = true;
},

Get the role list and display it in the drop-down menu

//Get the role list and display it in the drop-down menu
getRoleList(id) {
    return axios
        .post("/role/findAllRole", this.listQuery)
        .then(res => {
        console.log(res.data.content);
        this.allRoleList = res.data.content.map(item => {
            return {id: item.id, name: item.name};
        })
    })
        .catch(err => {
        this.$message("Failed to get role list!!!");
    });
},

Get the roles owned by the current user, and echo the default selection

//Gets the role owned by the current user. It is selected by default
getUserRoleById(id) {
    axios.get("/user/findUserRoleById?id=" + id)
        .then(res => {

        // Fetch data
        const allocRoleList = res.data.content;
        // Retrieve the role id in the data
        this.allocRoleIds = [];

        if (allocRoleList != null && allocRoleList.length > 0) {
            for (let i = 0; i < allocRoleList.length; i++) {
                // Save id
                this.allocRoleIds.push(allocRoleList[i].id);
            }
        }
    }).catch(error =>{
        this.$message("Operation failed!!!");
    });
},

Assign roles to users

//Assign roles to users
handleAllocRole() {

    // Prepare parameters
    const params = {
        userId: this.allocAdminId,
        roleIdList: this.allocRoleIds
    };

    axios.post("/user/userContextRole", params)
        .then(res => {
        this.allocDialogVisible = false;
    });
},

2. nginx

2.1 what is nginx?

Nginx (pronounced as engine x) is a lightweight Web server / reverse proxy server and e-mail (IMAP/POP3) proxy server, which is distributed under a BSD like protocol. It is developed by Igor sysoev, a Russian programmer, for rambler, a large Russian portal and search engine (Russian: Рамблер) use. Its characteristics are less memory and concurrent capability. In fact, nginx's concurrent capability is better in the same type of Web server. Chinese mainland users use Sina website: Sina, NetEase, Tencent, etc.

advantage:

​ 1. Less memory and strong concurrency

​ 2.Nginx is specially developed for performance optimization. In the case of high connection concurrency, it can support the response of up to 50000 concurrent connections

​ 3.Nginx supports hot deployment and can upgrade the software version without interruption of service

2.2 application scenarios

​ 1.http server: Nginx is an http service that can provide http services independently. Can be a static web server.

​ 2. Virtual host: multiple websites can be virtualized on one server. For example, the virtual host used by personal websites.

​ 3. Reverse proxy, load balancing: when the number of visits to the website reaches a certain level and a single server cannot meet the user's request, multiple server clusters need to be used. nginx can be used as the reverse proxy. Moreover, multiple servers can share the load equally, and a server will not be idle due to high load downtime of a server.

2.3 Nginx installation

Download nginx, official website: http://nginx.org/

We use version 1.17 Version 8.

Nginx is installed under Linux and only the source code is provided, so we need to compile it

2.3. 1. Installation environment configuration

1. Because Nginx is written in C language, the C language compilation environment needs to be configured (it must be installed in the networked state)

Installation required gcc Environment. Execute command: 
yum install gcc-c++

Note: if the following prompt appears when executing the command:

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-6lEtWkYI-1630919053125)(E:\MarkDown \ tick notes \ Nginx installation environment configuration problem)]

terms of settlement:

The problem is yum In locked state,Force off yum Process can
rm -f /var/run/yum.pid

2. Third party development packages need to be installed before compiling.

PCRE

The http module of nginx uses pcre to parse regular expressions, so you need to install pcre library on linux

Installation command:
yum install -y pcre pcre-devel

zlib

nginx uses zlib to gzip the contents of the http package, so you need to install the zlib library on linux.

Installation command:
yum install -y zlib zlib-devel

openssl

openssl is a powerful secure socket layer cipher library. nginx supports not only http protocol but also https, so you need to install openssl Library in linux.

Installation command:
yum install -y openssl openssl-devel
2.3. 2 steps for installing Nginx

1. Upload the source package of Nginx to Linux

2. Unzip Nginx

tar -xvf nginx-1.17.8.tar 

3. Enter the unzipped directory nginx-1.17 eight

4. Execute the command configure to generate the Mikefile file

./configure \
--prefix=/usr/local/nginx \
--pid-path=/var/run/nginx/nginx.pid \
--lock-path=/var/lock/nginx.lock \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--with-http_gzip_static_module \
--http-client-body-temp-path=/var/temp/nginx/client \
--http-proxy-temp-path=/var/temp/nginx/proxy \
--http-fastcgi-temp-path=/var/temp/nginx/fastcgi \
--http-uwsgi-temp-path=/var/temp/nginx/uwsgi \
--http-scgi-temp-path=/var/temp/nginx/scgi

After executing the command, a MakeFile file is generated

5. Create temporary file directory

mkdir /var/temp/nginx/client -p

6. Execute the make command to compile

make

7. Installation

make install
2.3. 3 start and access Nginx

1. Enter nginx installation directory

cd /usr/local/nginx/

2. Enter the sbin directory and execute the nginx command

./nginx start-up
./nginx -s stop close
ps aux | grep nginx View process

3. Access through the browser. The default port is 80 (Note: whether to turn off the firewall.)

2.4 configuring virtual hosts

Virtual host means that we use Nginx to configure multiple websites in one server

How to distinguish different websites:

  1. Different ports
  2. Different domain names
2.4. 1 distinguish different virtual hosts through ports
Nginx profile

1. Location of nginx configuration file

cd /usr/local/nginx/conf
nginx.conf namely Nginx Configuration file for

2. Description of nginx core configuration file

worker_processes  1; #The number of work processes. The default is 1
#The configuration affects the network connection between the nginx server and the user
events {
    worker_connections  1024; #Maximum concurrent connections per work
}

# http block is the most frequently configured part. It can nest multiple server s, configure proxy, cache, log definition and most other functions
http {
	# Import mime type definition file
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65; # Timeout
	
	#Server there can be multiple parameters related to configuring virtual hosts. A server is a virtual host
    server {
		# Listening port
        listen       80; 
		#Listening address
        server_name  localhost;         

		# Default request configuration
        location / {
            root   html; # Default site root
            index  index.html index.htm; # Welcome page
        }

		# Error prompt page
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}
Use Notpad to connect to Linux

The advantage of using notepad + + to connect to linux is that using notepad + + to edit batch text of files in linux is much more convenient and faster than directly operating in linux

1. Install NppFTP in Notepad plug-in

2. Open NppFTP

3. Select settings

4. Configure connection information

5. Connection

Configure nginx conf

1. Use Notpad in nginx Add a new server to conf

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;

        location / {
            root   html;
            index  index.html index.htm;
        }
    }
	
	# Configure a new server
	server {
        listen       81; # Modify port
        server_name  localhost;

        location / {
            root   html81; # Make a new directory
            index  index.html index.htm;
        }
    }

}

2. Copy an html directory

cp -r html html81

3. Reload the configuration file

sbin/nginx -s reload

4. Visit

http://192.168. 44.128 accessing the first server

http://192.168. 44.128:81 / access the second server
2.4. 2 distinguish different virtual hosts by domain name
What is a domain name

The web address is the domain name. It is the address of a website. It is provided by the domain name provider and generally needs to be purchased

www.baidu.com

www.taobao.com

www.jd.com

Domain name level

Primary domain name

For example com .org .cn

Secondary domain name

A secondary domain name is a domain name preceded by a primary domain name

Secondary domain name: baidu.com com , zhihu. com

Tertiary domain name

​ www.baidu.com

​ image.baidu.com

Domain name binding

A domain name corresponds to an ip address, and an ip address can be bound by multiple domain names.

Resolve domain name through DNS server

Configure domain name mapping

1. The hosts file can be modified for local testing. Modify the hosts file of window: (C:\Windows\System32\drivers\etc)

You can configure the mapping relationship between domain name and ip. If the corresponding relationship between domain name and ip is configured in the hosts file, you do not need to go to the dns server.

Configure it nginx Mapping of
192.168.44.128 www.t2.com

2. Use SwitchHosts to modify hosts

Decompress

Right click to run as administrator

Configure the mapping between IP and domain name

Configure nginx conf
#Distinguish virtual hosts by domain name
	server {
        listen       80;
        server_name  www.t1.com;

        location / {
            root   html-t1;
            index  index.html index.htm;
        }
    }
	
	server {
        listen       80;
        server_name  www.t2.com;

        location / {
            root   html-t2;
            index  index.html index.htm;
        }
    }

Create html-t1 and html-t2 directories

cp -r html html-t1
cp -r html html-t2

Modify the index HTML, refresh

sbin/nginx -s reload

visit

Although there is only one server, there are multiple websites running on this server. You can access different website contents by accessing different domain names

2.5 reverse proxy

2.5. 1 what is an agent

In fact, an agent is an intermediary. A and B can be connected directly. A C is inserted in the middle, and C is an intermediary. At the beginning, agents were mostly used to help intranet client s access Internet server s

When the client sends a request, it will not directly send it to the destination host, but first send it to the proxy server. After the proxy service accepts the client request, it will send it to the host, receive the data returned by the destination host, and then send it to the client.
2.5. 2 forward proxy

For example, if we can't access Google directly in China, we can first send the request to the proxy server through a forward proxy server. The proxy server can access Google. In this way, the proxy goes to Google to get the returned data, and then returns it to us, so that we can access Google

The forward proxy is the client, and the server does not know the client that actually initiated the request

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-fB1RfKTv-1630919053130)(E:\MarkDown \ hook notes \ Nginx forward agent)]

2.5. 3 reverse proxy

The difference between reverse proxy and forward proxy is: forward proxy client and reverse proxy server.

Reverse proxy refers to using the proxy server to receive the client's request, then forwarding the request to the internal application server of the website, and returning the results obtained from the server to the client

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-VF8nc5wP-1630919053136)(E:\MarkDown \ hook notes \ Nginx reverse agent)]

2.5. 4. Nginx implements reverse proxy

Nginx is installed on the server side as a reverse proxy server. The function of nginx is to forward requests to the subsequent application server

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-RnH8SEVn-1630919053140)(E:\MarkDown \ hook notes \ Nginx realizes reverse proxy)]

Configuration steps

Step 1: simply use two tomcat instances to simulate two http servers, and change the ports of tomcat to 8080 and 8081 respectively

Step 2: start two tomcat.

./bin/startup.sh 
Access two tomcat
http://192.168.44.128:8081/
http://192.168.44.128:8082/ 

Step 3: configure the reverse proxy server

	#Reverse proxy configuration 
	#The server in upstream is the address of the application server that actually handles the request
	upstream lagou1{
		#Define HTTP address with server
		server 192.168.44.128:8081;
	}
	
	
	server {
        listen       80;
        server_name  www.lagou1.com;
        location / {
        	# Using proxy_ pass can proxy requests to an HTTP service named by upstream
            proxy_pass http://lagou1;  # Address forwarded to
            index  index.html index.htm;
        }
    }
	
	upstream lagou2{
		#Define HTTP address with server
		server 192.168.44.128:8082;
	}
	
	
	server {
        listen       80;
        server_name  www.lagou2.com;
        location / {
            proxy_pass http://lagou2; 
            index  index.html index.htm;
        }
  }

Step 4: nginx reload the configuration file

nginx -s reload

Step 5: configure the domain name and add the mapping relationship between the domain name and ip in the hosts file

192.168.44.128 www.lagou1.com
192.168.44.128 www.lagou2.com

Enter the domain name through the browser and access the nginx proxy server. Nginx forwards the request to the corresponding target server according to the domain name. As a user, we see the response result page of the server. In the whole process, the target server is invisible to the client, and the server is exposed to the outside is the address of nginx

2.6 load balancing

2.6. 1 what is load balancing

When a request is sent, Nginx, as the reverse proxy server, will find the subsequent target server to process the request according to the request, which is the reverse proxy. So, if there are multiple target servers, which server to find to process the current request? This process of reasonably allocating requests to the server is called load balancing

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-TsnwUVLu-1630919053144)(E:\MarkDown \ hook notes \ load balancing)]

2.6. 2 why load balancing

When the system is faced with a large number of user access and the load is too high, it usually uses increasing the number of servers for horizontal expansion. Load balancing is mainly to share the access volume and reasonably distribute the requests to different servers to avoid temporary network congestion

2.6. 3 load balancing strategy
2.6. 3.1 polling

By default, each request is allocated to different servers one by one in chronological order. If a server goes offline, it can be automatically eliminated

collocation method

#load balancing 
upstream lagouedu{
    # Define HTTP address with server
    server 192.168.44.128:8081;
    server 192.168.44.128:8082;
}

server {
	listen       80;
	server_name  www.lagouedu.com;

	location / {

        proxy_pass   http://lagouedu; 	# Forwarding address
        index  index.html index.htm;
	}
}
2.6.3.2 weight

You can adjust the server weight according to the actual situation of the server. The higher the weight, the more requests are allocated, and the lower the weight, the fewer requests. The default is 1

#load balancing 
upstream lagouServer{
	# Define HTTP address with server
	server 192.168.44.128:8081 weight=1;
	server 192.168.44.128:8082 weight=10;
}

3. Project deployment and release

3.1 background project deployment

3.1.1 Linux environment preparation
  1. Software to be installed
Softwareedition
JDK11
Tomcat8.5
MySql5.7
Nginx1.17.8
  1. Turn off firewall

  2. Use SQLYog to connect to MySQL on Linux and import SQL script to create the database required by the project

3.1. 2. Project packaging and release

In the process of normal development, the relevant configurations of projects in different environments will be different. When we deploy in different environments, we need to manually modify the configuration of the corresponding environment, which is too troublesome and easy to make mistakes.

Next, we will specify the corresponding configuration files of each environment during packaging through the relevant configuration of maven

1. Modify ssm_dao sub module
2. Step 1: create a configuration file

Create the filter directory under src/main/resources of the project, and then create development. Net properties , product. Properties two files

Development is the content of development configuration.

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///ssm_lagou_edu?characterEncoding=UTF-8
jdbc.username=root
jdbc.password=123456

product is the formal configuration content

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.44.128:3306/ssm_lagou_edu?characterEncoding=UTF-8
jdbc.username=Weiwei
jdbc.password=Weiwei@666
3. Step 2: configure JDBC Properties file

jdbc. The contents in properties are no longer written dead, but obtained from the above two files

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=${jdbc.url}
jdbc.username=${jdbc.username}
jdbc.password=${jdbc.password}

Note: ${jdbc.url} directly corresponds to the development. URL configured above Properties or product The name in the properties file.

4. Step 3: configure POM of dao module XML file

Add the following configuration

<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <!-- development environment  -->
            <env>development</env>
        </properties>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <!-- Formal environment -->
            <env>product</env>
        </properties>
    </profile>
</profiles>

<build>
    <finalName>web</finalName>
    <filters>
        <filter>src/main/resources/filter/${env}.properties</filter>
    </filters>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <excludes>
                <exclude>filter/*.properties</exclude>

            </excludes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>
5. profile description

Profile allows us to define a series of configuration information and then specify its activation conditions. In this way, we can define multiple profiles, and then each profile corresponds to different activation conditions and configuration information, so as to achieve the effect of using different configuration information in different environments

The dev environment configuration is enabled by default:

<profiles>
    <profile>
        <id>dev</id>
        <properties>
            <!-- development environment  -->
            <env>development</env>
        </properties>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
    </profile>
    <profile>
        <id>prod</id>
        <properties>
            <!-- Formal environment -->
            <env>product</env>
        </properties>
    </profile>
</profiles>

Specify the database configuration file path, which can be customized:

<filters>
	<filter>src/main/resources/filter/${env}.properties</filter>
</filters>

Specify the resource directory path

<resources>
    <resource>
        <directory>src/main/resources</directory>
        <!-- The resource root directory excludes the configuration of each environment -->
        <excludes>
        	<exclude>filter/*.properties</exclude>
        </excludes>
        <filtering>true</filtering>
    </resource>
</resources>
6. Step 4: packing

Command packaging

Local package mvn -Pdev install perhaps mvn install(Because this example activeByDefault Equipped with true)
Packaging mvn -Pprod install

result: src/main/resources/config/jdbc.properties according to mvn -P Parameter decision value

Packaging with idea

7. Packaged documents

Use the configuration file of the production environment for packaging

Open the war package and we will find that other sub modules have been jar packaged and placed in the lib folder

8. Release

Change the project name

Upload to tomcat and start the test

Create an upload folder in the webapps directory where tomcat is deployed and save the pictures

mkdir upload

visit

http://192.168.44.128:8080/ssm_web/user/login?phone=18211111111&password=123456

Get the JSON of the response and publish it successfully

3.2 front end project deployment

3.2. 1. Modify the configuration file

Production environment profile, configuring background URL

VUE_APP_NAME = Edu Boss
VUE_APP_TITLE = Lagou Edu Boss (Dev)

VUE_APP_STORAGE_PREFIX = lagou_edu_boss_dev

#VUE_APP_API_FAKE = /front
VUE_APP_API_FAKE = http://192.168.44.128:8080/ssm_web

#VUE_APP_API_BASE = /boss
VUE_APP_API_BASE = http://192.168.44.128:8080/ssm_web

Customize the configuration file and configure packaging related information

Copy the following to Vue config. js

module.exports = {
  publicPath: process.env.NODE_ENV === "production" ? "/edu-boss/" : "/",
  indexPath: "index.html",
  assetsDir: "static",
  lintOnSave: process.env.NODE_ENV !== "production",
  productionSourceMap: false,
  devServer: {
    open: true,
    port: 8081
  }
};
3.2. 2 packaging test operation

Packaging command

npm run build

A dist directory will be generated under the project

Create an edu boss folder in the webapps directory of the local tomcat and copy the files in the dist directory to it

Test: start the local tomcat and access the front-end project path:

http://localhost:8081/edu-boss/
3.2. 3 release front-end projects

1. Unzip a new tomcat and modify the port number

#decompression
tar xvf apache-tomcat-8.5.50.tar 
#Rename
mv apache-tomcat-8.5.50 ui-tomcat
#Modify port number
cd ui-tomcat/conf/
vim server.xml 

2. Upload front-end projects to webapps

//Upload edu boss Zip and unzip
unzip edu-boss.zip 

//Delete edu boss zip
rm -rf edu-boss.zip

3. Run the front-end project and access

./bin/startup.sh 

//View log dynamically
tail -f logs/catalina.out 

//visit
http://192.168.44.128:8081/edu-boss/
3.2. 4. Modify tomcat default access item

1. Use notpad to open the front-end tomcat configuration file server XML, locate the Host tag

2. Add in the Host tag

<Context path="" docBase="edu-boss" reloadable="true" debug="0" privileged="true"></Context>

3. Restart and access the front-end project. At this time, you only need to directly access 8081

http://192.168.44.128:8081/
3.2. 5 configure reverse proxy

1. Use notpad to open the configuration file nginx conf

2. Configure reverse proxy

#Configure ssm project reverse proxy
	upstream lagouedu{
		server 192.168.44.128:8081;
	}
	
	server {
        listen       80;
        server_name  www.edu-boss.com;

        location / {
			
            proxy_pass http://lagouedu;  # Forwarding address
            index  index.html index.htm;
        }
    }

3. Modify local hosts and configure domain name mapping

4. Visit www.edu-boss.com com

Topics: Java Javascript node.js Tomcat Vue.js