JS的动态方法调用是通过eval函数实现

但是在Vue中是通过组件的$options.methods实现,

写这篇的随笔的原因是因为今天为了封装面包屑组件,花了一下午折腾这个动态方法调用

调用DEMO,通过字符串调用对应方法

1、组件标签:

<template>
<div>
<h3>调用Demo</h3>
<button @click="execDynamicMethod">动态方法调用</button>
</div>
</template>

2、方法声明:

methods: {
execDynamicMethod() {
/* 1、获取当前实例的方法集合 */
const methodList = this.$options.methods /* 2、类似Java的反射机制,动态调用需要指定调用的对象是谁 */
const that = this /* 3、根据方法名进行调用,入参调用的对象,和方法参数 */
const methodName = 'targetMethod'
methodList[methodName](that, 100)
},
/**
* 目标方法,
* 注意,使用动态方法调用,首个参数为调用的组件实例,
* 此组件实例可能和this实例不再一致,注意引用的变化
* @param val
* @param val2
*/
targetMethod(val, val2) {
console.log(`val === this ? ${val === this}`, val2)
}
}

3、打印结果:

  

组件封装案例

封装成组件使用,需要配合EventBus的方式实现组件通讯,先定义一个EventBus.js

import Vue from 'vue'

/* 向外部共享一个独立的Vue实例 */
export default new Vue()

  

然后是面包屑组件 Breadcrumb.vue

<template>
<div class="location">
<span class="loc-text">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item v-if="itemList[0]">{{ itemList[0] }}</el-breadcrumb-item>
<el-breadcrumb-item v-if="itemList[1]">{{ itemList[1] }}</el-breadcrumb-item>
<el-breadcrumb-item v-if="itemList[2]">{{ itemList[2] }}</el-breadcrumb-item>
</el-breadcrumb>
</span>
<span style="margin: 0 20px 0 0"><el-button v-show="hasAddAction" v-permission="btnPermit" type="primary" size="mini" :icon="iconClass" @click="addActionEvent()">{{ btnTxt }}</el-button></span>
</div>
</template> <script>
import EventBus from './EventBus'
export default {
name: 'Breadcrumb',
props: {
itemList: {
type: Array,
required: true,
default() {
return []
}
}
},
data() {
return {
btnPermit: '',
btnTxt: '新增',
methodName: 'openEditDialog',
iconClass: 'el-icon-plus',
hasAddAction: false
}
},
created() {
this.receiveBrotherCompData()
},
methods: {
/**
* 接收兄弟组件发送的数据
*/
receiveBrotherCompData() {
EventBus.$on('listParam', val => {
this.btnPermit = val.btnPermit
this.hasAddAction = val.hasAddAction
if (val.methodName) this.methodName = val.methodName
if (val.iconClass) this.iconClass = val.iconClass
if (val.btnTxt) this.btnTxt = val.btnTxt
})
},
/**
* 调用父组件事件,再通过父组件的$refs调用兄弟组件的方法
*/
addActionEvent() {
this.$emit('openAddDrawer', this.methodName)
}
}
}
</script>

 

界面效果是这样:

面包屑的文本条通过父组件穿进来即可,重点是按钮通讯,每个按钮的图标class,文本,调用的功能都不一样

除了方法之外都可以通过对象设置属性来传递控制,但是方法调用比较麻烦

方法调用的实现思路:

$refs引用调用 + 自定义$emit

面包屑组件和父组件通过$emit通许,然后父组件和其他子组件通讯用$refs这样,

这样串起来,就相当于面包屑控制兄弟组件的方法了

父组件Left.vue $emit绑定的事件名是 openAddDrawer,通过itemList传面包屑的文本内容

