vue 前端页面开发经验记录

本博文记录了在vue项目开发中的一些经验,具体包含:class动态绑定、子页面刷新、注入函数到子页面、数据加载效果、单击后编辑、文件上传、数据分页、表单提交等的使用记录。

1、class动态绑定

根据变量的值绑定不同的class样式,这里ftype的值可为full_label、zero_label、has_label三种class样式名

        <div class="folderImg" :class="ftype" @click="to_url('showimglist',fd.name)">
          <i class="el-icon-folder-opened avatar-uploader-icon"></i>
        </div>

对应的class设置有

.full_label {
    
    
  background-image: url(../../assets/full_label.png);
  background-repeat:no-repeat;
  background-size:100% 100%;
  -moz-background-size:100% 100%;
}

.zero_label {
    
    
  background-image: url(../../assets/zero_label.png);
  background-repeat:no-repeat;
  background-size:100% 100%;
  -moz-background-size:100% 100%;
}

.has_label {
    
    
  background-image: url(../../assets/has_label.png);
  background-repeat:no-repeat;
  background-size:100% 100%;
  -moz-background-size:100% 100%;
}

2、子页面刷新 | 注入函数到子页面

通过v-if的方式设置子页面不可见,然后在this.$nextTick设置子页面可见,从而实现对子页面的刷新。然后将封装为reload函数,并注入函数到子页面,在子页面调用即可。
具体参考:https://www.jb51.net/article/260561.htm

<template>
  <div id="app">
    <!--img src="./assets/logo.png"-->
    <router-view v-if="isRouterAlive"></router-view>
  </div>
</template>
<script>
import axios from 'axios'
export default {
      
      
  name: 'App',
  provide () {
      
       //设置被注入到子页面的函数
    return {
      
      
      reload: this.reload,
    }
  },
  data () {
      
      
    return {
      
      
      isRouterAlive: true,
    }
  },
  methods: {
      
      
    reload () {
      
      
      this.isRouterAlive = false
      this.$nextTick(function () {
      
      
        this.isRouterAlive = true
      })
    },
  }
}
</script>

子页面的js中设置inject即可在子页面刷新内容

//子页面
  <script>   
  export default {
      
      
    name: 'showanalysis',
    inject: ['reload'], //接收父页面注入的内容
  }
  </script>

3、数据加载效果

用于服务器返回结果中的过程界面(当前端调用后台接口时触发该效果,获取到结果后取消显示)

具体参考 https://element.faas.ele.me/#/zh-CN/component/loading#loading-jia-zai
在这里插入图片描述
具体用例如下,在元素中添加v-loading属性绑定变量loading,默认为false不显示效果;当触发其他耗时操作时,修改loading为true,显示加载中效果;当操作完成时,修改loading为false。

<template>
  <div class="demo-image"
  v-loading="loading"
  element-loading-text="等待服务器处理中"
  element-loading-spinner="el-icon-loading"
  element-loading-background="rgba(0, 0, 0, 0.8)"
 >
  </div>
</template>
  
<script>

export default {
      
      
  name: 'showdatafolder',
  data() {
      
      
    return {
      
      
      loading: false,
    }
  },
  }
}
</script>

4、宽高百分比

使用vh,vw来控制元素相对于屏幕的宽高百分比。vh和wh是视口单位,是相对长度单位。wh是相对于视口的宽度,“1vw”等于视口宽度的“1%”;而vh是相对于视口的高度,“1vh”等于视口高度的“1%”

具体参考:https://www.php.cn/faq/489349.html

5、单击后编辑

text类控件在单击后转换为input控件,在失去焦点后又变换text类空间。
以下为el-button控件与el-input控件的结合,当点击el-button后,与之对应的el-input展示,当el-input失去焦点后又显示为el-button。

<el-input
  class="input-new-tag"
  v-if="inputVisible"
  v-model="inputValue"
  ref="saveTagInput"  #为控件取了一个名字
  size="small"
  @keyup.enter.native="handleInputConfirm"
  @blur="handleInputConfirm"
>
</el-input>
<el-button v-else class="button-new-tag" size="small" @click="showInput">+ New Tag</el-button>


