background

After the completion of the development of the merchant store client, I have mastered some development technologies of the client and accumulated project experience. Because the merchant application page of the client is based on static data rendering, so in real application practice, static page data does not meet the needs of the actual application scenario. If we have to change the content of the page on a regular basis, it is not easy for the average application user. Perhaps the most awkward way to do this is to contact the application developer to modify the original project content, but it is much more convenient to use a GUI interface to transfer the usage requirements to ordinary users once and for all. While developing and managing background projects, I can also master the combination of Vue+ElementUI technology stack

Project introduction

This project is developed using Vue+ElementUI as the core technology, and all functions are independently developed by individuals. The program main function is to manage the client project http://123.57.204.48:5000 data, including the page to add and delete data, data visualization and application front page of the show.

You can also use this project as a template for secondary development, with the UI and functionality implementation of this project as a reference. If you are interested in further communication, welcome to the project warehouse to raise issues, and also welcome your collection and attention, thank you!

Warehouse address: github.com/konglingwen…

Online address: http://123.57.204.48:5000/admin

Screenshot of some application pages

Store Configuration page

Product Management Page

Product edit page

Application development techniques

Core technology architecture: [email protected][email protected]

Dependency package: VUE-Router V-Charts (VUE component library based on Echarts package) axios(HTTP request tool)

Build tool: VUe-CLI less

Deploying and running: Github – Actions Cloud server

Case introduction in the development process

Because this application is widely used, the implementation process of service scenarios is not introduced here. After the completion of project development, I summarized several common scenarios of technical implementation and would like to share here. If you encounter a type of functionality requirements can refer to the following functionality implementation

Secondary encapsulation of uplod upload components

Component source code address: github.com/konglingwen…

In the use of most applications we will encounter the need to upload one or more images from the local computer to the site, the above uploaded content as the site needs to show us the content. When developing this function, for me who is accustomed to using the Upload component in Element-UI, we can also customize the encapsulated Upload component for different business processes of uploading one or more pictures. Such fine-grained processing can better meet our business needs.

The most commonly used function is the image uploading function embedded in forms. In addition to forms such as input boxes, which can process data in the form of V-Model, upload can also process data, which greatly facilitates the data processing process when submitting forms.

Start by defining several props for the Upload component.

props: {
    value: [String, Array],
    multipart: { type: Boolean, default: false }
  },

Copy the code

The value attribute is used to receive the binding value of the V-Model directive on the component, emit an input event in the component, and the parameter is automatically updated to the binding value of the directive. Another multipart property indicates the number of files that the component can upload at one time. With these two props we have a general idea for implementing the functionality of the entire component. Now you can emit an input event based on these two attributes.

For all event listeners updated by native el-upload files, depending on the value of the multipart attribute, the on-success event is handled in this way.

// This is only part of the code
handlerSuccess(res,file,fileList){
	if (!this.multipart) {
          if (fileList.length > 1) { fileList.shift(); }}const value = this.multipart
          ? fileList.map(item= > {
              return (item.response && item.response.path) || item.url;
            })
          : res.path;

        this.$emit("input", value);
}
Copy the code

At this point, the logic for dynamically updating the binding value of the component’s V-model directive is done. However, after my tests, I found that there is another component initialization process that needs to be handled, namely the initialization of the file-list field bound to the component. You first need to define a fileList variable in the component

data(){
	return {
    	fileList: []}}Copy the code

With this fileList variable, we can initialize the data of the basic Upload component. The specific binding process is not shown here. You can check the official usage of the component for details. When we call this component in the form, most of the values bound by the V-model are retrieved asynchronously by the API request. When the request is complete, the values passed in by the V-model instruction are not synchronized to the fileList variable in real time. After some thinking, I came up with the $watch API provided by VUE. We can solve this problem by observing the value attribute of the component

// We only need to observe the change of the 'value' property once the component is created
created() {
    const unwatch = this.$watch("value".newVal= > {
      this.fileList = Array(this.newVal)
        .flat()
        .map(item= > ({ name: "".url: newVal }));

      unwatch && unwatch();
    });
  }

Copy the code

:::^ Note component source code: github.com/konglingwen…

: : :

Now test the component and find that it works perfectly

Page TAB view switch

rendering