<template>
<div style="height: 100%">
<el-container style="height: 100%">
<el-aside class="aside" :style="{ width: isCollapse ? '63px' : '220px' }">
<div class="navbox">
<el-col class="sideleft-nav" :style="{ width: isCollapse ? '63px' : '220px' }">
<el-menu index="0" class="el-menu-vertical-demo" :style="{ width: isCollapse ? '63px' : '220px' }" :collapse="isCollapse" :collapse-transition="isTransition" :default-active="funcNav[0].id + ' '">
<template v-for="firstItem in funcNav">
<el-menu-item v-if="firstItem.fuHref !== null && firstItem.fuHref !== undefined" :key="firstItem.id" :index="firstItem.id + ' '" @click="clickNav(firstItem.fuHref)">
<svg-icon :icon-class="firstItem.fuIcon" class="nav-icon" />
<span v-show="!isCollapse">{{ firstItem.fuName }}</span>
</el-menu-item>
<el-submenu v-else :key="firstItem.id" :index="firstItem.id + ' '">
<template slot="title">
<svg-icon :icon-class="firstItem.fuIcon" class="nav-icon" />
<span slot="title">{{ firstItem.fuName }}</span>
</template>
<template v-for="secondItem in firstItem.children">
<el-menu-item v-if="secondItem.fuHref !== null && secondItem.fuHref !== undefined" :key="secondItem.id" :index="secondItem.id + ' '" @click="clickNav(secondItem.fuHref, firstItem.fuName, secondItem.fuName)">
<template slot="title">
<svg-icon :icon-class="secondItem.fuIcon" class="nav-icon" />
<span slot="title">{{ secondItem.fuName }}</span>
</template>
</el-menu-item>
<el-submenu v-else :key="secondItem.id" :index="secondItem.id + ' '">
<template slot="title">
<svg-icon :icon-class="secondItem.fuIcon" class="nav-icon" />
<span slot="title">{{ secondItem.fuName }}</span>
</template>
<el-menu-item v-for="thirdItem in secondItem.children" :key="thirdItem.id" :style="[isCollapse ? stylePadding20 : stylePadding40]" :index="thirdItem.id + ' '" @click="clickNav(thirdItem.fuHref, firstItem.fuName, secondItem.fuName, thirdItem.fuName)">{{ thirdItem.fuName }}</el-menu-item>
</el-submenu>
</template>
</el-submenu>
</template>
</el-menu>
</el-col>
</div>
<div class="spread" :style="{ width: isCollapse ? '63px' : '220px' }">
<svg-icon v-if="!isCollapse" icon-class="shrink-icon" class="shrink-icon" @click="shrinkNav" />
<svg-icon v-if="isCollapse" icon-class="unfold-icon" class="shrink-icon" @click="shrinkNav" />
</div> </el-aside>
<el-main class="box-main">
<breadcrumb :item-list="itemList" @openAddDrawer="openAddDrawer" />
<component :is="currentComponent" ref="dynamicRefKey" />
</el-main>
</el-container>
</div>
</template> <script>
import { getFuncTree } from '@/api/system/privileges/func'
import { uuid } from '@/utils/UUID' import Home from './home' import SystemParam from '@/views/amerp/system/common/param/param-list'
import SystemLog from '@/views/amerp/system/common/log/log-list'
import AreaList from '@/views/amerp/system/common/area/area-list'
import Dict from '@/views/amerp/system/common/dict/dict-list' import User from '@/views/amerp/system/privileges/user/user-list'
import Role from '@/views/amerp/system/privileges/role/role-list' import ProcessModel from '@/views/amerp/system/process/model/model-list'
import ProcessInstance from '@/views/amerp/system/process/instance/instance-list'
import ProcessDesign from '@/views/amerp/system/process/design/design-panel'
import ProcessDefine from '@/views/amerp/system/process/define/define-list' import PushTask from '@/views/amerp/system/push/task/task-list' import Company from '@/views/amerp/system/archives/company/company-list'
import BankAccount from '@/views/amerp/system/archives/bankacco/bankacco-list'
import Department from '@/views/amerp/system/archives/department/department-list'
import Employee from '@/views/amerp/system/archives/employee/employee-list'
import Ware from '@/views/amerp/system/archives/ware/ware-list'
import Wacost from '@/views/amerp/system/archives/ware/wacost-list'
import Waprice from '@/views/amerp/system/archives/ware/waprice-list'
import Subject from '@/views/amerp/system/archives/subject/subject-list'
import Expeitem from '@/views/amerp/system/archives/expeitem/expeitem-list'
import ExSuList from '@/views/amerp/system/archives/expeitem/exsu-list'
import Customer from '@/views/amerp/system/archives/customer/customer-list'
import Cubank from '@/views/amerp/system/archives/customer/cubank-list' import DiApp from '@/views/amerp/system/dingtalk/app/app-list'
import DiDept from '@/views/amerp/system/dingtalk/department/dept-list'
import DiUser from '@/views/amerp/system/dingtalk/user/user-list' import PrInfo from '@/views/amerp/sales/project/info/info-list'
import PrIncontent from '@/views/amerp/sales/project/incontent/incontent-list'
import PrInrival from '@/views/amerp/sales/project/inrival/inrival-list'
import PrIntender from '@/views/amerp/sales/project/intender/intender-list'
import PrIndealfactor from '@/views/amerp/sales/project/indealfactor/indealfactor-list'
import PrInsolution from '@/views/amerp/sales/project/insolution/insolution-list'
import PrInvisit from '@/views/amerp/sales/project/invisit/invisit-list'
import PrInContact from '@/views/amerp/sales/project/incontact/incontact-list'
import PrInCoFamily from '@/views/amerp/sales/project/incofamily/incofamily-list' import FinExApply from '@/views/amerp/financial/expense/exapply/exapply-list'
import FinExApDetail from '@/views/amerp/financial/expense/exapdetail/exapdetail-list'
import FinExApTravel from '@/views/amerp/financial/expense/exaptravel/exaptravel-list'
import FinExApAllot from '@/views/amerp/financial/expense/exapallot/exapallot-list' import FinSpApply from '@/views/amerp/financial/spend/spapply/spapply-list'
import FinSpApDetail from '@/views/amerp/financial/spend/spapdetail/spapdetail-list' import OpeSeApply from '@/views/amerp/operating/seal/seapply/seapply-list'
import Breadcrumb from '@/components/Breadcrumb/Index' export default {
name: 'Left',
components: { Breadcrumb, Home, SystemParam, User, Role, Company, Dict, SystemLog, AreaList, BankAccount, Department, Ware, Wacost, Waprice, Subject, Expeitem, ExSuList, Employee, Customer, Cubank, PrInfo, PrIncontent, PrInrival, PrIntender, PrIndealfactor, PrInsolution, PrInvisit, FinExApply, FinExApDetail, FinExApTravel, FinExApAllot, FinSpApply, FinSpApDetail, PrInContact, PrInCoFamily, OpeSeApply, DiUser, DiDept, DiApp, ProcessModel, ProcessInstance, ProcessDesign, PushTask, ProcessDefine },
data() {
return {
currentComponent: '',
flag: true,
funcNav: [],
uuid: uuid,
isCollapse: false,
isTransition: false,
defaultActive: '',
stylePadding20: {
'margin-left': '0px!important',
'padding-left': '20px!important'
},
stylePadding40: {
'margin-left': '14px!important',
'padding-left': '28px!important'
},
itemList: null
}
},
created() {
this.initFuncNav('0')
},
methods: {
openAddDrawer(val) {
const methodList = this.$refs['dynamicRefKey'].$options.methods
const that = this.$refs['dynamicRefKey']
methodList[val](that, '')
},
initFuncNav(parentId) {
getFuncTree({
module: escape('系统管理-菜单管理-查询菜单'),
uuid: this.uuid,
sys_code: 'amerp',
parentIds: parentId
}).then(res => {
if (res.code === 200) {
if (res.data) {
this.funcNav = res.data
// 选中第一个
this.defaultActive = res.data[0].id + ' '
this.currentComponent = res.data[0].fuHref
}
}
})
},
clickNav(href, nav1, nav2, nav3) {
this.currentComponent = href
this.itemList = [nav1, nav2, nav3]
},
shrinkNav() {
this.isCollapse = !this.isCollapse
}
}
}
</script>