<script>
  export default {
      
      
    data() {
      
      
      return {
      
      
        dynamicTags: ['标签一', '标签二', '标签三'],
        inputVisible: false,
        inputValue: ''
      };
    },
    methods: {
      
      
      showInput() {
      
      
        this.inputVisible = true;
        this.$nextTick(_ => {
      
      
          this.$refs.saveTagInput.$refs.input.focus();//设置指定控件获取输入焦点
        });
      },
      handleInputConfirm() {
      
      
        let inputValue = this.inputValue;
        if (inputValue) {
      
      
          this.dynamicTags.push(inputValue);
        }
        this.inputVisible = false;
        this.inputValue = '';
      }
    }
  }
</script>

关键解析:
1、通过v-if='inputVisible' , v-else,绑定变量inputVisible设置el-button与与el-input的显示切换
2、为el-input控件设置ref,既为控件取名字。当点击button时,触发showInput事件,设置el-input可见,并获取输入焦点
3、通过设置el-input的@blur与@keyup.enter.native属性,对输入完成后的操作进行监听,并设置inputVisible = false 输入框不可见,inputValue = '' 输入框的值为空`

代码参考自:https://element.faas.ele.me/#/zh-CN/component/tag

单击前效果

在这里插入图片描述
控件输入时效果
在这里插入图片描述

6、文件上传

这里仅实现最简单的文件上传方式,多文件上传(实质为多次调用文件上传接口)请参考 https://element.faas.ele.me/#/zh-CN/component/upload

html页面

设置action为null,主要是在:before-upload所绑定的函数中实现文件上传

      <el-upload class="upload-demo" drag action="null" :before-upload="beforeAvatarUpload">
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">将pt文件拖到此处,或<em>点击上传</em></div>
        <div class="el-upload__tip" slot="tip">只能上传pt文件,且不超过500kb</div>
      </el-upload>

js实现

在beforeAvatarUpload函数中,可以对文件后缀、文件size进行判断,最后使用axios创建post请求将文件提交到后端

    beforeAvatarUpload(file) {
    
    
      import axios from 'axios'
      const isAllow = file.name.indexOf('.pt') > 0 || file.name.indexOf('.PT') > 0;
      const isLt200M = file.size / 1024 / 1024 / 200;
      if (!isAllow) {
    
    
        this.$message.error("只允许上传pt文件!");
        return false
      } else if (!isLt200M) {
    
    
        this.$message.error('上传模型大小不能超过 200MB!');
        return false
      } else {
    
    
        //手动进行文件上传
        let formData = new FormData();
        formData.append("file", file);
        axios({
    
    
          url: "/api/uploader",//上传文件接口
          method: "post",
          headers: {
    
    
            "Content-Type": "multipart/form-data",
          },
          data: formData,
        }).then((res) => {
    
    
          this.$message({
    
    
            title: '结果提示',
            message: res.data.msg,
            type: 'success'
          });
          this.reload();
        });
      }
      return false
    },

后端代码

后端基于flask实现

@app.route('/uploader',methods=['GET','POST'])
def uploader():
    if request.method == 'POST':
        f = request.files['file']#跟表单里file中的name要一样
        spath=os.path.join(runDir, f.filename)
        print(spath)
        f.save(spath)
        json={
    
    'code':0,'msg':'文件上传成功!'}
        return jsonify(json) 
    else:
        return 'please uplaod file use post method!'

更多内容参考 https://element.faas.ele.me/#/zh-CN/component/upload

7、数据分页

在element-ui中详细介绍了分页控件的用法,但并未配合任何具体案例介绍。https://element.faas.ele.me/#/zh-CN/component/pagination

html页面

第一个部件为grid-content,里面v-for循环对应变量img_list,用于显示分页的具体内容
第二部分为el-pagination控件,对应具体的分页控件。可以通过修改layout对应的text,实现对分页部件内容的位置控制

<template>
  <div class="demo-image"
  >
    <el-row>
        <div class="grid-content">
          <div class="block" v-for="d in img_list" :key="d">
            <div class="demo-image__preview">
              <el-image style="width: 200px; height: 200px" :src="d" :preview-src-list="img_list">
              </el-image>
              <div class="bottom clearfix">
                <el-button type="text" class="button" @click="to_url('labelimg',{'currentImage':d})">标注</el-button>
                <el-button type="text" class="button" @click="delete_img(d)">删除</el-button>
              </div>
            </div>
          </div>
        </div>
    </el-row>
    <el-row>
        <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="0"
          :page-sizes="[6, 8, 10, 12, 15, 18]" :page-size="default_page_size"
          layout="total, sizes, prev, pager, next, jumper" :total="all_img_list.length">
        </el-pagination>
    </el-row>

  </div>
</template>

js实现

主要涉及的变量有all_img_list | 所有的数据、img_list | 当前分页的数据、default_page_size | 每一页的数据量、default_page | 当前页。
在页面初始化的时候,既mounted函数中获取全部数据设置到all_img_list中,并根据default_page_size、default_page截取当前页内容设置到img_list中。

<script>
import axios from 'axios'
export default {
      
      
  name: 'showimglist',
  data() {
      
      
    return {
      
      
      all_img_list: [],
      img_list: [],
      default_page_size: 10,
      default_page: 1,
    }
  },
  mounted() {
      
      
    if(this.$route.query.dname){
      
      
      localStorage.setItem('dname', this.$route.query.dname);
    }
    let dname = localStorage.getItem('dname');//this.$route.params.dname;
    this.dname=dname;
    if (dname) {
      
      
      let formData = new FormData();
      formData.append("dname", dname);
      axios({
      
      
        url: '/api/datamanage_get_imglist',//上传文件接口
        method: "post",
        headers: {
      
      
          "Content-Type": "multipart/form-data",
        },
        data: formData,
      }).then((res) => {
      
      
        this.all_img_list = res.data;
        this.img_list = this.all_img_list.slice(0, this.default_page_size)
      });
    }
  },
  methods: {
      
      
    handleSizeChange(val) {
      
      
      this.default_page_size = val;
      this.img_list = this.all_img_list.slice((this.default_page - 1) * this.default_page_size, this.default_page * this.default_page_size);
    },
    handleCurrentChange(val) {
      
      
      this.default_page = val
      this.img_list = this.all_img_list.slice((this.default_page - 1) * this.default_page_size, this.default_page * this.default_page_size);
    },
    
  }
}
</script>

以上代码中,handleSizeChange用于监听分页数量的改变,handleCurrentChange用于检测页码数的改变

后端实现

这里的分页本质上与后端没有任何关系,每次分页时并未向后端请求对应页码的数据,仅在已有数据中进行截取。
当总数据较多时,可以在handleSizeChange与handleCurrentChange中实时从后端获取数据。

最终实现的页面效果如下
在这里插入图片描述

8、表单提交

表单实现

以下表单所绑定的为json对象:model="ruleForm",故此v-model="ruleForm.name"其el-form-item所绑定的为ruleForm.name,同时还:rules="rules"绑定了验证规则rules,ref="ruleForm" 表单被命名为ruleForm。
由submitForm事件提交表单

  <el-form :model="ruleForm" :rules="rules" ref="ruleForm"  
  style="padding: 30px;width:400px;background-color: bisque;" label-width="100px" class="demo-ruleForm">
    <el-form-item label="用户名" prop="name">
      <el-input v-model="ruleForm.name"></el-input>
    </el-form-item>
    <el-form-item label="密码" prop="pwd">
      <el-input type="password" v-model="ruleForm.pwd"></el-input>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="submitForm('ruleForm')">立即登录</el-button>
      <el-button @click="resetForm('ruleForm')">重置</el-button>
    </el-form-item>
  </el-form>

表单验证及提交

ruleForm对象为表单所绑定的数据模型
rules为表单具体字段的验证规则,针对每一个字段都可以设置多条规则
submitForm函数实现表单的验证及提交

 export default {
    
    
    name: 'login',
    data() {
    
    
      return {
    
    
        ruleForm: {
    
    
          name: '',
          pwd: '',
        },
        rules: {
    
    
          name: [
            {
    
     required: true, message: '请输入用户名', trigger: 'blur' },
            {
    
     min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
          ],
          pwd: [
            {
    
     required: true, message: '请输入密码', trigger: 'blur' },
            {
    
     min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
          ],
        }
      };
    },
      methods: {
    
    
        submitForm(formName) {
    
    
          this.$refs[formName].validate((valid) => {
    
    
            if (valid) {
    
    //表单验证成功,进入后台主页
              this.$router.push({
    
    name:'mainframe'});
            } else {
    
    
              console.log('error submit!!');
              return false;
            }
          });
        },
        resetForm(formName) {
    
    
          this.$refs[formName].resetFields();
        }
      }
    }

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/a486259/article/details/132132449