Vue中使用slot或extends实现相似组件的继承复用
Vue中相似组件复用的几种种方法问题几种复用页面的方法问题项目中有七八个页面是非常相似的,都是从左边选择人员,通过按钮移动到右边。下面截取了部分页面,这几个页面只有画红圈的地方可能有区别:几种复用页面的方法最简单的方式是把这七八个页面的内容封装到一个组件中去,通过组件的属性控制不同部分的显示,但是这样会导致页面内容太多,不同页面的逻辑混到一起,不好维护。另一种方式是把公共部分封装到一个组件中去,最
问题
项目中有七八个页面是非常相似的,都是从左边选择人员,通过按钮移动到右边。下面截取了部分页面,这几个页面只有画红圈的地方可能有区别:
几种复用页面的方法
- 最简单的方式是把这七八个页面的内容封装到一个组件中去,通过组件的属性控制不同部分的显示,但是这样会导致页面内容太多,不同页面的逻辑混到一起,不好维护。
- 另一种方式是把公共部分封装到一个组件中去,最下方使用
<slot name="footer">
开放一个接口,使用的时候通过footer定义页面最下方圆圈中的部分,类似这样:
<template>
<chooseuser-template ref="chooseRef">
<template #footer>
<div id="#noticelay" style="width:100%;padding-left:50px">
<el-form>
<el-form-item style="margin-bottom: 2px;" label="加签办理方式">
<el-radio-group ref="dealwayTypeRef" v-model="dealAddType">
<el-radio label="backward">加签人办理完成后,我再办理</el-radio>
<el-radio label="forward">加签人办理完成后,直接通过</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item style="margin-bottom: 2px;" label="通知方式">
<el-checkbox-group ref="noticetypeRef" v-model="noticeType">
<el-checkbox v-for="(notice,index) in notices" :key="index" :label="notice.ccode">{{ notice.cname }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-form>
</div>
</template>
</chooseuser-template>
</template>
<script>
//省略...
export default {
components: { chooseuserTemplate },
data() {
return {
// 隐藏上移下移按钮
showUpArrow: false,
showDownArrow: false,
// 通知类型
noticeType: ['pm'],
// 处理方式
dealAddType: 'backward',
notices: []
}
},
mounted() {
ChooseUserRequest.getNoticeType().then(res => {
this.notices = res.data
})
},
methods: {
init(params) {
this.$refs['chooseRef'].selectedItemsData = params.addPersonList
this.noticeType = params.noticetype.split(',')
this.dealAddType = params.dealway
}
}
}
</script>
<style lang="scss" scoped>
.formStyle {height:100%;overflow:auto}
</style>
<chooseuser-template>
为公共组件,通过名为footer的slot可以自定义页面底部的部分
- 还有一种方式是使用extends关键字,vue开发中相似的页面可以通过extends关键字复用公共组件,但是如果在子页面中包含
<template></template>
标签,则会使用子页面的<template>
覆盖公共组件的<template>
,如果仅想改变一部分html该怎么办呢?vue本身并没有提供这种能力,不过就这几个页面来说可以结合vue的$mount()函数来实现,对于不同的footer部分,可以通过new Vue()结合jsx动态创建footer部分,然后mount到模板底部,具体是这样的,首先定义公共组件<chooseuser-template>
作为模板:
<template>
<el-form ref="form" :model="form" label-width="120px" class="formStyle">
<el-row style="margin-top:10px">
<el-col :span="12">
<el-form-item label="所属组织" prop="cOrgnName">
<el-input v-model="form.cOrgnName" suffix-icon="el-icon-more" readonly style="width:200px" @click.native="openOrgnChooser" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="搜索职员" prop="condtion">
<el-input v-model="form.condtion" style="width:200px">
<span slot="suffix" @click="search">
<i class="el-input__icon el-icon-search"></i>
</span>
</el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<!-- 左侧 -->
<el-col :span="11">
<el-form-item prop="queryType">
<el-radio-group v-model="form.queryType" style="margin-left:-70px" @change="handleQueryTypeChanged">
<el-radio v-for="item in queryTypeData" :key="item.id" :label="item.id">{{ item.text }}</el-radio>
<!-- <el-radio label="dept">部门</el-radio>
<el-radio label="group">分组</el-radio>
<el-radio label="relation">关系</el-radio> -->
</el-radio-group>
</el-form-item>
<el-form-item v-show="showLeftTree1" style="margin-left:-70px">
<el-scrollbar ref="leftTree1ScrollbarRef" style="height:130px" class="scroll-bar-custom" wrap-style="overflow-x:auto;">
<el-tree
ref="leftTree1Ref"
v-loading="leftTree1Loading"
:data="leftTree1Data"
node-key="cguid"
:default-expand-all="true"
:props="leftTree1Props"
:expand-on-click-node="false"
@node-click="handleLeftTree1Click"
>
<span slot-scope="{node,data}" class="span-ellipsis">
<span :title="node.label" :style="{color:data.enabled === false?'rgb(220 222 226)':'black'}">{{ node.label }}</span>
</span>
</el-tree>
</el-scrollbar>
</el-form-item>
<el-form-item style="margin-left:-70px">
<el-scrollbar ref="leftTree2ScrollbarRef" style="height:130px;" class="scroll-bar-custom" wrap-style="overflow-x:auto;">
<el-tree
ref="leftTree2Ref"
v-loading="leftTree2Loading"
:data="leftTree2Data"
node-key="cguid"
:default-expand-all="true"
:props="leftTree2Props"
:expand-on-click-node="false"
>
<span slot-scope="{node}" class="span-ellipsis">
<span :title="node.label">{{ node.label }}</span>
</span>
</el-tree>
</el-scrollbar>
</el-form-item>
<!-- 组织参照 -->
<orgn-chooser
:visible.sync="showOrgnDialog"
:orgn-options="{root:'1', orgntype:'ADMIN'}"
@close="handleOrgnChooserClose"
/>
</el-col>
<!-- 中间按钮 -->
<el-col :span="2" class="center-col">
<el-button v-if="showRightArrow" icon="el-icon-arrow-right" type="primary" circle @click="addUser"></el-button>
<el-button v-if="showLeftArrow" icon="el-icon-arrow-left" type="primary" circle @click="delUser"></el-button>
<el-button v-if="showUpArrow" icon="el-icon-arrow-up" type="primary" circle @click="moveUpDown('up')"></el-button>
<el-button v-if="showDownArrow" icon="el-icon-arrow-down" type="primary" circle @click="moveUpDown('down')"></el-button>
</el-col>
<!-- 右侧 -->
<el-col :span="11">
<el-form-item id="selectedItems" label="已选">
<el-scrollbar style="height:275px;" class="scroll-bar-custom" wrap-style="overflow-x:auto;">
<el-tree
ref="selectedItemsRef"
v-loading="selectedItemsLoading"
:data="selectedItemsData"
node-key="code"
:default-expand-all="true"
:props="selectedItemsProps"
draggable
:allow-drop="allowDrop"
:expand-on-click-node="false"
:highlight-current="true"
>
<span slot-scope="{node}" class="span-ellipsis">
<span :title="node.label">{{ node.label }}</span>
</span>
</el-tree>
</el-scrollbar>
</el-form-item>
</el-col>
</el-row>
<!-- 通知方式 ,子类实现, 用于第三种实现方式-->
<el-row>
<div id="footer" />
</el-row>
<!-- 通知方式 ,子类实现, 用于第二种实现方式-->
<slot name="footer"></slot>
</el-form>
</template>
<script>
//省略...
</script>
代码最下方定义了footer,用作接口,当实现具体的页面时,可以这样:
<script>
//省略...
export default {
name: 'FreeChooseCheckUser',
extends: chooseuserTemplate,
data() {
return {
dealType: 'serial',
noticeType: ['pm']
}
},
async mounted() {
const instance = this
const response = await ChooseUserRequest.getNoticeType()
const footer = new Vue({
parent: instance, // 必须手动指定父组件,否则watch监听器中获取不到$parent
computed: {
computedDealType: {
get() {
return this.$parent.dealType
},
set(v) {
this.$parent.dealType = v
}
},
computedNoticeType: {
get() {
return this.$parent.noticeType
},
set(v) {
this.$parent.noticeType = v
}
}
},
render() {
return (
<div id='#noticelay' style='width:100%;padding-left:50px'>
<el-form>
<el-form-item label='执行方式' style='margin-bottom:5px'>
<el-radio-group ref='dealtypeRef' v-model={this.computedDealType}>
<el-radio label='serial'>串行</el-radio>
<el-radio label='parallel'>并行</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label='通知方式' style='margin-bottom:5px'>
<el-checkbox-group ref='noticetypeRef' v-model={this.computedNoticeType}>
{
response.data.map((value) => {
return <el-checkbox label={value.ccode}>{value.cname}</el-checkbox>
})
}
</el-checkbox-group>
</el-form-item>
</el-form>
</div>
)
}
}).$mount('#footer')
this.$refs['footer'] = footer
}
}
</script>
<style lang="scss" scoped src="@/views/aos/workflow/styles/chooseuser.scss"></style>
<style lang="scss" scoped>
.formStyle {height:100%;overflow:auto}
</style>
这里定义了一个FreeChooseCheckUser组件,它通过extends继承<chooseuser-template>
后不需要定义<template>
标签,<template>
标签会完全继承<chooseuser-template>
,而footer部分是通过new Vue()结合jsx动态生成的,生成后通过$mount方法挂载到公共组件上(其实也可以把footer写成vue文件,然后import进来,再mount到模板底部,不过footer部分都很简短,没必要单独写成一个vue文件)。这种方式看起来比第二种方式麻烦一点,但是逻辑上讲似乎更自然一些,extends继承公共组件后,缺少什么就补充什么,而第二种方式需要在<template>
中写<chooseuser-template>
,尽管整个页面只有<chooseuser-template>
一个组件,凭空多了一层控件引用关系。
上面只说了footer部分的处理,至于上面的筛选条件(部门、分组、关系)和中间的按钮(上移下移左移右移)处理很简单,他们都是在<chooseuser-template>
中通过data里的变量控制的,当使用第三种extends方式定义页面时,只需要覆盖相应的变量即可,类似这样:
<script>
export default {
name:'myComponent',
data() {
return {
//....
// 查询条件,部门,分组,关系
queryTypeData: [{
id: 'dept', text: '部门'
}, {
id: 'group', text: '分组'
}
//隐藏关系
//, {
// id: 'relation', text: '关系'
//}
],
// 控制中间的四个按钮显示
showUpArrow: true,
showRightArrow: true,
showLeftArrow: true,
showDownArrow: true,
}
}
}
//....
</script>
更多推荐
所有评论(0)