子组件代码:

声明了 initialBreadcrumb 方法,初始化的时候赋值面包屑组件需要的内容

<template>
<div>
<div class="row-search">
<div class="row-1">
<el-form ref="form" :model="form" label-width="82px" :inline="true">
<el-form-item label="代码编号" size="small">
<el-input v-model.trim="form.diCode" style="width: 170px" clearable placeholder="代码编号 \ 代码名称" />
</el-form-item> <el-form-item label="代码类别" size="small">
<el-input v-model.trim="form.diCateIdent" style="width: 190px" clearable placeholder="代码类别 \ 代码类别名称" />
</el-form-item> <el-form-item label="封存状态" size="small">
<el-select v-model="form.sealupState" clearable style="width: 100px" placeholder="请选择">
<el-option v-for="item in sealupStateList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item> <el-form-item size="small">
<el-button icon="el-icon-search" @click="searchPage">查询</el-button>
<el-button icon="el-icon-delete" @click="resetInput">重置</el-button>
</el-form-item>
</el-form>
</div> </div>
<div class="list-table">
<el-table v-loading="loading" size="small" stripe :data="tableData">
<el-table-column align="center" type="index" width="50px" label="序号" />
<el-table-column prop="diCode" min-width="120px" align="center" label="代码编号" />
<el-table-column prop="diName" min-width="120px" align="left" label="代码名称" />
<el-table-column prop="diCateIdent" min-width="120px" align="center" label="代码类别" />
<el-table-column prop="diCateName" min-width="120px" align="center" label="代码类别名称" />
<el-table-column prop="diParentCode" min-width="120px" align="center" label="上级代码编号" />
<el-table-column prop="sort" min-width="80px" align="center" label="顺序" />
<el-table-column prop="sealupState" fixed="right" min-width="80px" align="center" label="封存状态">
<template slot-scope="scope">
<el-switch v-model="scope.row.sealupState" :disabled="!permissions.indexOf('amerp:dict:seal') > 0" active-value="1" inactive-value="0" @change="sealup(scope.row)" />
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="120px" align="center">
<template slot-scope="scope">
<span v-permission="'amerp:dict:update'" class="link-color" @click="openEditDialog('', scope.row)">
<i class="el-icon-edit-outline" /> 修改
</span>  
<!--<span v-permission="'amerp:dict:delete'" class="link-color" @click="deleteThisRow(scope.row)">-->
<!--<i class="el-icon-circle-close" /> 删除-->
<!--</span>-->
</template>
</el-table-column>
</el-table>
<el-pagination style="float: none; text-align: right; margin-top: 10px;" :current-page="page.current" :page-size="page.size" layout="total, prev, pager, next, jumper" :total="page.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" />
</div> <!-- 编辑弹窗 -->
<!--<el-dialog v-if="editDialogVisible" :title="editDialogTitle" :append-to-body="true" :close-on-click-modal="false" :visible.sync="editDialogVisible" width="760px" @close="closeEditDialog">-->
<!--<dict-edit-dialog :ref="editDialogRef" :form="currentRow" />-->
<!--</el-dialog>--> <el-drawer :title="editDialogTitle" :append-to-body="true" size="35%" :modal="false" :visible.sync="editDialogVisible" :destroy-on-close="true" @close="closeEditDialog">
<dict-edit-dialog :ref="editDialogRef" :form="currentRow" />
</el-drawer>
</div>
</template> <script>
import '@/assets/css/style.css'
import DictEditDialog from './dict-add'
import { getDictPage, deleteDict, sealupDict, unSealupDict } from '@/api/system/common/dict'
import { mapGetters } from 'vuex'
export default {
name: 'DictList',
components: {
DictEditDialog
},
data() {
return {
/* 编辑弹窗 */
editDialogVisible: false,
editDialogTitle: '',
editDialogRef: 'editDialogRefKey', /* 详情弹窗 */
detailDialogVisible: false,
detailDialogRef: 'detailDialogRefKey', currentRow: undefined, /* 列表变量 */
loading: false,
tableData: [],
form: {
diCode: '',
diCateIdent: '',
sealupState: ''
},
sealupStateList: [
{ label: '全部', value: '' },
{ label: '封存', value: '1' },
{ label: '启用', value: '0' }
],
createTimeArr: [],
page: {
current: 0,
size: 10,
total: 0
}
}
},
computed: {
...mapGetters([
'permissions'
])
},
created() {
this.initialBreadcrumb()
this.searchPage()
},
methods: {
initialBreadcrumb() {
const param = {
btnPermit: 'amerp:dict:add',
btnTxt: '新增',
iconClass: 'el-icon-plus',
methodName: 'openEditDialog',
hasAddAction: true
}
this.$eventBus.$emit('listParam', param)
},
handleSizeChange(pageSize) {
this.page.size = pageSize
this.query()
},
handleCurrentChange(pageIndex) {
this.page.current = pageIndex
this.query()
},
searchPage() {
this.page.current = 0
this.page.total = 0
this.query()
},
async query() {
this.loading = true
this.form.page = this.page
const { data: res, total } = await getDictPage(this.form)
this.tableData = res
this.page.total = total
this.loading = false
},
resetInput() {
this.form = {
diCode: '',
diCateIdent: '',
sealupState: ''
}
},
openEditDialog(curr, row) {
const isUpdate = !!row
if (isUpdate) curr = this
curr.editDialogTitle = isUpdate ? '更新公共字典' : '新增公共字典'
curr.currentRow = isUpdate ? JSON.parse(JSON.stringify(row)) : undefined
curr.editDialogVisible = true
},
closeEditDialog() {
this.editDialogVisible = false
},
deleteThisRow(row) {
this.$confirm(`确认要删除公共字典[${row.diCode}], 是否继续?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async() => {
await deleteDict(row)
this.$message.success('删除成功!')
this.searchPage()
}).catch(() => {
this.query()
})
},
createTimeChange(val) {
const hasSelect = !!val
this.form.startCreateTime = hasSelect ? val[0] : ''
this.form.endCreateTime = hasSelect ? val[1] : ''
},
openDetailDialog(row) {
this.currentRow = JSON.parse(JSON.stringify(row))
this.detailDialogVisible = true
},
closeDetailDialog() {
this.detailDialogVisible = false
},
sealup(row) {
const isSealupState = row.sealupState === '1'
this.$confirm(`确认要${isSealupState ? '封存' : '启用'}公共字典[${row.diCode}], 是否继续?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async() => {
isSealupState ? await sealupDict(row) : await unSealupDict(row)
this.$message.success(isSealupState ? '封存成功!' : '启用成功!')
this.searchPage()
}).catch(() => {
this.query()
})
}
}
}
</script>

  