The source code github.com/konglingwen…

TAB view is similar to page browsing in a browser by clicking on a TAB to switch pages. We can view the clicked page here and close the corresponding TAB by clicking on the Close button. The function of the TAB component itself is very simple and will not be covered here. The following is mainly about how to achieve the interactive effect after clicking the page.

Now that we have tabs, all we need is to statically preserve the page we visited and leave it as we left it the next time we switch back. It’s easy to think of vue’s own keep-alive component, which wraps our dynamically switched routing component to cache components rendered by the project. The specific usage of keep-alive is not explained. For details, please refer to the official documentation.

In order to store TAB and cache component data we need to define two array types of variables, through the operation of these two arrays to achieve the specific function.

/* Tab-tag module usage description title:HTML native attribute String click: listen on the tag click event Function close: listen on the tag close event Function Closable: Whether the tag can be closed Boolean active: Boolean label: Displays text, which is passed in as a String */ // displays only relevant code snippets<div class="tag-container">
          <tab-tag
            :title="tag.label"
            @click="toggleTag(tag)"
            @close="closeTag(tag, index)"
            v-for="(tag, index) in tags"
            :key="index"
            :closable=! "" tag.isHome"
            :active="$route.path === tag.path"
            :style="{ width: `${100 / maxTags}%` }"
          >{{ tag.label }}</tab-tag>
        </div>

        <el-main>
          <keep-alive :max="maxTags" :include="cacheViews">
            <router-view :key="$route.fullPath"></router-view>
          </keep-alive>
        </el-main>

