Vue常用代码

来自ling
跳转至: 导航搜索

常用链接

https://cn.vuejs.org/v2/guide/ http://element-cn.eleme.io/#/zh-CN/component/installation https://git.avue.top/ https://avuex.avue.top/#/doc/installation Vue基础精讲 —— Vue的组件之组件的定义、继承、自定义双向绑定、高级属性

Vue类重构代码——02 开始:一个简单的类


avuex

ts

@Prop({ default: false }) public isBlockUI!: boolean;
@Prop({default:"0"}) public tempMode?:string;
@Inject({from:'navModel',default: 'tab'})
public customViewParams:any ={};

bladex

dicUrl: "/api/blade-system/dict/dictionary?code=data_scope_type",
dicUrl: "/api/blade-system/dict/dictionary-tree?code=data_scope_type",
dicUrl: "/api/blade-system/dict-biz/dictionary?code=data_scope_type",
dicUrl: "/api/blade-system/dict-biz/dictionary-tree?code=data_scope_type",
{
              label: "接口类型",
              type: "select",
              dicUrl: "/api/blade-system/dict/dictionary?code=api_scope_type",
              props: {
                label: "dictValue",
                value: "dictKey"
              },
              dataType: "number",
              slot: true,
              width: 100,
              prop: "scopeType",
              rules: [{
                required: true,
                message: "请输入通知类型",
                trigger: "blur"
              }]
            },

input-with-select

     <el-input placeholder="请输入内容" v-model="composition" class="input-with-select">
       <el-button slot="append" icon="el-icon-search" @click="handlePrescriptionAnalysisNode">分析</el-button>
     </el-input>

组件定义和使用

定义组件

<template>
  <div class="more" @click="open">
    <span class="up" v-if="isopen"></span>
    <span class="down" v-if="!isopen"></span>
  </div>
</template>
<script>
export default {
  props:{
    isopen:Boolean
  },
  methods:{
    open () {
      this.$emit("checkState",this.isopen);
    }
  }
}
</script>

双向赋值只能用computed

<template>
  <div>
    selectValues:{{selectValues}}
    <el-select
      multiple
      placeholder="请输入内容"
      :value="selectValues"
      filterable
      remote
      reserve-keyword
      :remote-method="remoteMethod"
      @change="onChange"
      :aria-readonly="readonly"
      :loading="loading">
      <el-option
        v-for="user in users"
        :key="user.id"
        :label="user.eName"
        :value="user.email">
      </el-option>
    </el-select>
  </div>
</template>


<script>

  import {userSearchNone} from "../api/basic/user";

  export default {
    name: "UserMailSelect",
    model: {
      prop: 'inputValues', // prop,子组件接收父组件通过v-model传递过来的值,可重命名为value22
      event: 'change'// event,该组件在派发emit 'change' 事件的时候,传递参数的值就是父组件v-model能够收到的值。
    },
    props: {
      inputValues: {
        type: Array,
      },
      readonly: {
        type: Boolean,
        default: function () {
          return true
        }
      }
    },
    data() {
      return {
        loading: false,
        users: [],
      }
    },
    methods: {
      onChange(val) {
        if (val) {
          this.$emit('change', val)
        }
      },
      remoteMethod(query) {
        if (query !== '') {
          this.loading = true;
          let data = {
            pageNum: 1,
            pageSize: 20,
            reqeustData: {
              roleCode: '',
              condition: query,
            }
          };
          userSearchNone(data).then((res) => {
            this.loading = false;
            this.users = [];
            let emailMap = {};
            if (res.content) {
              res.content.map((item, index) => {
                if (item.email != "") {
                  if (emailMap[item.email]) {
                    item.email = item.email + index
                    this.users.push(item);
                  } else {
                    emailMap[item.email] = item.email
                    this.users.push(item);
                  }
                }
              });
            }
          });
        }
      },
    },
    mounted() {
      // this.selectValues = this.inputValues;
    },
    computed: {
      selectValues: function () {
        return this.inputValues
      }
    }
    // watch:{
    //   selectValues:{
    //     immediate:true,
    //     handler:function(newVal, oldVal){
    //       console.log(newVal)
    //       // this.selectValues = this.inputValues;
    //     }
    //   }
    // }
  }