每个功能页都需要用这个面包屑,所以EventBus注册成了全局变量,方便调用

/* main.js 全局注册 */
import EventBus from '@/components/Breadcrumb/EventBus'
Vue.prototype.$eventBus = EventBus

  

父组件动态调用时,通过$refs引用过去的:

    openAddDrawer(val) {
const methodList = this.$refs['dynamicRefKey'].$options.methods
const that = this.$refs['dynamicRefKey']
methodList[val](that, '')
},

  

被调用的方法需要多写this参数位:

这里要区分是从本this对象调用的还是父组件$refs调用

    openEditDialog(curr, row) {
const isUpdate = !!row
if (isUpdate) curr = this
curr.editDialogTitle = isUpdate ? '更新公共字典' : '新增公共字典'
curr.currentRow = isUpdate ? JSON.parse(JSON.stringify(row)) : undefined
curr.editDialogVisible = true
},

注意本实例调用和动态调用的参数位一致:

<span v-permission="'amerp:dict:update'" class="link-color" @click="openEditDialog('', scope.row)">

  

【Vue】动态方法调用的更多相关文章

  1. Struts2学习笔记 - Action篇<动态方法调用>

    有三种方法可以使一个Action处理多个请求 动态方法调用DMI 定义逻辑Acton 在配置文件中使用通配符 这里就说一下Dynamic Method nvocation ,动态方法调用,什么是动态方 ...

  2. 第三章Struts2 Action中动态方法调用、通配符的使用

    01.Struts 2基本结构 使用Struts2框架实现用登录的功能,使用struts2标签和ognl表达式简化了试图的开发,并且利用struts2提供的特性对输入的数据进行验证,以及访问Servl ...

  3. Struts2 动态方法调用

    01.Struts 2基本结构 使用Struts2框架实现用登录的功能,使用struts2标签和ognl表达式简化了试图的开发,并且利用struts2提供的特性对输入的数据进行验证,以及访问Servl ...

  4. struts之动态方法调用使用通配符

    一.DMI动态方法调用的其中一种改变form表单中action属性的方式已经讲过了.还有两种,一种是改变struts.xml配置文件中action标签中的method属性,来指定执行不同的方法处理不同 ...

  5. struts之动态方法调用改变表单action属性

      一.动态方法调用(DMI:Dynamic Method Invocation) ⒈struts2中同样提供了这个包含多个逻辑业处理的Action,这样就可以在一个Action中进行多个业务逻辑处理 ...

  6. struts2DMI(动态方法调用)

    struts2动态方法调用共有三种方式: 1.通过action元素的method属性指定访问该action时运行的方法 <package name="action" exte ...

  7. Struts2 Action中动态方法调用、通配符的使用

    一.Struts2执行过程图: 二.struts2配置文件的加载顺序 struts-default.xml---struts-plugin.xml---struts.xml 具体步骤: 三.Actio ...

  8. Struts(八):动态方法调用

    动态方法调用:通过url动态调用action中的方法. 默认情况下,Struts的动态方法调用处于禁用状态. 测试定义一个action类: package com.dx.actions; public ...

  9. Struts 2之动态方法调用,不会的赶紧来

    学习Struts2框架以来为了减少Action 的数量,我们可以使用动态方法进行处理. 动态方法调用(Dynamic Method Invocation,DMI)是指表单元素的Action并不是直接等 ...

  10. struts2视频学习笔记 11-12(动态方法调用,接收请求参数)

    课时11 动态方法调用 如果Action中存在多个方法时,可以使用!+方法名调用指定方法.(不推荐使用) public String execute(){ setMsg("execute&q ...

随机推荐

  1. umask永久修改用户创建文件权限

    Linux里永久设置用户创建文件权限的配置文件是/etc/profile.可以在该文件中添加umask命令来设置默认权限.具体操作步骤如下: 打开/etc/profile文件:sudo vi /etc ...

  2. Mesh快连

    Mesh快连 一.名词解释 Mesh快连是一种由多个节点组成的网络系统,这些节点可以相互连接,形成一个"网状"的结构. 二.如何使用 有线Mesh: 网络拓扑: 设备版本:3.7. ...

  3. TypeScript keyof

    keyof 是 TypeScript 中的一个关键字,用于获取一个类型的所有键(属性名)构成的联合类型.它主要用于在类型系统中引用对象类型的键. 以下是一些 keyof 的用法和示例: 1. 获取对象 ...

  4. kettle从入门到精通 第十八课 kettle Metadata Injection

    1.Metadata Injection 类似于java里面的模版,设置通用的模版,通过输入不同的数据,得到不同的结果.本示例演示两个字段拼接成一个新字段. 2.设置模版,设置模版时,只需要根据自己的 ...

  5. 接口自动化之request模块

    1.安装 方式一.命令行直接 pip install requests 方式二.PyCharm中,File >> Settings >> Project:Practice &g ...

  6. 从pfx私钥证书中提取私钥

    要准备openssl win 32 del /Q c:\mch_private2.pem c:\OpenSSL-Win32\bin\openssl.exe pkcs12 -in c:\mch_priv ...

  7. TypeAdapter处理Gson解析,null值替换为"",null值替换为[]

    前言 在与后端对接过程中,常常会出现因为后端不规范,导致某些String字段有时传null,有时传"".但我们在对接时并不知道哪些可能为空,他到底会传啥,总不能将Bean类中的所有 ...

  8. java读取txt文件行的两种方式对比

    import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import ja ...

  9. 支付宝 返回的form如何在前端打开

    支付宝支付时返回了一段标签标签大概是 <form></form><script></script> 试了innerHtml怎么试都不能用,是那种直接把字 ...

  10. VUE中watch的详细使用教程

      1.watch是什么? watch:是vue中常用的侦听器(监听器),用来监听数据的变化 2.watch的使用方式如下 watch: { 这里写你在data中定义的变量名或别处方法名: { han ...