Copy the code
 data() {
    return {
      maxTags: 7.// Maximum number of tabs to store
      tags: [{label: "Home page".// Label content
          name: "seller-dashboard".// The name of the corresponding page route
          path: "/seller/dashboard".// The path of the corresponding page route
          isHome: true}].// the TAB initializes the data
      cacheViews: []   // The cached page component stores the manually declared 'name' value in the component
    };

Copy the code

With the variables defined above in the data function, we just need to implement the page interaction function. How should each page switch be stored? Dealing with this problem requires us to watch the routing instance object change in real time, and then manipulate the tags and cacheViews variables to implement our requirements

watch: {
    $route(newRoute) {
      const index = this.tags.findIndex(item= > item.path === newRoute.path);
      if (index === -1) {
        if (this.tags.length >= this.maxTags) {
          this.tags.splice(1.1);
        }
        if (!this.cacheViews.includes(this.$route.name)) {
          this.cacheViews.push(this.$route.name);
        }
        this.tags.push({
          label:
            newRoute.meta.breadcrumbMenus[
              newRoute.meta.breadcrumbMenus.length - 1].path: newRoute.path,
          name: newRoute.name
        });  // 'meta. BreadcrumbMenus' are the data structures I set up to display the breadcrumb menu on the corresponding page. You can also customize your own' label 'field.}}},Copy the code

Be careful when storing the route cache and TAB tags. By default, I’ve set the page name and route name to be the same. You can use any flexibility you want, but make sure that the cacheViews array stores the name values defined in the page component. This is also required when the keep-alive component is cached, otherwise the dynamic component would not be cached by it

After caching page components and storing TAB tags, it is easy to switch and close TAB tags by listening for click and close events of TAB tags to handle the corresponding logic

methods: {
    closeTag(tag, index) {
      this.tags.splice(index, 1);
      const isActive = this.$route.path === tag.path;
      this.cacheViews.splice(this.cacheViews.indexOf(tag.name), 1);

      if(isActive && ! tag.isHome) {this.$router.push(this.tags[this.tags.length - 1].path); }},toggleTag(tag, index) {
      if (tag.path === this.$route.path) {
        return;
      }
      this.$router.push(tag.path); }}Copy the code

At this point the entire TAB view functions are complete, the introduction of the above is my own project is implemented, no generalization processing, part code, this function remains to be optimized, the follow-up after the completion of the I will update in time, when you encounter similar scenario development demand can consult this implementation mode, welcome in the comments section to give directions!

Reference: juejin. Cn/post / 684490…

Technical magic used in the project to share

Computed properties of Vue

We all know that computed property of VUE is ubiquitous in project development, but the getter function of this property is used most. Those who know more about computed property must know that it also has setter functions for us to use, but many people do not know its application scenarios. Even MYSELF, I knew very little about the usage scenarios of this setter before developing this project. Here’s how to write all of the syntax for computed attributes

// Use the 'formType' attribute as an example
computed: {formType: {
      set(val) {
        this.form.type = val === "" ? -1 : val;
      },
      get() {
        return this.form.type === -1 ? "" : this.form.type; }}Copy the code

The formType property in the code is a calculated property with a getter and setter. It works much like the object.defineProperty method in JS, which does some specific logic by intercepting the reading of an Object property. We can see that reading the formType property causes another reading, which leads to the form.type property value. Now that we know the basic way to use it, we need to apply it further

data(){
	return {
          form: { name: "".type: -1 }, // Since the actual project form item has multiple entries, 'type' here is defined as the attribute value of the 'form' object}}Copy the code

As you can see from the code logic, the two pieces of code combine to create a special correspondence between formType and form.type for the ” and -1 values. So where does this special one-to-one data conversion correspondence fit in? Here I use a dynamic screenshot to show it

To clarify, I use an Elemental-UisELECT selection box for mouse action in the form, and the value bound to the selection box form item is the formType

Component HTML

<el-select v-model="formType" clearable placeholder="Please select offer type (optional)">
            <el-option
              v-for="item in options"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            ></el-option>
          </el-select>

Copy the code

Js part

data(){
	return {
          options: [{value: 0.label: "Full reduction" },
          { value: 1.label: "Discount" },
          { value: 2.label: "Special offer" },
          { value: 3.label: "Supporting invoice" },
          { value: 4.label: "Takeout insurance"}].}}Copy the code

As can be seen from the js part of the code, all the data rendered in the form item selection box looks like this, and the value taken by the selection box after bidirectional binding is the value field. Because some design rules on the server side mostly return the data of type number to the front end, and the form.type field value after we use the calculation attribute also meets the requirements. So when I edit the form, the data type is exactly what the back end needs, which saves the front end from having to redeal with data structure inconsistencies when submitting requests to the interface and enhances the responsiveness of the component data itself.

Automatic deployment

This project uses the integrated deployment environment provided by Github-Actions, where local code is automatically deployed to the cloud server after submitting updates. Detailed deployment profiles can be found here at github.com/konglingwen…

Administrator permission level management

For a large management background system will be assigned to multiple administrators at the same time, these administrators will also have the classification of permission levels. In order to practice, this project divides the service function into two levels: super and primary. The default user for online login is the primary administrator, which can be seen in the administrator list.

Administrators at different levels can view only registered users at the same level or lower than their own levels. Super administrators can view all users. To log in as the super administrator, you need to use the supporting backend github.com/konglingwen… The project is available only after it initializes the automatic script using the Node environment.

harvest

Through the development of this project, I have gained more experience and technical harvest in b-side application. Through the continuous practical application of Vue and its component library in actual projects, I have also had more thoughts on the front-end development process and specifications. In the past, a good project was about how to implement requirements, how to implement features with less code. Now let me to develop a project, I will be more to consider how to complete the same demand, with another way of thinking the same functional requirements may have many answers, try more solution also can let yourself in the other used in some scene, one-sentence summary word is different solutions have the same effect, It also makes you think more broadly.

Not only do I have a better understanding of the front end technically, but I also have a better understanding of the development specifications myself. Such as code readability, maintainability, variable naming semantics and so on. Taking code readability as an example, this issue is very important for multi-person collaborative development projects. How to make others understand your project based on the code semantics as quickly as possible is also an issue THAT I consider more in application practice. When solving a technical problem, it is better to make the code size much larger, but also to make the code semantics and readability better, and not to pursue the logical code simplification (which can be done when studying THE API practice demo) to harm the overall project maintainability and code readability problems.

Project preview

Online address: http://123.57.204.48:5000/admin

Making: github.com/konglingwen…

Other related items

Customers on the sidelines address: http://123.57.204.48:5000

Client storage address: github.com/konglingwen…

Server storage address: github.com/konglingwen…

Thank you

If you have good suggestions on this project, welcome to the warehouse to raise issues, welcome friends to exchange messages, thank you!

Thanks to all of you who read, liked and followed!