</script>

<style lang="scss" scoped>
  .el-select {
    width: 100%;
  }

  .el-select /deep/ .el-select__tags > span {
    display: contents !important;
  }

  .el-select-collapse /deep/ .el-select__tags input {
    display: none !important;
  }
</style>

组件调用

https://www.cnblogs.com/zqzjs/p/6789232.html

<script>
  import Lines from "../../../components/Lines";
  import ShowSearch from "../../../components/ShowSearch";
  import CommonTitle from "../../../components/CommonTitle";
  import {selectOnJob} from "../../../constant";
  import {getUserLevel,getServiceLine,getUserArea, getLocation, getCompany} from "../../../api/basic/menuDown";
  import {userSearch,userExport,userDelete} from "../../../api/basic/user";

  export default {
    components: {
      Lines,
      CommonTitle,
      ShowSearch,
    },
    methods: {
......
      openSelectList (state) {
        this.isopen = !state;
      },
......

......
<ShowSearch :isopen="isopen" @checkState="openSelectList" />
<SubmitAuditDialog ref="submitAuditDialog" @searchProjectChangeInfo="searchProjectChangeInfo"/>
this.$refs.submitAuditDialog.setParentData(data);
......
    methods: {
      // 设置父组件选中的值
      setParentData(data) {
        this.parentData = data;
        this.requestData = {
          roleName: data.roleName,
          condition: 'AND'
        };
        firstGetNextRoleUsers(this.parentData).then((res) => {
          this.nextRoleUsers = res;
          if (res.users.length === 1) {
            this.$confirm(this.$t('m.common.confirm_operate'), this.$t('m.common.info'), {
              confirmButtonText: 'OK',
              cancelButtonText: 'Cancel',
              type: 'warning'
            }).then(() => {
              this.submitToAudit([res.users[0].staffCode]);
            }).catch(() => {});
          } else {
            this.parentData.users = res.users;
            this.roleName = res.roleName;
            this.roleList.push(res.roleName);
            this.open();
          }
        });
      },
......

路由

vue项目 hash模式下使用this.$router.query取值 undefined的问题记录 https://blog.csdn.net/Wybbcc/article/details/118514692


https://blog.csdn.net/gqzydh/article/details/81453990

this.$router.push({name:'master',params:{id:'参数'}});

//name和params搭配,刷新的话,参数会消失

this.$router.push({path:'/master',query:{id:'参数'}});
this.$route.query.name和this.$route.params.name。

//path和query搭配,刷新页面的话,url中的参数不会丢失,query中的参数成了url中的一部分

除了使用 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。

想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。

当你点击 <router-link> 时,这个方法会在内部调用,所以说,点击 <router-link :to="..."> 等同于调用 router.push(...)。

声明式:

编程式:router.push(…)

该方法的参数可以是一个字符串路径,或者一个描述地址的对象。

// 字符串 router.push('home')

// 对象 this.$router.push({path: '/login?url=' + this.$route.path});

// 命名的路由 router.push({ name: 'user', params: { userId: 123 }})

// 带查询参数,变成/backend/order?selected=2 this.$router.push({path: '/backend/order', query: {selected: "2"}});

// 设置查询参数

this.$http.post('v1/user/select-stage', {stage: stage})
     .then(({data: {code, content}}) => {
           if (code === 0) {
               // 对象
               this.$router.push({path: '/home'});
           }else if(code === 10){
               // 带查询参数,变成/login?stage=stage
               this.$router.push({path: '/login', query:{stage: stage}});
          }
});

// 设计查询参数对象

let queryData = {};
if (this.$route.query.stage) {
    queryData.stage = this.$route.query.stage;
}
if (this.$route.query.url) {
    queryData.url = this.$route.query.url;
}
this.$router.push({path: '/my/profile', query: queryData});

replace

类型: boolean 默认值: false 设置 replace 属性的话,当点击时,会调用 router.replace() 而不是 router.push(),于是导航后不会留下 history 记录。即使点击返回按钮也不会回到这个页面。 //加上replace: true后,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。

this.$router.push({path: '/home', replace: true})

//如果是声明式就是像下面这样写: <router-link :to="..." replace></router-link> // 编程式:

router.replace(...)

案例

this.$router.push({path: '/coach/' + this.$route.params.id, query: queryData});

this.$router.push、replace、go的区别

1, this.$router.push()

跳转到不同的url,但这个方法回向history栈添加一个记录,点击后退会返回到上一个页面。 - 用法:

// 字符串

router.push('home')

// 对象

this.$router.push({path: 'home'})

//命名的路由

this.$router.push({name: 'user', params: {userId: 123}})

// 带查询参数,变成/register?plan=private

this.$router.push({path: 'register', query: {plan: "private"}});

2, this.$router.replace() 描述:同样是跳转到指定的url,但是这个方法不会向history里面添加新的记录,点击返回,会跳转到上上一个页面。上一个记录是不存在的。

3, this.$router.go(n) 相对于当前页面向前或向后跳转多少个页面,类似 window.history.go(n)。n可为正数可为负数。正数返回上一个页面

     openBook(data) {
       let {href} = this.$router.resolve({path: '/filemanager/book?fileId=' + data.folderId + '&businessType=' + this.businessType})
       window.open(href, '_blank')
     }


         <router-link
         icon="el-icon-download"
         :to="'/filemanager/book?fileId='+scope.row.folderId+'&businessType='+businessType"
         target="_blank">
         查看
         </router-link>

v-for

 <li v-for="(item, index) in items":key="item.id">
    {{ index }} - {{ item.text }}
 </li>

          <el-table-column v-for="(item, index) in features" label="item.caseNo" align="center" :key="item.caseNo">
            <el-table-column :prop="item.caseNo" :label="item.submitDate" width="270" align="center"></el-table-column>
          </el-table-column>

watch

    props: {
      entity: {
        type: Object,
      }
    },

    watch: {
      entity: {
        immediate: true,
        handler(newVal, oldVal) {
          this.currentEntity = newVal;
          this.loadShareHolders();
        }
      }
    }

props

props: {
    // Basic type check (`null` and `undefined` values will pass any type validation)
    propA: Number,
    // Multiple possible types
    propB: [String, Number],
    // Required string
    propC: {
      type: String,
      required: true
    },
    // Number with a default value
    propD: {
      type: Number,
      default: 100
    },
    // Object with a default value
    propE: {
      type: Object,
      // Object or array defaults must be returned from
      // a factory function
      default: function () {
        return { message: 'hello' }
      }
    },
    // Custom validator function
    propF: {
      validator: function (value) {
        // The value must match one of these strings
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }


    watch: {
      fileId: {
        deep: true,  // 深度监听
        handler(newVal,oldVal) {
          this.field=newVal;
          this.initContent();
        }
      }
    },

定义静态变量

export const MENU_ICON = [
  {
    key: 'charts_center',
    label: 'ICON 1',
    src: '/static/images/menu/charts_center.png'
  },
  {
    key: 'file_center',
    label: 'ICON 2',
    src: '/static/images/menu/file_center.png'
  },
  {
    key: 'gdc_setting',
    label: 'ICON 3',
    src: '/static/images/menu/gdc_setting.png'
  },
  {
    key: 'project_deal_center',
    label: 'ICON 4',
    src: '/static/images/menu/project_deal_center.png'
  },
  {
    key: 'project_setting_center',
    label: 'ICON 5',
    src: '/static/images/menu/project_setting_center.png'
  },
  {
    key: 'user_center',
    label: 'ICON 6',
    src: '/static/images/menu/user_center.png'
  }
];

定义类

Vue类重构代码——02 开始:一个简单的类

自定义组件的prop

The prop is used to pass in an initial value; the child component wants to use it as a local data property afterwards. In this case, it’s best to define a local data property that uses the prop as its initial value:
props: ['initialCounter'],
data: function () {
  return {
    counter: this.initialCounter
  }
}
The prop is passed in as a raw value that needs to be transformed. In this case, it’s best to define a computed property using the prop’s value:
props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

computed

https://segmentfault.com/a/1190000014478664?utm_source=tag-newest

确认按钮

              <el-button type="text" size="medium" @click="deleteDialog=true">Delete</el-button>
              <el-dialog
                :visible.sync="deleteDialog"
                width="10%"
                :append-to-body="true"
                size="small"
                customClass="customWidth">
                <span slot="title" class="dialog-title" style="color: #606266;font-size: 20px">提示</span>
                <span style="font-size: 15px">
                  <i class="el-icon-warning" style="font-size:20px;color: #E6A23C"></i>
                  确认操作
                </span>
                <span slot="footer" class="dialog-footer">
                  <el-button @click="deleteDialog = false" size="small">Cancel</el-button>
                  <el-button type="primary" size="small" @click="handelDelete(scope.row)">OK</el-button>
                </span>
              </el-dialog>

消息提示

this.$notify({
          title: '成功',
          message: '<strong>这是 <i>HTML</i> 片段</strong>'
          type: 'success'
        });
warning,


单独引用

单独引入 Notification:

import { Notification } from 'element-ui';
此时调用方法为 Notification(options)。我们也为每个 type 定义了各自的方法,如 Notification.success(options)。并且可以调用 Notification.closeAll() 手动关闭所有实例。

字典翻译

   components: {
     DictTranslate,
     DictSelect
   },
           <template slot-scope="scope">
             <DictTranslate :input-value="scope.row.status" dict-code="COR_CASE_STATUS_DICT"></DictTranslate>
           </template>

字典下拉

   components: {
     DictTranslate,
     DictSelect
   },
<DictSelect v-model="searchObj.actionType" dict-code="COR_CASE_TYPE_DICT_QUERY" placeholder="Please choose a type" size="small"></DictSelect>

Vue 将数据库中带html标签的内容输出

<template>
  <div v-html=translateName v-if="isHtml"></div>
  <span v-else="!isHtml">{{translateName}}</span>
</template>

高度

 const that = this;
 window.onresize = () => {
        return (() => {
          // that.height = document.body.clientHeight - document.getElementById('file-manage-header').offsetHeight - 32;
          that.height = document.body.clientHeight - 32;
        })();
      };
:style="{height:height+'px'}"

Object.assign

 getList(page, params) {
      this.tableLoading = true
      fetchList(Object.assign({
        current: page.currentPage,
        size: page.pageSize
      }, params, this.searchForm)).then(response => {
        this.tableData = response.data.data.result
        this.page.total = response.data.data.total
        this.tableLoading = false
      })

文件下载

export function handleDown(filename, bucket) {
  return request({
    url: '/admin/sys-file/' + bucket + '-' + filename,
    method: 'get',
    responseType: 'blob'
  }).then((response) => { // 处理返回的文件流
    const blob = response.data
    const link = document.createElement('a')
    link.href = URL.createObjectURL(blob)
    link.download = filename
    document.body.appendChild(link)
    link.click()
    window.setTimeout(function() {
      URL.revokeObjectURL(blob)
      document.body.removeChild(link)
    }, 0)
  })
}


	public void getFile(String fileName, HttpServletResponse response) {
		int separator = fileName.lastIndexOf(StrUtil.DASHED);
		try (InputStream inputStream = minioTemplate.getObject(fileName.substring(0, separator),
				fileName.substring(separator + 1))) {
			response.setContentType("application/octet-stream; charset=UTF-8");
			IoUtil.copy(inputStream, response.getOutputStream());
		} catch (Exception e) {
			log.error("文件读取异常", e);
		}
	}

问题解决

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value

这种情况下就会报这个错,因为传入的prop中的值是不允许改变的。这个在vue更新后才会出现的,网上是这么说的:

在vue2中,直接修改prop是被视作反模式的。由于在新的渲染机制中,每当父组件重新渲染时,子组件都会被覆盖,所以应该把props看做是不可变对象 ^1。

不能更改 quantity prop使其和父组件同步 , 而是让应该这个组件提交个事件给父组件,可以 watch quantity 变量,如果变量发生改变就emit事件,所以这里压根不需要 prop。

生命周期

Vue life.png